I have some jQuery code that repeatedly attaches delegated handlers (e.g. click()) to an element, so that when the event is triggered, the handler gets called several times.
I do know the strategic solution: in my scenario, the delegate should be the only child of the AJAX-loaded code container, so that any replacement will wipe out, together with the DOM elements, any handlers they might have delegated.
Another possibility would be to rewrite such assignments as
$(...).off(event, selector).on(event, selector, event => { });
to ensure that no double delegation ever happens.
What I'd like is a tactical solution to warn myself and the other project developers when they have inadvertently added (or left in place, since this is a major re-engineering and code cleanup project) any delegated handlers to objects higher in the DOM tree than the chosen container. My idea is:
+++ let before = app.developer.calculateHandlerSyndrome($('#container'));
performObjectLoad('#container', whatever)
.then(...)
+++ .then(_ => {
+++ if (before !== app.developer.calculateHandlerSyndrome($('#container'))) {
+++ sys.error("The object " + whatever + " has over-delegated");
+++ }
+++ });
For my calculateHandlerSyndrome($selector), I'm thinking of returning an array of selectors, starting from the specified one and working upwards recursively through all the parents until I hit document. To compare the arrays I'll just JSON.stringify() them.
What I lack is a method to retrieve the event handlers for a given selector, since the $.data('events') method appears not to be working.
I have found the events I need inside a property of each DOM element called "jQuery1234(ALARGENUMBER)56789", but it's obvious that the number is a unique ID and will change at each page load. I could check all properties of each element and find whether there's one called jQuerysomething, but it's too kludgy a solution.
The jQuery.fn.data('events') interface is deprecated and has been replaced by $._data(element).events.
So the approach would be:
function handlerSyndrome($selector) {
let parent = $selector.parent(), syndrome = [ ];
if (parent.length) {
syndrome = handlerSyndrome(parent);
}
// Get the element's handlers
let ev = $._data($selector[0]).events;
// Transform it to an object with count: { click: 7, blur: 2 }, ...
let hnd = { };
for (const h in ev) {
hnd[h] = ev[h].length;
}
if (Object.getOwnPropertyNames(hnd).length) {
syndrome.push(hnd);
}
return syndrome;
}
This will take a jQuery selector such as $('#container') and return something like,
[
{
"click": 20,
"focusout": 1,
"focusin": 1,
"keydown": 2,
"keyup": 1,
"mouseup": 1,
"mousedown": 1,
"change": 2
},
{
"click": 1
}
]
and will allow checking whether the parent tree above that selector got anything added to its handlers. It will not check for changes or rearrangements.
It might be necessary to delay the call to check after the load using window.setTimeout().