From: Alexander Wilms Date: Thu, 26 Jun 2025 22:20:35 +0000 (+0200) Subject: refactor(mermaid-viewer): Use AbortController for event listener cleanup X-Git-Url: http://source.bookstackapp.com/hacks/commitdiff_plain/1294dc9f9b295285dd3999e22d2d3591f4dbf802 refactor(mermaid-viewer): Use AbortController for event listener cleanup --- diff --git a/content/mermaid-viewer/head.html b/content/mermaid-viewer/head.html index fe9d0cd..b737ba0 100644 --- a/content/mermaid-viewer/head.html +++ b/content/mermaid-viewer/head.html @@ -74,6 +74,9 @@ this.zoomOutBtn = null; this.zoomResetBtn = null; + // Use an AbortController for robust event listener cleanup. + this.abortController = new AbortController(); + // Bind event handlers for proper addition and removal this.boundMouseMoveHandler = this.handleMouseMove.bind(this); this.boundMouseUpHandler = this.handleMouseUp.bind(this); @@ -191,22 +194,24 @@ } setupEventListeners() { - this.toggleInteractionBtn.addEventListener('click', this.boundToggleInteraction); - this.copyCodeBtn.addEventListener('click', this.boundCopyCode); - this.zoomInBtn.addEventListener('click', this.boundZoomIn); - this.zoomOutBtn.addEventListener('click', this.boundZoomOut); - this.zoomResetBtn.addEventListener('click', this.boundResetZoom); + const { signal } = this.abortController; + + this.toggleInteractionBtn.addEventListener('click', this.boundToggleInteraction, { signal }); + this.copyCodeBtn.addEventListener('click', this.boundCopyCode, { signal }); + this.zoomInBtn.addEventListener('click', this.boundZoomIn, { signal }); + this.zoomOutBtn.addEventListener('click', this.boundZoomOut, { signal }); + this.zoomResetBtn.addEventListener('click', this.boundResetZoom, { signal }); - this.viewport.addEventListener('wheel', this.boundHandleWheel, { passive: false }); - this.viewport.addEventListener('mousedown', this.boundHandleMouseDown); + this.viewport.addEventListener('wheel', this.boundHandleWheel, { passive: false, signal }); + this.viewport.addEventListener('mousedown', this.boundHandleMouseDown, { signal }); // Listen on document for mousemove to handle dragging outside viewport - document.addEventListener('mousemove', this.boundMouseMoveHandler); + document.addEventListener('mousemove', this.boundMouseMoveHandler, { signal }); // Listen on window for mouseup to ensure drag ends even if mouse is released outside - window.addEventListener('mouseup', this.boundMouseUpHandler, true); // Use capture phase + window.addEventListener('mouseup', this.boundMouseUpHandler, { signal, capture: true }); - this.viewport.addEventListener('contextmenu', this.boundPreventDefault); - this.viewport.addEventListener('selectstart', this.boundPreventSelect); + this.viewport.addEventListener('contextmenu', this.boundPreventDefault, { signal }); + this.viewport.addEventListener('selectstart', this.boundPreventSelect, { signal }); } toggleInteraction() { @@ -394,21 +399,8 @@ } destroy() { - // Remove event listeners specific to this instance - this.toggleInteractionBtn.removeEventListener('click', this.boundToggleInteraction); - this.copyCodeBtn.removeEventListener('click', this.boundCopyCode); - this.zoomInBtn.removeEventListener('click', this.boundZoomIn); - this.zoomOutBtn.removeEventListener('click', this.boundZoomOut); - this.zoomResetBtn.removeEventListener('click', this.boundResetZoom); - - this.viewport.removeEventListener('wheel', this.boundHandleWheel, { passive: false }); - this.viewport.removeEventListener('mousedown', this.boundHandleMouseDown); - this.viewport.removeEventListener('contextmenu', this.boundPreventDefault); - this.viewport.removeEventListener('selectstart', this.boundPreventSelect); - - document.removeEventListener('mousemove', this.boundMouseMoveHandler); - window.removeEventListener('mouseup', this.boundMouseUpHandler, true); - + // Abort all listeners attached with this controller's signal. + this.abortController.abort(); this.container.innerHTML = ''; // Clear the container's content } }