From bddb3c27ce759d48a952dd192acd5eb44a39bdae Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 10 Jul 2018 12:00:41 +0530 Subject: [PATCH] fix(destroy): Cleanup event listeners on destroy Event listeners attached to elements inside the root datatable wrapper are automatically removed when the root element is removed from DOM. But, there are event listeners which are attached to window and body, those have to be manually removed. This commit introduces an internal event system, through which we destroy global event handlers. --- src/columnmanager.js | 18 +++++++++++++++--- src/datatable.js | 18 +++++++++++++++++- src/defaults.js | 3 ++- src/style.js | 18 ++++++++++++------ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/columnmanager.js b/src/columnmanager.js index 4b2f749..fcfbaac 100644 --- a/src/columnmanager.js +++ b/src/columnmanager.js @@ -78,9 +78,13 @@ export default class ColumnManager { } }); - $.on(document.body, 'click', (e) => { + const deactivateDropdownOnBodyClick = (e) => { if (e.target.matches(toggleClass)) return; deactivateDropdown(); + }; + $.on(document.body, 'click', deactivateDropdownOnBodyClick); + this.instance.on('onDestroy', () => { + $.off(document.body, 'click', deactivateDropdownOnBodyClick); }); const dropdownItems = this.options.headerDropdown; @@ -126,7 +130,7 @@ export default class ColumnManager { startX = e.pageX; }); - $.on(document.body, 'mouseup', (e) => { + const onMouseup = (e) => { document.body.classList.remove('dt-resize'); if (!$resizingCell) return; isDragging = false; @@ -137,9 +141,13 @@ export default class ColumnManager { this.setColumnWidth(colIndex); this.style.setBodyStyle(); $resizingCell = null; + }; + $.on(document.body, 'mouseup', onMouseup); + this.instance.on('onDestroy', () => { + $.off(document.body, 'mouseup', onMouseup); }); - $.on(document.body, 'mousemove', (e) => { + const onMouseMove = (e) => { if (!isDragging) return; const finalWidth = startWidth + (e.pageX - startX); const { @@ -154,6 +162,10 @@ export default class ColumnManager { width: finalWidth }); this.setColumnHeaderWidth(colIndex); + }; + $.on(document.body, 'mousemove', onMouseMove); + this.instance.on('onDestroy', () => { + $.off(document.body, 'mousemove', onMouseMove); }); } diff --git a/src/datatable.js b/src/datatable.js index 8761c8a..8abebee 100644 --- a/src/datatable.js +++ b/src/datatable.js @@ -100,6 +100,7 @@ class DataTable { destroy() { this.wrapper.innerHTML = ''; this.style.destroy(); + this.fireEvent('onDestroy'); } appendRows(rows) { @@ -193,7 +194,22 @@ class DataTable { } fireEvent(eventName, ...args) { - this.events[eventName].apply(this, args); + // fire internalEventHandlers if any + // and then user events + const handlers = [ + ...(this._internalEventHandlers[eventName] || []), + this.events[eventName] + ].filter(Boolean); + + for (let handler of handlers) { + handler.apply(this, args); + } + } + + on(event, handler) { + this._internalEventHandlers = this._internalEventHandlers || {}; + this._internalEventHandlers[event] = this._internalEventHandlers[event] || []; + this._internalEventHandlers[event].push(handler); } log() { diff --git a/src/defaults.js b/src/defaults.js index f4c4e5c..6742c98 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -32,7 +32,8 @@ export default { onRemoveColumn(column) {}, onSwitchColumn(column1, column2) {}, onSortColumn(column) {}, - onCheckRow(row) {} + onCheckRow(row) {}, + onDestroy() {} }, sortIndicator: { asc: '↑', diff --git a/src/style.js b/src/style.js index f285b3b..f06bc24 100644 --- a/src/style.js +++ b/src/style.js @@ -30,18 +30,24 @@ export default class Style { } bindResizeWindow() { + this.onWindowResize = this.onWindowResize.bind(this); + this.onWindowResize = throttle(this.onWindowResize, 300); + if (this.options.layout === 'fluid') { - $.on(window, 'resize', throttle(() => { - this.distributeRemainingWidth(); - this.refreshColumnWidth(); - this.compensateScrollbarWidth(); - this.setBodyStyle(); - }, 300)); + $.on(window, 'resize', this.onWindowResize); } } + onWindowResize() { + this.distributeRemainingWidth(); + this.refreshColumnWidth(); + this.compensateScrollbarWidth(); + this.setBodyStyle(); + } + destroy() { this.styleEl.remove(); + $.off(window, 'resize', this.onWindowResize); } setStyle(selector, styleObject) {