0

假设 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>

我想要“容器” div 中具有“MyClass”类的所有元素,但“excludedElement”div 中的元素除外。在这种情况下,结果仅包含跨度 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 选择器来检索这组特定的元素?

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

4

4 回答 4

2

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

container.querySelectorAll('div:not(#excludedElement) > 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)')];
于 2020-01-24T01:11:01.543 回答
1

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

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>

于 2020-01-24T00:59:53.420 回答
0

我有这个....

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>

于 2020-01-24T01:19:00.503 回答
0

您可以在以下位置使用此精确选择器.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>

于 2020-01-24T14:40:33.683 回答