Skip to content

Commit

Permalink
impl atk.elementRemoveObserver
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Jan 28, 2025
1 parent 6b0e5c2 commit 191d585
Show file tree
Hide file tree
Showing 6 changed files with 841 additions and 11 deletions.
105 changes: 105 additions & 0 deletions js/src/Helper/elementRemoveObserver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const observerByElement = new Map();
const observedChildrenByElement = new Map();
const removeHandlersByElement = new Map();

function handleElementRemove(elem) {
const observedChildren = observedChildrenByElement.get(elem) ?? [];
const removeHandlers = removeHandlersByElement.get(elem) ?? [];

removeObserverIfUnused(elem, true);

for (const child of observedChildren) {
handleElementRemove(child);
}

for (const handler of removeHandlers) {
handler();
}
}

function addObserverToParentElement(elem) {
const parentElem = elem.parentElement;
if (parentElem === null) {
return;
}

if (!observerByElement.has(parentElem)) {
addObserverToParentElement(parentElem);

const observer = new MutationObserver((mutationRecords) => {
for (const mutationRecord of mutationRecords) {
if (mutationRecord.removedNodes.length > 0) {
const removedNodes = observedChildrenByElement.get(parentElem).intersection(mutationRecord.removedNodes);
for (const removedNode of removedNodes) {
handleElementRemove(removedNode);
}
}
}
});
observer.observe(parentElem, { childList: true, characterData: false });

observerByElement.set(parentElem, observer);
observedChildrenByElement.set(parentElem, new Set());
}

observedChildrenByElement.get(parentElem).add(elem);
}

function removeObserverIfUnused(elem, force = false) {
let updated = false;

if (removeHandlersByElement.has(elem)) {
if (!force && removeHandlersByElement.get(elem).size !== 0) {
return;
}

removeHandlersByElement.delete(elem);

updated = true;
}

if (observerByElement.has(elem)) {
if (!force && observedChildrenByElement.get(elem).size !== 0) {
return;
}

const observer = observerByElement.get(elem);
observer.disconnect();
observerByElement.delete(elem);

observedChildrenByElement.delete(elem);

updated = true;
}

if (updated) {
const parentElem = elem.parentElement;
if (parentElem !== null) {
removeObserverIfUnused(parentElem);
}
}
}

export default {
/**
* @param {HTMLElement} element
*/
addHandler: function (element, handler) {
addObserverToParentElement(element);

if (!removeHandlersByElement.has(element)) {
removeHandlersByElement.set(element, new Set());
}

removeHandlersByElement.get(element).add(handler);
},

/**
* @param {HTMLElement} element
*/
removeHandler: function (element, handler) {
removeHandlersByElement.get(element).delete(handler);

removeObserverIfUnused(element);
},
};
2 changes: 2 additions & 0 deletions js/src/setupUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import $ from 'external/jquery';
import mitt from 'mitt';
import lodashDebounce from 'lodash/debounce';
import atk from 'atk';
import elementRemoveObserver from './Helper/elementRemoveObserver';
import gridCheckboxHelper from './Helper/gridCheckboxHelper';
import tableDropdownHelper from './Helper/tableDropdownHelper';
import urlHelper from './Helper/urlHelper';
Expand Down Expand Up @@ -83,6 +84,7 @@ atk.utils = {
},
};

atk.elementRemoveObserver = elementRemoveObserver;
atk.gridCheckboxHelper = gridCheckboxHelper;
atk.tableDropdownHelper = tableDropdownHelper;
atk.urlHelper = urlHelper;
Expand Down
Loading

0 comments on commit 191d585

Please sign in to comment.