Pourquoi mon sélecteur jQuery: not () ne fonctionne pas dans CSS?

J’ai cette mise en page:

Sectors

Avec ces règles CSS:

 #sectors { width: 584px; background-color: #ffd; margin: 1.5em; border: 4px dashed #000; padding: 16px; overflow: auto; } #sectors > h1 { font-size: 2em; font-weight: bold; text-align: center; } #sectors > div { float: left; position: relative; width: 180px; height: 240px; margin: 16px 0 0 16px; border-style: solid; border-width: 2px; } #sectors > div::after { display: block; position: absolute; width: 100%; bottom: 0; font-weight: bold; text-align: center; text-transform: capitalize; background-color: rgba(255, 255, 255, 0.8); border-top: 2px solid; content: attr(id) ' - ' attr(class); } #sectors > div:nth-of-type(3n+1) { margin-left: 0; } #sectors > div.alpha { color: #b00; background-color: #ffe0d9; } #sectors > div.beta { color: #05b; background-color: #c0edff; } #sectors > div.gamma { color: #362; background-color: #d4f6c3; } 

J’utilise jQuery pour append la classe unassigned aux secteurs qui n’ont pas l’une des classes alpha , beta ou gamma :

 $('#sectors > div:not(.alpha, .beta, .gamma)').addClass('unassigned'); 

Ensuite, j’applique des règles différentes à cette classe:

 #sectors > div.unassigned { color: #808080; background-color: #e9e9e9; opacity: 0.5; } #sectors > div.unassigned::after { content: attr(id) ' - Unassigned'; } #sectors > div.unassigned:hover { opacity: 1.0; } 

Et tout fonctionne parfaitement dans les navigateurs modernes.

Aperçu interactif de jsFiddle

Mais vu que le sélecteur :not() de jQuery est basé sur :not() dans CSS3 , je pensais pouvoir le déplacer directement dans ma feuille de style pour ne pas avoir à append une classe supplémentaire avec jQuery. De plus, je ne suis pas vraiment intéressé par les anciennes versions d’IE, et les autres navigateurs supportent parfaitement le sélecteur :not() .

J’essaie donc de modifier la partie ci-dessus. (Sachant que je n’aurai que des secteurs Α, Β et Γ dans ma mise en page):

 #sectors > div:not(.alpha, .beta, .gamma) { color: #808080; background-color: #e9e9e9; opacity: 0.5; } #sectors > div:not(.alpha, .beta, .gamma)::after { content: attr(id) ' - Unassigned'; } #sectors > div:not(.alpha, .beta, .gamma):hover { opacity: 1.0; } 

Mais dès que je fais cela, il cesse de fonctionner – dans tous les navigateurs ! Mes secteurs non atsortingbués ne sont plus grisés, supprimés ou étiquetés “Non atsortingbué”.

Aperçu de jsFiddle mis à jour mais moins interactif

Pourquoi le sélecteur :not() fonctionne-t-il dans jQuery mais échoue-t-il dans CSS? Est-ce que cela ne devrait pas fonctionner de la même façon dans les deux cas puisque jQuery prétend être “conforme à CSS3”, ou y a-t-il quelque chose qui me manque?

Existe-t-il une solution de contournement CSS pure ou devrais-je utiliser un script?

Pourquoi le sélecteur :not() fonctionne-t-il dans jQuery mais échoue-t-il dans CSS? Est-ce que cela ne devrait pas fonctionner de la même façon dans les deux cas puisque jQuery prétend être “conforme à CSS3”, ou y a-t-il quelque chose qui me manque?

Peut-être devrait -il le faire , mais il s’avère que ce n’est pas le cas : jQuery étend le sélecteur :not() à pouvoir y passer n’importe quel sélecteur , aussi complexe soit-il, et je soupçonne que la raison principale est que pour la parité avec la méthode .not() , qui prend également tout sélecteur et filtre arbitrairement complexe en conséquence. Il conserve en quelque sorte une syntaxe de type CSS, mais il s’étend de ce qui est défini dans la norme.

Autre exemple, cela fonctionne très bien (je sais que c’est un exemple incroyablement ridicule comparé à ce qui est donné dans la question, mais uniquement à des fins d’illustration):

 /* * Select any section * that's neither a child of body with a class * nor a child of body having a descendant with a class. */ $('section:not(body > [class], body > :has([class]))') 

Aperçu de jsFiddle

N’oubliez pas que transmettre une liste de sélecteurs séparés par des virgules à :not() signifie filtrer les éléments qui ne correspondent à aucun des sélecteurs répertoriés .

