后代未正确选择的 queryselectorAll
Posted
技术标签:
【中文标题】后代未正确选择的 queryselectorAll【英文标题】:queryselectorAll with descendant not selecting correctly 【发布时间】:2018-09-07 18:33:13 【问题描述】:我有以下 DOM 结构:
var parent = document.querySelector(".test");
var navChild = parent.querySelectorAll("ul > li > a");
for (i = 0; i < navChild.length; i++)
navChild[i].style.backgroundColor = "red";
console.log(navChild.lenght);
console.log(navChild);
<!-- begin snippet: js hide: false console: true babel: false -->
<ul>
<li class="test">
<a>ssss</a> <!--this will also be affected -->
<ul>
<li><a>fsfsf</a></li>
<li><a>sff</a></li>
<li><a>ggg</a></li>
</ul>
</li>
</ul>
我想使用 vanilla javascript 选择 3 个 a
标记,并从带有类测试的 li
开始。我把它放在一个parent
变量中,然后使用querySelectorAll 来尝试只获取下一个列表中的a
标记。但是,返回的节点列表还包括第一个 a
标记,即使它不是后代。这是我的代码:
var navChild = parent.querySelectorAll("ul > li > a");
真正奇怪的是,如果我只查询 li
后代,我只会得到 3。所以我不明白为什么当我查询 li
的子后代时,我也会得到第一个 @987654332 @ 标记在 DOM 上两级。
我知道我可以使用 jQuery 做到这一点,但我不想使用它。
已编辑以显示我如何设置父变量
我正在尝试将那些a
标签添加到childs
变量中,然后在我不想包含在子节点列表中的第一个a
标签上按下回车键.
var alinks = document.querySelectorAll('.test > a');
[].forEach.call(alinks, function(link)
link.addEventListener('keyup', function(e)
e.preventDefault();
if(e.keyCode === 13)
var linkParent = e.target.parentElement;
var childs = menuParent.querySelectorAll('ul > li > a');
)
);
当我登录 linkParent
时,它被正确设置为 li
类 test
。我不明白为什么如果我从那里运行查询,它仍然包含第一个 a
标记。正如我之前所说,如果我只查询ul > li
,它会给我正确的li
标签。
【问题讨论】:
显示你是如何设置parent
的。
相关:Why does querySelector('div span') match even though querySelector('div') does not?.
【参考方案1】:
这是因为您尝试忽略的第一个 <a>
标记仍属于选择器规则 ul > li > a
。因此,即使您以 <li class="test">
作为根开始查询(顺便说一下,我不知道为什么其他答案说文档仍然是根),它找到的第一个子元素是<a>
标签,实际上,它是 <li>
的子代,<ul>
的子代。这最终会“超过”您指定的根的事实被忽略了。
编辑: 如果您希望选择器规则也适用于根,您可以使用它来代替:
parent.querySelectorAll(":scope ul > li > a");
为了进一步澄清,browser CSS engines evaluate CSS rules right-to-left。在您的想法中,您希望浏览器从根(父级<li class="test">
)开始,然后找到<ul>
标记,然后找到<li>
标记,然后找到<a>
标记。
但是,浏览器从根目录开始,然后查找根目录下的所有<a>
标记。然后它检查<a>
标记是否在<li>
内,然后检查<li>
是否在<ul>
内。所以<li class="parent">
标记确实是根,但之后它不遵循选择器规则的从左到右的层次结构,而是从右到左。
【讨论】:
所以你是说在运行这种类型的后代查询时,它总是忽略根,只找到符合选择器规则的项目?? @chavab_1 不,我不是这个意思。它只会找到符合选择器规则且位于根下方的项目——第一个<a>
标记位于根下方。但是,它“忽略”的部分是 <a>
标记的父元素使其适合规则可以位于根之上。
@chavab_1 如果这不是您想要的行为,请参阅我关于添加:scope
的编辑
非常感谢。我不知道;我期待与在 CSS 中使用选择器规则时相同的行为。正如您所解释的,我在此链接中找到了确切的答案:developer.mozilla.org/en-US/docs/Web/API/Element/… 现在一切正常。再一次,谢谢!!
我发现 IE(大惊喜)和 Opera 都不支持 :scope
伪类。你有另一种方法来解决这个任务吗?我可以忽略节点列表中的第一个节点,它会解决这个问题,但是我还必须确保我只选择直接后代的节点,因为每个子 a
s 也可以有一个 ul
, li
和 a
s 自己的。我唯一能想到的就是使用children
方法,直到我到达我需要的节点。但我只是认为必须有更好的解决方案。【参考方案2】:
这是因为您的第一个 <a>
也匹配 ul > li > a
,并且由于说 a
仍然是调用元素 qSA 的后代,因此它被包含在 NodeList 中。
https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll#Javascript
Element.querySelector 和 Element.querySelectorAll 在这方面有些古怪:虽然唯一匹配的元素必须是您调用它的 Element 的子元素,但选择器字符串 仍然从文档级别开始 -它不会通过首先隔离元素及其后代来验证选择器。
const child = document.querySelector('#child');
const span = child.querySelector('#parent span');
console.log(span.textContent);
<div id="parent">
<div id="child">
<span>text</span>
</div>
</div>
【讨论】:
我认为这并不能真正回答问题。正在使用子选择器 (>
),因此它应该只返回作为 li
子元素的 a
元素,而这些元素又是 ul
元素的子元素。这更像是他使用ul > li, li > a
他没想到会被选中的a
是与ul > li > a
匹配的a
。
我不会称之为“古怪”。它准确地选择了符合选择器描述的元素。他们可以使用:scope
来限制选择。 parent.querySelectorAll(":scope ul > li > a");
【参考方案3】:
使用它来定义您的var navChild = document.querySelectorAll("test > ul > li > a");
。我不得不使用一个正在运行的示例来解决这个问题。这将得到你想要的 3 vanilla <a>
querySelectorAll() 方法将文档中与指定 CSS 选择器匹配的所有元素作为静态 NodeList 对象返回。
我认为即使您在文档中设置了父级也是如此。
var navChild = document.querySelectorAll(".test > ul > li > a");
for (i = 0; i < navChild.length; i++)
navChild[i].style.backgroundColor = "red";
//console.log(navChild.lenght);
//console.log(navChild);
<ul>
<li class="test">
<a>qqqq</a>
<ul>
<li><a>aaa</a></li>
<li><a>bbb</a></li>
<li><a>ccc</a></li>
</ul>
</li>
</ul>
【讨论】:
不正确 - 对元素调用的 qSA 仅选择该元素的子元素。 @CertainPerformance 如果您编辑我的运行示例并使用以下内容:var parent = document.querySelector(".test"); var navChild = parent.querySelectorAll("ul > li > a");
所有 4 个项目符号都会受到影响。
是的,但是代码中的第一个 <a>
仍然是父 .test
的子代 - 这并不意味着 element.qSA 总是选择文档中的每个元素,它只是意味着你的例子是错误的。见这里:jsfiddle.net/qt83o3ph
你能详细说明这个例子有问题吗?我看到您从 ul > li > a
中删除了 <a>
并将其放在外面。我认为显示另一个具有相同结构的列表会更有效。我明白你在那种情况下的意思,但不是试图解决的情况。见这里:jsfiddle.net/qt83o3ph/6
您暗示选择的第一个 是 element.qSA
选择文档中所有匹配元素的示例,但 <a>qqqq</a>
仍然是的子元素#test
,因此无论 qSA 选择所有匹配元素还是仅选择正在调用元素 qSA 的子元素的匹配元素,都会选择它。 (另一个问题是("test > ul > li > a")
将不起作用,除非文档中恰好有一个非标准标签<test>
)以上是关于后代未正确选择的 queryselectorAll的主要内容,如果未能解决你的问题,请参考以下文章
`querySelectorAll` - 使用数据选择器查询时返回空值
js高级选择器querySelector和querySelectorAll