e.matches()方法是否遍历DOM?

问题描述 投票:0回答:3

e.matches()方法是否必须遍历DOM来评估选择器?

我问是因为以下情况:

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. */
}

如果从元素

e
到祖先树向上遍历DOM,判断e.matches()是否为true/false;然后,当先前的条件为假时,它将对每个
else if
重复该过程。所以,我想知道这是否效率不高,我应该使用
e.classList.contains()
循环一次祖先树来查找首先遇到的集合(class_1、class_2、class_3)中的哪一个,或者到达指示应该停止搜索的祖先元素。但
contains()
似乎也不是一个快速测试。

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;
} 

在此 DOM 结构中,class_1 始终比 class_2 更深,而 class_2 始终比 class_3 更深。所以,我想首先确定

e
属于哪个级别。不需要确定哪个元素是 class_1、2、3,而只需确定它们中的哪一个(如果有)
e
在树的上行路径上最接近。

谢谢你。

于 2023 年 12 月 24 日添加

考虑这个问题并尝试这个小例子已经过去了几个月。结果有点出乎意料,因为

e.closest()
e.matches()
几乎相同,并且当有匹配元素时速度非常快;但是,当没有匹配时,
e.closest()
会慢得多,而
e.matches()
则相同。在 Firefox 中运行这个。在 Brave 中尝试过,5000 次迭代导致它在 SO 中崩溃;但它运行的迭代次数较少,并且结果相似。

看来文档(https://dom.spec.whatwg.org/#ref-for-dom-element-closest%E2%91%A0)虽然我不明白它的确切含义,表明两种方法之间的过程存在差异。当有比赛时,从内向外计算,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.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.matches(".evt_3, .evt_3 *") + " " + (Date.now() - t) );
t = Date.now();
console.log( d.closest(".evt_3, .evt_3 *") + " " + (Date.now() - t) );

/*
true 0
[object HTMLDivElement] 0
false 0
null 112
true 0
[object HTMLDivElement] 0
*/

javascript html dom
3个回答
0
投票

它可能必须遍历祖先列表,因此可能会有些浪费,但自己遍历一次可能不会比浏览器遍历3次更快。

如果网站存在明显的性能问题(例如,如果多次执行此操作),您可以在不同的浏览器上测量这两种方法,看看哪种方法更快。但在大多数情况下,实际上没有什么区别,那么最好选择让代码更清晰的东西。


0
投票

如果我正确理解了这个问题,似乎你想要元素方法

closest()
使用类名的选择器列表

document.querySelectorAll(".clickable").forEach((div) => {
  div.addEventListener("click", (ev) => {
    console.log(ev.currentTarget.closest(".class_1, .class_2, .class_3"));
  });
});
<div>
  <div class="class_3">
    <div class="class_2">
      <div class="class_1">
        <div class="clickable">Click 1</div>
      </div>
    </div>
  </div>

  <div class="class_3">
    <div class="class_2">
      <div class="clickable">Click 2</div>
    </div>
  </div>

  <div class="class_3">
    <div class="clickable">Click 3</div>
  </div>
</div>


0
投票

您可以通过分析已分配给单击(或以其他方式选择)元素的类来在任何循环之外完成“繁重”工作:

document.querySelectorAll("div").forEach(d=>d.onclick=e=>{
  const classes=Object.fromEntries([...e.target.classList].map(cl=>([cl,1])));
  // classes is an object that has class-named properties. These can be easily checked for existence:
  if (classes.class_1) console.log("A class 1 element was clicked");
  else if (classes.class_2) console.log("A class 2 element was clciked");
  else if (classes.class_3) console.log("A class 3 element was clciked");
})
.class_1,.class_2,.class_3 {cursor:pointer; background-color:#ddd; margin:6px} 
<p>Please click on any of the divs below:</p>
<div class="class_1">class 1 item</div>
<div class="class_2">class 2 item</div>
<div class="class_3">class 3 item</div>
<div class="class_1 class_2">class 1 and 2 item</div>
<div class="class_2 class_3">class 2 and 3 item</div>
<div class="class_3 class_1">class 3 and 1 item</div>

看到您的

if
语句序列总是过滤掉按字母顺序排列最低的“class_...”条目,您可以进一步简化脚本:

document.querySelectorAll("div[class*=class_]").forEach(d=>d.onclick=e=>{
  const minClass=[...e.target.classList].filter(c=>c.slice(0,6)=="class_").reduce((a,c)=>c<a?c:a)
  if      (minClass=="class_1") console.log("A class 1 element was clicked");
  else if (minClass=="class_2") console.log("A class 2 element was clciked");
  else if (minClass=="class_3") console.log("A class 3 element was clciked");
})
.class_1,.class_2,.class_3 {cursor:pointer; background-color:#ddd; margin:6px}
<p>Please click on any of the divs below:</p>
<div class="class_1">class 1 item</div>
<div class="class_2">class 2 item</div>
<div class="class_3">class 3 item</div>
<div class="extraClass">this chould not be clickable</div>
<div class="class_1 abc class_2">class 1 and 2 item</div>
<div class="class_2 class_3">class 2 and 3 item</div>
<div class="class_3 class_1">class 3 and 1 item</div>

© www.soinside.com 2019 - 2024. All rights reserved.