Issue
Does the e.matches() method have to traverse the DOM to evaluate the selector?
I ask because of a situation such as:
if ( e.matches('.class_1, .class_1 *') {
/* Do something. */
} else if ( e.matches('.class_2, .class_2 *') {
/* Do something. */
} else if ( e.matches('.class_3, .class_3 *') {
/* Do something. */
} else {
/* Do something. */
}
If the DOM is traversed from the element e
up the ancestor tree to determine if e.matches() is true/false; then it would repeat the process for each else if
when the previous condition is false. So, I wondered if this was not inefficient and I should just loop up the ancestor tree once using e.classList.contains()
to find which of the set (class_1, class_2, class_3) is encountered first or reach an ancestor element that indicates that should stop searching. But contains()
doesn't appear to be a quick test either.
let c = e.classList;
while ( e ) {
if ( c.contains('class_1') ) {
/* Do something. */
break;
} else if ( c.contains('class_2') ) {
/* Do something. */
break;
} else if ( c.contains('class_3') ) {
/* Do something. */
break;
}
e = e.parentNode;
}
In this DOM sturcture class_1 is always deeper down than class_2 which is always deeper down than class_3. So, I'd like to determine to which level e
belongs first. Which element is the class_1, 2, 3 does not need to be determined, but only to which of them (if any) e
is closest to on the way up the tree.
Thank you.
ADDED 24 DEC 2023
It's been a few months since thinking about this and tried this small example. The results are a bit unexpected, in that e.closest()
and e.matches()
are just about the same and very fast when there is a matching element; but, when there is not a match, then e.closest()
is much slower and e.matches()
is the same. Ran this in Firefox. Tried in Brave and 5000 iterations causes it to crash in SO; but it ran for fewer iterations and the resuls are similar.
By unexpected, I mean, if e.closest()
has to step upward through the DOM until it fines a match, then why does it not take the same time to find evt_1
(at the very outside) as it does to find that there is not an evt_2
? Both would have to step through all 5000 levels.
It appears that the documentation (https://dom.spec.whatwg.org/#ref-for-dom-element-closest%E2%91%A0), although I don't understand what it means exactly, indicates there is a difference in process between the two methods. When there is a match, there is no time difference between evt_3
at 2500 level and evt_1
at 5000, if count from the inside outward.
var
D = document.createElement("DIV"),
d = D;
D.className = "evt_1";
for ( let i = 0; i < 5000; i++ ) {
d = d.appendChild(document.createElement("DIV"));
if ( i === 2500 ) d.className = "evt_3";
}
document.body.append(D);
let t = Date.now();
console.log( d.matches(".evt_1, .evt_1 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_1, .evt_1 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_1") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.matches(".evt_2, .evt_2 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_2, .evt_2 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_2") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.matches(".evt_3, .evt_3 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_3, .evt_3 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_3") + " " + (Date.now() - t) );
/*
true 0
[object HTMLDivElement] 0
false 0
null 112
true 0
[object HTMLDivElement] 0
*/
Solution
It probably has to traverse the ancestor list so there might be some waste, but traversing it yourself once might not be faster than the browser traversing it 3 times.
If the site has a noticable performance issue (e.g. if this operation is performed a lot of times), you can measure both methods on different browsers and see what's faster. But in most cases there wouls be practically no difference anyway and then it's probably better to go with what makes the code clearer.
Answered By - Noam
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.