使用 Element.querySelector 出现意外结果

Zip*_*ppy 4 html javascript css-selectors queryselector

给出以下标记(为了简洁起见,我仅包含相关部分)

<ul>
  <li id="item">
    <a href="#foo">Stuff</a>
    <ul>
      <li> <a href="#foo">Foo stuff</a></li>
      <li><a href="#bar">Bar Stuff</a></li>
      <li><a href="#foobar">Foo-Bar Stuff</a></li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

假设我有一个对ID 为“item”的li元素的引用,我可以这样得到:

let item = document.querySelector('#item');
Run Code Online (Sandbox Code Playgroud)

现在我需要检索嵌套ul列表中的最后一个锚元素。为此,我将使用“最后一个子”选择器。这是我最初尝试的:

let lastLink = item.querySelector('li:last-child > a');
Run Code Online (Sandbox Code Playgroud)

我认为这可以解决问题,但可惜它实际上返回了标记中的第一个链接(“Stuff”)。我不太明白这一点,因为我的基本元素已经是“item”元素,我要求它在该基本元素中找到 li元素,这是其父元素的最后一个子元素,然后返回a元素从里面。

事实上,如果我这样做:

let lastLink = item.querySelector('li:last-child');
Run Code Online (Sandbox Code Playgroud)

实际上返回嵌套 ul 列表中的最后一个 li 元素,那么为什么第一个选项不能简单地获取其中的'a'元素呢?

以下是我尝试过的其他一些有效的方法:

let lastLink = item.querySelector('#item li:last-child > a');
Run Code Online (Sandbox Code Playgroud)

但是,由于我已经有了对基本元素的引用,因此在此处指定其 ID 似乎毫无意义。这些也可以工作并返回正确的a元素:

let lastLink = item.querySelector('li ul li:last-child > a');
let lastLink = item.querySelector('li:last-child li:last-child > a');  
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么第一个选项不起作用?如果有人能阐明原因,我将不胜感激

Cer*_*nce 5

本质上做的rootElm.querySelector(selector)是:

for (const node of recursiveDescendantNodes) {
  if (node.matches(selector)) {
    return node;
  }
}
Run Code Online (Sandbox Code Playgroud)

不会对其selector进行分解和分析以查看选择器的第一部分是否从根节点开始 - 通常,指定根节点所做的只是指示要搜索哪些后代。

由于第一个<a>指向#foo匹配传递的选择器,因此它被返回。

for (const node of recursiveDescendantNodes) {
  if (node.matches(selector)) {
    return node;
  }
}
Run Code Online (Sandbox Code Playgroud)
console.log(
  document.querySelector('#item > a').matches('li:last-child > a')
);
Run Code Online (Sandbox Code Playgroud)

不过,有一种方法可以绕过这种行为,即使用:scope- 在受支持的浏览器中,表示“元素querySelector(或querySelectorAll)被调用”,这是您希望看到的功能。

<ul>
  <li id="item">
    <a href="#foo">Stuff</a>
    <ul>
      <li> <a href="#foo">Foo stuff</a></li>
      <li><a href="#bar">Bar Stuff</a></li>
      <li><a href="#foobar">Foo-Bar Stuff</a></li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)
let item = document.querySelector('#item');
let lastLink = item.querySelector(':scope li:last-child > a');
console.log(lastLink);
Run Code Online (Sandbox Code Playgroud)