I’m looking for the most brilliant idea. The focus is Efficiency – let’s try to avoid a bunch of loops =)
No libraries. This is an exercise in pure JS genius. Feel free to reference functions from inside a library though, if they accomplish this. No need for fallbacks to crappy old browsers (ie: IE).
The Setup
I am trying to create event delegation. I am binding an event listener to a container element (the context) for each event type that happens within this element (click, mouseenter, etc.). The listener references a function, we’ll call the “route”.
There’s an object, created separately that looks something like this (example):
myarr['click']['.myclass'] = functionReference1;
myarr['click']['#myid'] = functionReference2;
myarr['click']['div>a'] = functionReference3;
The route takes event.type (here, ‘click’) and goes to myarr and gets all the click records. route then loops through each selector key and matches it against the event.target. If it matches, the function reference is called, the callback if you will.
This is all well and good. Fairly quick, straightforward.
The Question
The event.target may be a child of an element that matches the selector key. I’m looking for the crazy smooth voodoo black magic to determine if that parent is between our context element and event.target. Preferably, some native browser function.
IDEALLY, as example, I could do something along the lines of
context.querySelectorAll(key + " " + event.target);
but obviously, I can’t pass an object into querySelectorAll, as far as I know.
What’s Been Tried
Two methods have been tried. Both WORK. Neither is pretty.
1)
Loop through the event.target.parentNode until a match is found. This totes does it, but it creates a loop inside my existing loop. Starts getting SLOW when there’s several selector keys and lots of elements on the page.
if (event['target'].matchesSelector(key)) {
//do callback
} else {
var et = event['target'];
while (et['parentNode'] !== null and et['parentNode'] !== this) {
et = et['parentNode'];
if (et.matchesSelector(key)) {
//do callback
break;
}
}
}
2)
Use ranges. I’m rather proud of this one, but it has the same multi-loop problem. Haven’t tested performance too heavily, but there’s MANY things that happen here, and if there’s a LOT of elements matching the selector key, this is probably going to hurt efficiency.
if (event['target'].matchesSelector(key)){
//do callback
} else {
var range2 = document.createRange();
range2.selectNode(event.target);
var range = document.createRange();
var allthese = document.querySelectorAll(key)
for (var i = 0; i < allthese.length; i++){
range.selectNode(allthese[i]);
if (range2.compareBoundaryPoints(range2.START_TO_START, range) && range2.compareBoundaryPoints(range2.END_TO_END, range) <= 0){
//do callback
break;
}
}
}
MAGIC VOODOO – find me some, and I’ll create a big bounty when I have the rep again.
Maybe you should try this:
Here my second condition will check if the target is Descendant of the key selector
As you can see in this JSFIDDLE(fiddle Works in webkit(prefer chrome) browsers only) the child/Descendant is matched using parent selector
PS: This method would not work if you also need that element which matches the actual key, but in your question as you said “crazy smooth voodoo black magic to determine if that parent is between our context element and event.target”, so I suppose you don’t need the actual element