后代未正确选择的 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 时,它被正确设置为 litest。我不明白为什么如果我从那里运行查询,它仍然包含第一个 a 标记。正如我之前所说,如果我只查询ul &gt; li,它会给我正确的li 标签。

【问题讨论】:

显示你是如何设置parent的。 相关:Why does querySelector('div span') match even though querySelector('div') does not?. 【参考方案1】:

这是因为您尝试忽略的第一个 &lt;a&gt; 标记仍属于选择器规则 ul &gt; li &gt; a。因此,即使您以 &lt;li class="test"&gt; 作为根开始查询(顺便说一下,我不知道为什么其他答案说文档仍然是根),它找到的第一个子元素是&lt;a&gt; 标签,实际上,它是 &lt;li&gt; 的子代,&lt;ul&gt; 的子代。这最终会“超过”您指定的根的事实被忽略了。

编辑: 如果您希望选择器规则也适用于根,您可以使用它来代替:

parent.querySelectorAll(":scope ul > li > a");

为了进一步澄清,browser CSS engines evaluate CSS rules right-to-left。在您的想法中,您希望浏览器从根(父级&lt;li class="test"&gt;)开始,然后找到&lt;ul&gt; 标记,然后找到&lt;li&gt; 标记,然后找到&lt;a&gt; 标记。

但是,浏览器从根目录开始,然后查找根目录下的所有&lt;a&gt; 标记。然后它检查&lt;a&gt; 标记是否在&lt;li&gt; 内,然后检查&lt;li&gt; 是否在&lt;ul&gt; 内。所以&lt;li class="parent"&gt; 标记确实是根,但之后它不遵循选择器规则的从左到右的层次结构,而是从右到左。

【讨论】:

所以你是说在运行这种类型的后代查询时,它总是忽略根,只找到符合选择器规则的项目?? @chavab_1 不,我不是这个意思。它只会找到符合选择器规则且位于根下方的项目——第一个 &lt;a&gt; 标记位于根下方。但是,它“忽略”的部分是 &lt;a&gt; 标记的父元素使其适合规则可以位于根之上。 @chavab_1 如果这不是您想要的行为,请参阅我关于添加:scope的编辑 非常感谢。我不知道;我期待与在 CSS 中使用选择器规则时相同的行为。正如您所解释的,我在此链接中找到了确切的答案:developer.mozilla.org/en-US/docs/Web/API/Element/… 现在一切正常。再一次,谢谢!! 我发现 IE(大惊喜)和 Opera 都不支持 :scope 伪类。你有另一种方法来解决这个任务吗?我可以忽略节点列表中的第一个节点,它会解决这个问题,但是我还必须确保我只选择直接后代的节点,因为每个子 as 也可以有一个 ul , lias 自己的。我唯一能想到的就是使用children 方法,直到我到达我需要的节点。但我只是认为必须有更好的解决方案。【参考方案2】:

这是因为您的第一个 &lt;a&gt; 也匹配 ul &gt; li &gt; 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>

【讨论】:

我认为这并不能真正回答问题。正在使用子选择器 (&gt;),因此它应该只返回作为 li 子元素的 a 元素,而这些元素又是 ul 元素的子元素。这更像是他使用ul &gt; li, li &gt; a 他没想到会被选中的aul &gt; li &gt; a匹配的a 我不会称之为“古怪”。它准确地选择了符合选择器描述的元素。他们可以使用:scope 来限制选择。 parent.querySelectorAll(":scope ul &gt; li &gt; a");【参考方案3】:

使用它来定义您的var navChild = document.querySelectorAll("test &gt; ul &gt; li &gt; a");。我不得不使用一个正在运行的示例来解决这个问题。这将得到你想要的 3 vanilla &lt;a&gt;

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 &gt; li &gt; a"); 所有 4 个项目符号都会受到影响。 是的,但是代码中的第一个 &lt;a&gt; 仍然是父 .test 的子代 - 这并不意味着 element.qSA 总是选择文档中的每个元素,它只是意味着你的例子是错误的。见这里:jsfiddle.net/qt83o3ph 你能详细说明这个例子有问题吗?我看到您从 ul &gt; li &gt; a 中删除了 &lt;a&gt; 并将其放在外面。我认为显示另一个具有相同结构的列表会更有效。我明白你在那种情况下的意思,但不是试图解决的情况。见这里:jsfiddle.net/qt83o3ph/6 您暗示选择的第一个 是 element.qSA 选择文档中所有匹配元素的示例,但 &lt;a&gt;qqqq&lt;/a&gt; 仍然是的子元素#test,因此无论 qSA 选择所有匹配元素还是仅选择正在调用元素 qSA 的子元素的匹配元素,都会选择它。 (另一个问题是("test &gt; ul &gt; li &gt; a") 将不起作用,除非文档中恰好有一个非标准标签&lt;test&gt;

以上是关于后代未正确选择的 queryselectorAll的主要内容,如果未能解决你的问题,请参考以下文章

for 循环 querySelectorAll

querySelectorAll( ) 方法

`querySelectorAll` - 使用数据选择器查询时返回空值

js高级选择器querySelector和querySelectorAll

javascript高级选择器querySelector和querySelectorAll

重要选择器querySelector和querySelectorAll