带有后代的 queryselectorAll 没有正确选择

queryselectorAll with descendant not selecting correctly

我有以下 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>

我想 select 3 a 标签使用原版 javascript 并从 li 开始使用 class 测试。我将它放在 parent 变量中,然后使用 querySelectorAll 尝试仅获取下一个列表中的 a 标签。但是,返回的节点列表还包括第一个 a 标记,即使它不是后代。这是我的代码:

var navChild = parent.querySelectorAll("ul > li > a");

真正奇怪的是,如果我只查询 li 个后代,我只会得到 3 个。所以我不明白为什么当我查询那个 li 的子后代时,我也得到第一个 a 标签,它比 DOM.

高两层

我知道我可以使用 jQuery 来做到这一点,但我不想使用它。

已编辑以显示我如何设置父变量

我尝试在第一个 a 标签上按下回车键后将那些 a 标签添加到 childs 变量想要包含在子节点列表中。

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 和 class test。我不明白为什么如果我从那里 运行 查询它仍然包含第一个 a 标记。正如我之前所说,如果我只查询 ul > li 它会给我正确的 li 标签。

这是因为您的第一个 <a> 也匹配 ul > li > a,并且由于所说的 a 仍然是调用元素 qSA 的后代,它被包含在 NodeList 中。

https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll#Javascript

Element.querySelector 和 Element.querySelector 在这方面都有些古怪:虽然唯一匹配的元素必须是您调用它的元素的子元素,但选择器字符串 仍然开始在文档级别 - 它不会首先通过隔离元素及其后代来验证选择器。

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>

这是因为您试图忽略的第一个 <a> 标记仍然属于选择器规则 ul > li > a。因此,即使您以 <li class="test"> 作为根开始查询(顺便说一句,我不知道为什么其他答案说文档仍然是根),第一个子元素它finds 是 <a> 标签,实际上,它是 <li> 的子标签,而 <li><ul> 的子标签。事实上,这最终会 "above" 您指定的根被忽略。

编辑: 如果您也希望选择器规则的范围限定为根目录,则可以改用它:

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"> 标签确实是根,但之后它不遵循选择器规则的 left-to-right 层次结构,而是 right-to-left.

使用它来定义您的 var navChild = document.querySelectorAll("test > ul > li > a");。我不得不使用 运行 示例来解决问题。这将得到你想要的 3 个香草 <a>

querySelectorAll() 方法 returns 文档中匹配指定 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>