关于e.matches()是否遍历DOM及DOM查询性能优化的问询
Element.matches()是否需要遍历DOM来评估选择器? 问题场景
我提出这个疑问是因为遇到了这样的代码场景:
if ( e.matches('.class_1, .class_1 *') ) { /* 执行操作 */ } else if ( e.matches('.class_2, .class_2 *') ) { /* 执行操作 */ } else if ( e.matches('.class_3, .class_3 *') ) { /* 执行操作 */ } else { /* 执行操作 */ }
如果e.matches()需要从元素e向上遍历DOM祖先树来判断结果,当前面的条件不成立时,每个else if都会重复这个遍历过程,这看起来不够高效。
优化思路的困惑
于是我考虑改用e.classList.contains()只遍历一次祖先树,确定e向上遍历时最先匹配class_1、class_2、class_3中的哪一个,或者遇到需要停止搜索的祖先元素就终止。但不确定这种方式是否真的更高效:
let currentEl = e; while ( currentEl ) { const classList = currentEl.classList; if ( classList.contains('class_1') ) { /* 执行操作 */ break; } else if ( classList.contains('class_2') ) { /* 执行操作 */ break; } else if ( classList.contains('class_3') ) { /* 执行操作 */ break; } currentEl = currentEl.parentNode; }
补充说明:在我的DOM结构中,class_1的层级始终比class_2深,class_2始终比class_3深。我只需要确定e属于哪一层级,不需要找到具体的元素,只需判断向上遍历时最先匹配的类(如果存在的话)。
后续测试结果与新疑惑(2023年12月24日)
数月后我做了个小测试,结果出乎意料:
- 当存在匹配元素时,
e.closest()和e.matches()速度相近且都很快; - 当无匹配元素时,
e.closest()会慢很多,但e.matches()速度保持不变。
测试在Firefox中运行,Brave浏览器中5000次迭代会崩溃,但少次迭代的结果类似。
让我疑惑的点:
- 如果
e.closest()需要向上遍历DOM寻找匹配项,那找到最外层的evt_1和确定不存在evt_2的耗时为什么会有差异?两者理论上都需要遍历5000层级。 - 存在匹配时,找到从内向外第2500层的
evt_3和第5000层的evt_1的查询耗时没有差异。
测试代码及结果
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
内容的提问来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