Maintenant, la pseudo-classe :not() dans le niveau 3 de Selectors , par contre, est très limitée par elle-même. Vous ne pouvez passer qu’un seul sélecteur simple en argument à :not() . Cela signifie que vous ne pouvez passer qu’un seul à la fois:

  • Sélecteur universel ( * ), éventuellement avec un espace de noms
  • Sélecteur de type ( a , div , span , ul , li , etc), éventuellement avec un espace de noms
  • Sélecteur d’atsortingbut ( [att] , [att=val] , etc), éventuellement avec un espace de noms
  • Sélecteur de classe ( .class )
  • Sélecteur d’ID ( #id )
  • Pseudo-classe ( :pseudo-class )

Donc, voici les différences entre le sélecteur jQuery :not() et le standard actuel :not() :

  1. Tout d’abord, pour répondre directement à la question: vous ne pouvez pas passer une liste de sélecteurs séparés par des virgules. 1 Par exemple, alors que le sélecteur donné fonctionne dans jQuery comme démontré dans le violon, ce n’est pas un CSS valide:

     /* If it's not in the Α, Β or Γ sectors, it's unassigned */ #sectors > div:not(.alpha, .beta, .gamma) 

    Existe-t-il une solution de contournement CSS pure ou devrais-je utiliser un script?

    Heureusement, dans ce cas, il y a. Vous devez simplement enchaîner plusieurs sélecteurs :not() , l’un après l’autre, pour le rendre valide:

     #sectors > div:not(.alpha):not(.beta):not(.gamma) 

    Cela ne rend pas le sélecteur beaucoup plus long, mais l’incohérence et les inconvénients restnt évidents.

    Mise à jour interactive de l’aperçu jsFiddle

  2. Vous ne pouvez pas combiner des sélecteurs simples dans des sélecteurs composés à utiliser avec :not() . Cela fonctionne dans jQuery, mais n’est pas valide CSS:

     /* Do not find divs that have all three classes together */ #foo > div:not(.foo.bar.baz) 

    Vous devrez le diviser en plusieurs négations (pas seulement les enchaîner!) Pour le rendre valide:

     #foo > div:not(.foo), #foo > div:not(.bar), #foo > div:not(.baz) 

    Comme vous pouvez le constater, cela est encore plus gênant que le point 1.

  3. Vous ne pouvez pas utiliser de combinateurs. Cela fonctionne dans jQuery, mais pas CSS:

     /* * Grab everything that is neither #foo itself nor within #foo. * Notice the descendant combinator (the space) between #foo and *. */ :not(#foo, #foo *) 

    Ceci est un cas particulièrement désagréable, principalement parce qu’il n’a pas de solution de rechange appropriée. Il existe quelques solutions de rechange ( 1 et 2 ), mais elles dépendent presque toujours de la structure HTML et leur utilité est donc très limitée.

  4. Dans un navigateur qui implémente querySelectorAll() et le sélecteur :not() , l’utilisation de :not() dans une chaîne de sélecteur de manière à en faire un sélecteur CSS valide provoquera le renvoi direct des résultats à la méthode, au lieu de revenir à Sizzle (Le moteur de sélection de jQuery qui implémente l’extension :not() ). Si vous êtes exigeant en matière de performance, il s’agit d’un bonus vraiment minuscule dont vous ne pourrez que saliver.

La bonne nouvelle est que le sélecteur 4 améliore le sélecteur :not() pour autoriser une liste de sélecteurs complexes séparés par des virgules. Un sélecteur complexe est simplement un sélecteur simple ou composé simple, ou une chaîne entière de sélecteurs composés séparés par des combinateurs. En bref, tout ce que vous voyez ci-dessus.

Cela signifie que les exemples jQuery ci-dessus deviendront des sélecteurs de niveau 4 valides, ce qui rendra la pseudo-classe beaucoup plus utile lorsque les implémentations CSS commenceront à la supporter dans les années à venir.


1 Bien que cet article indique que vous pouvez transmettre une liste de sélecteurs séparés par des virgules à :not() dans Firefox 3, vous n’êtes pas censé pouvoir le faire. Si cela fonctionne dans Firefox 3 comme le prétend cet article, alors c’est à cause d’un bogue dans Firefox 3 pour lequel je ne trouve plus le ticket, mais cela ne devrait pas fonctionner tant que les futurs navigateurs n’implémenteront plus les futures normes. Voyant à quelle fréquence cet article est cité à ce jour, j’ai laissé un commentaire à cet effet, mais en voyant aussi quel est l’âge de l’article et comment le site est mis à jour, je ne compte pas il.