如何解决多个后代选择器,错误还是误解?
以下两种选择节点的方法不应该产生相同的结果吗?
let tmp = fruits.querySelector("ul:first-of-type li:first-of-type");
tmp = tmp.querySelector("span")
对比
let tmp = fruits.querySelector("ul:first-of-type li:first-of-type span");
(查看实际操作 here)
我已经在 Firefox 和 chrome 中对此进行了测试。两种情况下的结果不同。谁能解释一下为什么?
堆栈片段中的示例:
let fruits = document.querySelector("[data-segment='fruits']");
console.log(fruits);
let tmp = fruits.querySelector("ul:first-of-type li:first-of-type")
tmp = tmp.querySelector("span")
console.log("Works:")
console.log(tmp)
console.log("Does not work:")
console.log(fruits.querySelector("ul:first-of-type li:first-of-type span"))
<main id="app" data-v-app="">
<section>
<h2>Tree</h2>
<ul role="tree">
<li role="treeitem" data-segment="fruits" aria-level="1" aria-setsize="3" aria-posinset="1" aria-expanded="true"><span tabindex="0">Fruits</span>
<ul role="group">
<li role="none" data-segment="oranges" aria-level="2" aria-setsize="5" aria-posinset="1"><span tabindex="-1">Oranges</span>
<!--v-if-->
</li>
<li role="none" data-segment="pineapple" aria-level="2" aria-setsize="5" aria-posinset="2"><span tabindex="-1">Pineapple</span>
<!--v-if-->
</li>
<li role="treeitem" data-segment="apples" aria-level="2" aria-setsize="5" aria-posinset="3" aria-expanded="false"><span tabindex="-1">Apples</span>
<ul role="group">
<li role="none" data-segment="macintosh" aria-level="3" aria-setsize="3" aria-posinset="1"><span tabindex="-1">Macintosh</span>
<!--v-if-->
</li>
<li role="none" data-segment="granny_smith" aria-level="3" aria-setsize="3" aria-posinset="2"><span tabindex="-1">Granny Smith</span>
<!--v-if-->
</li>
<li role="none" data-segment="fuji" aria-level="3" aria-setsize="3" aria-posinset="3"><span tabindex="-1">Fuji</span>
<!--v-if-->
</li>
</ul>
</li>
<li role="none" data-segment="bananas" aria-level="2" aria-setsize="5" aria-posinset="4"><span tabindex="-1">Bananas</span>
<!--v-if-->
</li>
<li role="none" data-segment="pears" aria-level="2" aria-setsize="5" aria-posinset="5"><span tabindex="-1">Pears</span>
<!--v-if-->
</li>
</ul>
</li>
<li role="none" data-segment="vegetables" aria-level="1" aria-setsize="3" aria-posinset="2"><span tabindex="-1">Vegetables</span>
<!--v-if-->
</li>
<li role="none" data-segment="grains" aria-level="1" aria-setsize="3" aria-posinset="3"><span tabindex="-1">Grains</span>
<!--v-if-->
</li>
</ul>
</section>
</main>
解决方法
documentation 解释了这一点:
element = baseElement.querySelector(selectors);
返回值
baseElement
的第一个后代元素,它与 selectors
的指定组匹配。 匹配时考虑元素的整个层次结构,包括元素集之外的元素,包括baseElement
及其后代;换句话说,selectors
首先应用于整个文档,而不是 baseElement
,以生成潜在元素的初始列表。然后检查结果元素以查看它们是否是 baseElement
的后代。这些剩余元素的第一个匹配项由 querySelector
方法返回。
(强调我的。)
让我们考虑一个简化的例子:
console.log(document.getElementById("a").querySelector("ul li span"));
<ul>
<li id="a"><span>A</span>
<ul>
<li><span>B</span></li>
</ul>
</li>
</ul>
这里,baseElement
是 document.getElementById("a")
; selectors
是 "ul li span"
。
document.querySelector("ul li span")
确实包含两个 <span>
,并且它们都在 baseElement
内。 <span>A</span>
恰好是这个集合中的第一个。
有一个名为 :scope
的新伪类可能会有所帮助:
console.log(document.getElementById("a").querySelector(":scope ul li span"));
<ul>
<li id="a"><span>A</span>
<ul>
<li><span>B</span></li>
</ul>
</li>
</ul>
el.querySelector(selector)
可能会返回令人惊讶的结果,如果您不知道它在后台是如何工作的。
el.querySelector(selector)
实际情况是这样的:
(el,selector) => [...document.querySelectorAll(selector)].filter(node => el !== node && el.contains(node))[0]
看这个例子:
const el = document.getElementById('outer');
const selector = 'span > span';
console.log(el.querySelector(selector).id); // logs "inner",not "innermost"
// same as if you did
console.log([...document.querySelectorAll(selector)].filter(node => el !== node && el.contains(node))[0].id);
<span id="outermost">
<span id="outer">
<span id="inner">
<span id="innermost"></span>
</span>
</span>
</span>
因此,如果您执行了 el.querySelector(selector)
,则 document.querySelectorAll(selector)
将返回满足以下条件的第一个匹配项:找到的节点必须是 el
的后代。
更令人惊讶的是,如果您传递一个您认为没有匹配项的 el.querySelector
,selector
甚至会找到匹配项,因为 selector
的部分甚至不在 {{ 1}}:
el
const el = document.getElementById('outer');
const selector = '#outermost > span > span';
console.log(el.querySelector(selector).id); // logs "inner"!
// same as if you did
console.log([...document.querySelectorAll(selector)].filter(node => el !== node && el.contains(node))[0].id);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。