0
votes

Comment utiliser un sélecteur CSS qui obtient tous les éléments correspondants, à l'exception de ceux à l'intérieur d'un enfant spécifique?

Supposons une structure HTML comme indiqué:

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);
    }
}

Je veux tous les éléments à l'intérieur de la div "container" qui ont la classe "MyClass" à l'exception de ceux à l'intérieur de la div "exclusionElement". Dans ce cas, le résultat ne contient que les travées 4, 5 et 6.

Ma solution actuelle est d'abord d'obtenir tous les éléments avec "MyClass", puis d'obtenir tous les éléments à l'intérieur d'exclusElement avec "MyClass". Pour chaque élément de la première liste, nous vérifions s'il se trouve dans la seconde, et le sautons si c'est le cas. Il s'agit du temps d'exécution O (n ^ 2), donc j'aimerais éviter cela. Psuedocode pour référence:

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

Y a-t-il un moyen de créer un sélecteur CSS dans querySelectorAll () qui peut récupérer cet ensemble particulier d'éléments?

Une façon est de supprimer temporairement excluElement de l'arborescence, recherchez "MyClass", puis remplacez le excluElement, mais je veux éviter de modifier le DOM.


0 commentaires

4 Réponses :


1
votes

Vous pouvez sélectionner tous les descendants de .MyClass , puis .filter la collection selon que l'élément en cours d'itération a un ancêtre #excludedElement avec .closest:

<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>
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';
}

À moins que vous ne connaissiez à l'avance le type exact de structure des descendants de #container , je ne pense pas qu'il y ait une manière élégante de faire cela avec une seule chaîne de requête; : not n'accepte que des sélecteurs simples.

Juste à des fins d'information, une méthode idiote et répétitive que vous ne devriez pas utiliser serait d'utiliser la requête chaîne:

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

<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>
const classes = [...container.querySelectorAll('.MyClass')]
  .filter(span => !span.closest('#excludedElement'));
for (const span of classes) {
  span.style.backgroundColor = 'yellow';
}


0 commentaires

2
votes

Si la structure est prévisible et déjà connue:

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

Si le la structure n'est pas connue et vous êtes d'accord pour ajouter des classes afin d'éviter O (n ^ 2):

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

const filteredMyClass = [...container.querySelectorAll('.MyClass:not(.excluded)')];


2 commentaires

@Mister Jojo travaille pour moi - try.jsoup.org/~bswchh1jSMyrvL0dVYrAMn1KQn4


L'OP utilise container.querySelectorAll au lieu de document.querySelectorAll Si vous utilisez document.querySelectorAll cela fonctionne bien et peut probablement être simplifié en : non (#excludedElement)> p .MyClass



0
votes

J'ai ceci ....

<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>
const  Excludes   = [...container.querySelectorAll('#excludedElement .MyClass')]
  ,    noExcludes = [...container.querySelectorAll('.MyClass')].filter(el=>(!Excludes.includes(el)))
  ;
noExcludes.forEach(element => element.style.backgroundColor = 'lightgreen');


0 commentaires

0
votes

Vous pouvez utiliser ce sélecteur précis dans .querySelectorAll():

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

Exemple de travail:

const includedSpans = [... document.querySelectorAll(':not(#excludedElement) > p > .MyClass')];

includedSpans.forEach((includedSpan) => console.log(includedSpan.textContent));
:not(#excludedElement) > p > .MyClass


0 commentaires