使用 Element.querySelector 的意外结果
Unexpected results using Element.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>
假设我有一个对 ID 为 'item' 的 li 元素的引用,我可以这样得到它:
let item = document.querySelector('#item');
现在我需要检索嵌套 ul 列表中的最后一个锚点元素。为此,我将使用 'last-child' 选择器。这是我最初尝试的:
let lastLink = item.querySelector('li:last-child > a');
我认为这可以解决问题,但它实际上 return 是标记中的第一个 link ('Stuff')。我不太明白这一点,因为我的基本元素已经是 'item' 元素,我要求它在该基本元素中找到 li 元素,这是其父项的最后一个子项,然后 return 来自其中的 a 元素。
的确,如果我这样做:
let lastLink = item.querySelector('li:last-child');
它 实际上 return 嵌套 ul 列表中的最后一个 li 元素,那么为什么第一个选项不能简单地获取 'a'元素在那个?
这是我尝试过的其他一些做工作的东西:
let lastLink = item.querySelector('#item li:last-child > a');
但是,因为我已经有了对基本元素的引用,所以在这里指定它的 ID 似乎毫无意义。这些也有效 return 正确的 a 元素:
let lastLink = item.querySelector('li ul li:last-child > a');
let lastLink = item.querySelector('li:last-child li:last-child > a');
我的问题是,为什么第一个选项不起作用?感谢有人能解释一下原因
做rootElm.querySelector(selector)
本质上是:
for (const node of recursiveDescendantNodes) {
if (node.matches(selector)) {
return node;
}
}
selector
没有被分解和分析,以查看选择器的第一部分是否开始低于根节点 - 通常,指定根节点所做的只是指示要搜索哪些后代。
由于指向 #foo
的第一个 <a>
与传递的选择器匹配,因此返回。
console.log(
document.querySelector('#item > a').matches('li:last-child > a')
);
<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>
不过,有一种方法可以避免这种行为,方法是使用 :scope
- 在支持的浏览器中,它表示“元素 querySelector
(或 querySelectorAll
)被调用on”,这是您希望看到的功能。
let item = document.querySelector('#item');
let lastLink = item.querySelector(':scope li:last-child > a');
console.log(lastLink);
<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>
鉴于以下标记(为了简洁起见,我只包含了相关部分)
<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>
假设我有一个对 ID 为 'item' 的 li 元素的引用,我可以这样得到它:
let item = document.querySelector('#item');
现在我需要检索嵌套 ul 列表中的最后一个锚点元素。为此,我将使用 'last-child' 选择器。这是我最初尝试的:
let lastLink = item.querySelector('li:last-child > a');
我认为这可以解决问题,但它实际上 return 是标记中的第一个 link ('Stuff')。我不太明白这一点,因为我的基本元素已经是 'item' 元素,我要求它在该基本元素中找到 li 元素,这是其父项的最后一个子项,然后 return 来自其中的 a 元素。
的确,如果我这样做:
let lastLink = item.querySelector('li:last-child');
它 实际上 return 嵌套 ul 列表中的最后一个 li 元素,那么为什么第一个选项不能简单地获取 'a'元素在那个?
这是我尝试过的其他一些做工作的东西:
let lastLink = item.querySelector('#item li:last-child > a');
但是,因为我已经有了对基本元素的引用,所以在这里指定它的 ID 似乎毫无意义。这些也有效 return 正确的 a 元素:
let lastLink = item.querySelector('li ul li:last-child > a');
let lastLink = item.querySelector('li:last-child li:last-child > a');
我的问题是,为什么第一个选项不起作用?感谢有人能解释一下原因
做rootElm.querySelector(selector)
本质上是:
for (const node of recursiveDescendantNodes) {
if (node.matches(selector)) {
return node;
}
}
selector
没有被分解和分析,以查看选择器的第一部分是否开始低于根节点 - 通常,指定根节点所做的只是指示要搜索哪些后代。
由于指向 #foo
的第一个 <a>
与传递的选择器匹配,因此返回。
console.log(
document.querySelector('#item > a').matches('li:last-child > a')
);
<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>
不过,有一种方法可以避免这种行为,方法是使用 :scope
- 在支持的浏览器中,它表示“元素 querySelector
(或 querySelectorAll
)被调用on”,这是您希望看到的功能。
let item = document.querySelector('#item');
let lastLink = item.querySelector(':scope li:last-child > a');
console.log(lastLink);
<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>