【问题标题】:How to use a CSS selector that gets all matching elements except those inside a specific child?如何使用 CSS 选择器来获取除特定子元素之外的所有匹配元素?
【发布时间】:2020-01-24 22:40:33
【问题描述】:

假设 HTML 结构如图所示:

<div id="container">
     <div class="A">
         <div id="excludedElement">
             <p>
                 <span class="MyClass">1</span>
                 <span class="MyClass">2</span>
                 <span class="MyClass">3</span>
             </p>
         </div>
     </div>
     <div class="B">
         <p>
             <span class="MyClass">4</span>
             <span class="MyClass">5</span>
             <span class="MyClass">6</span>
         </p>
     </div>
</div>

除了“excludedElement”div 中的元素之外,我想要“容器”div 中具有“MyClass”类的所有元素。在这种情况下,结果仅包含跨度 4、5 和 6。

我目前的解决方案是首先使用“MyClass”获取所有元素,然后使用“MyClass”获取excludedElement 内的所有元素。对于第一个列表中的每个元素,我们检查它是否在第二个列表中,如果是则跳过它。这是 O(n^2) 运行时间,所以我想避免这种情况。伪代码供参考:

const allElements = container.querySelectorAll('.MyClass');
const excludedElements = container.querySelectorAll('#excludedElement .MyClass');
var result = [];

for (const element in allElements)
{
    if (!excludedElements.Contains(element))
    {
        result.Add(element);
    }
}

有没有办法在 querySelectorAll() 中制作一个 CSS 选择器来检索这个特定的元素集?

一种方法是暂时从树中删除excludedElement,查询“MyClass”,然后替换excludedElement,但我想避免修改DOM。

【问题讨论】:

    标签: javascript html css css-selectors queryselector


    【解决方案1】:

    如果结构是可预测且已知的:

    container.querySelectorAll('div:not(#excludedElement) &gt; p .MyClass');

    如果结构已知,并且您可以添加类以避免 O(n^2):

    const excludes = [...container.querySelectorAll('#excludedElement .MyClass')];
    excludes.forEach(element => element.classList.add('excluded'));
    
    const filteredMyClass = [...container.querySelectorAll('.MyClass:not(.excluded)')];
    

    【讨论】:

    • @Mister Jojo 为我工作 - try.jsoup.org/~bswchh1jSMyrvL0dVYrAMn1KQn4
    • OP 使用的是container.querySelectorAll 而不是document.querySelectorAll 如果你使用document.querySelectorAll 它工作正常,可能可以简化为:not(#excludedElement) &gt; p .MyClass
    【解决方案2】:

    您可以选择所有.MyClass 后代,然后通过当前被迭代的项目是否具有#excludedElement 祖先.closest 来选择.filter 集合:

    const classes = [...container.querySelectorAll('.MyClass')]
      .filter(span => !span.closest('#excludedElement'));
    for (const span of classes) {
      span.style.backgroundColor = 'yellow';
    }
    <div id="container">
         <div class="A">
             <div id="excludedElement">
                 <p>
                     <span class="MyClass">1</span>
                     <span class="MyClass">2</span>
                     <span class="MyClass">3</span>
                 </p>
             </div>
         </div>
         <div class="B">
             <p>
                 <span class="MyClass">4</span>
                 <span class="MyClass">5</span>
                 <span class="MyClass">6</span>
             </p>
         </div>
    </div>

    除非您事先知道#container 后代的确切结构类型,否则我认为没有一种优雅的方法可以使用单个查询字符串来做到这一点; :not 只接受简单的选择器。

    仅出于提供信息的目的,您不应该使用的一种愚蠢且重复的方法是使用查询字符串:

    :scope > .MyClass,
    :scope > *:not(#excludedElement) > .MyClass,
    :scope > *:not(#excludedElement) > *:not(#excludedElement) > .MyClass
    ...
    

    const selector = `
    :scope > .MyClass,
    :scope > *:not(#excludedElement) > .MyClass,
    :scope > *:not(#excludedElement) > *:not(#excludedElement) > .MyClass
    `;
    
    const classes = container.querySelectorAll(selector);
    for (const span of classes) {
      span.style.backgroundColor = 'yellow';
    }
    <div id="container">
         <div class="A">
             <div id="excludedElement">
                 <p>
                     <span class="MyClass">1</span>
                     <span class="MyClass">2</span>
                     <span class="MyClass">3</span>
                 </p>
             </div>
         </div>
         <div class="B">
             <p>
                 <span class="MyClass">4</span>
                 <span class="MyClass">5</span>
                 <span class="MyClass">6</span>
             </p>
         </div>
    </div>

    【讨论】:

      【解决方案3】:

      我有这个....

      const  Excludes   = [...container.querySelectorAll('#excludedElement .MyClass')]
        ,    noExcludes = [...container.querySelectorAll('.MyClass')].filter(el=>(!Excludes.includes(el)))
        ;
      noExcludes.forEach(element => element.style.backgroundColor = 'lightgreen');
      <div id="container">
        <div class="A">
          <div id="excludedElement">
            <p>
              <span class="MyClass">1</span>
              <span class="MyClass">2</span>
              <span class="MyClass">3</span>
            </p>
          </div>
        </div>
        <div class="B">
          <p>
            <span class="MyClass">4</span>
            <span class="MyClass">5</span>
            <span class="MyClass">6</span>
          </p>
        </div>
      </div>

      【讨论】:

        【解决方案4】:

        你可以在.querySelectorAll()中使用这个精确的选择器:

        :not(#excludedElement) > p > .MyClass
        

        工作示例:

        const includedSpans = [... document.querySelectorAll(':not(#excludedElement) > p > .MyClass')];
        
        includedSpans.forEach((includedSpan) => console.log(includedSpan.textContent));
        <div id="container">
             <div class="A">
                 <div id="excludedElement">
                     <p>
                         <span class="MyClass">1</span>
                         <span class="MyClass">2</span>
                         <span class="MyClass">3</span>
                     </p>
                 </div>
             </div>
             <div class="B">
                 <p>
                     <span class="MyClass">4</span>
                     <span class="MyClass">5</span>
                     <span class="MyClass">6</span>
                 </p>
             </div>
        </div>

        【讨论】:

          猜你喜欢
          • 2019-11-25
          • 1970-01-01
          • 2016-03-19
          • 2013-05-22
          • 2011-05-06
          • 2013-09-28
          • 2011-02-04
          相关资源
          最近更新 更多