¿Por qué mi selector jQuery: not () no funciona en CSS?

Tengo este diseño:

Sectors

Con estas reglas de 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; } 

Yo uso jQuery para agregar la clase unassigned a sectores que de otra manera no tienen una de las clases alpha , beta o gamma :

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

Luego aplico algunas reglas diferentes a esa clase:

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

Y todo funciona perfectamente en los navegadores modernos.

Vista previa jsFiddle interactiva

Pero viendo como el selector :not() en jQuery está basado en :not() en CSS3 , estaba pensando que podría moverlo directamente a mi hoja de estilos para no tener que depender de agregar una clase extra usando jQuery. Además, no estoy realmente interesado en soportar versiones anteriores de IE, y otros navegadores tienen soporte excelente para el selector :not() .

Así que trato de cambiar la porción .unassigned anterior a esto (sabiendo que solo tendré sectores Α, Β y Γ en mi diseño):

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

Pero tan pronto como lo hago, deja de funcionar, ¡en todos los navegadores ! Mis sectores no asignados no aparecen atenuados, desvanecidos o etiquetados como “Sin asignar”.

Vista previa jsFiddle actualizada pero no tan interactiva

¿Por qué el selector :not() funciona en jQuery pero falla en CSS? ¿No debería funcionar de manera idéntica en ambos lugares ya que jQuery afirma ser “compatible con CSS3”, o hay algo que me falta?

¿Existe una solución pura de CSS para esto o tendré que depender de un script?

¿Por qué el selector :not() funciona en jQuery pero falla en CSS? ¿No debería funcionar de manera idéntica en ambos lugares ya que jQuery afirma ser “compatible con CSS3”, o hay algo que me falta?

Tal vez debería , pero resulta que no : jQuery amplía el selector :not() para que pueda pasarle cualquier selector , sin importar lo complejo que sea, y sospecho que la razón principal de esto es para la paridad con el método .not() , que también toma los filtros y selectores arbitrariamente complejos en consecuencia. De alguna manera mantiene una syntax similar a CSS, pero se extiende desde lo definido en el estándar.

Como otro ejemplo, esto funciona bien (sé que es un ejemplo increíblemente ridículo en comparación con lo que se da en la pregunta, pero es sólo para fines ilustrativos):

 /* * 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]))') 

Vista previa jsFiddle

Recuerde que pasar una lista de selectores separados por comas a :not() significa filtrar elementos que no coinciden con ninguno de los selectores listados .

Ahora, la pseudo-clase :not() en el nivel 3 de Selectores , por otro lado, está muy limitada por sí misma. Solo puede pasar un único selector simple como argumento a :not() . Esto significa que puede pasar solo uno de estos a la vez:

  • Selector universal ( * ), opcionalmente con un espacio de nombres
  • Selector de tipos ( a , div , span , ul , li , etc.), opcionalmente con un espacio de nombres
  • Selector de atributos ( [att] , [att=val] , etc.), opcionalmente con un espacio de nombres
  • Selector de clase ( .class )
  • Selector de ID ( #id )
  • Pseudo-clase ( :pseudo-class )

Entonces, aquí están las diferencias entre el selector jQuery’s :not() y el selector estándar :not() :

  1. En primer lugar, para responder la pregunta directamente: no puede pasar una lista de selector separada por comas. 1 Por ejemplo, mientras el selector dado funciona en jQuery como se demuestra en el violín, no es válido CSS:

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

    ¿Existe una solución pura de CSS para esto o tendré que depender de un script?

    Afortunadamente, en este caso, hay. Simplemente tiene que encadenar selectores múltiples :not() , uno detrás de otro, para que sea válido CSS:

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

    No hace que el selector sea mucho más largo, pero la inconsistencia y la inconveniencia siguen siendo evidentes.

    Vista previa jsFiddle interactiva actualizada

  2. No puede combinar selectores simples en selectores compuestos para usar con :not() . Esto funciona en jQuery, pero no es CSS válido:

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

    Tendrá que dividirlo en múltiples negaciones (¡no solo encadenarlas!) Para que sea válido CSS:

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

    Como puede ver, esto es aún más inconveniente que el punto 1.

  3. No puedes usar combinadores. Esto funciona en jQuery, pero no CSS:

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

    Este es un caso particularmente desagradable, principalmente porque no tiene una solución adecuada. Existen algunas soluciones alternativas ( 1 y 2 ), pero casi siempre dependen de la estructura HTML y, por lo tanto, su utilidad es muy limitada.

  4. En un navegador que implementa querySelectorAll() y el selector :not() , el uso de :not() en una cadena de selector de forma que lo convierte en un selector de CSS válido hará que el método devuelva resultados directamente, en lugar de volver a Sizzle (Motor selector de jQuery que implementa la extensión :not() ). Si eres un riguroso para el rendimiento, este es un bono positivamente minúsculo que definitivamente salivarás.

La buena noticia es que Selectores 4 mejora el selector :not() para permitir una lista de selectores complejos separados por comas. Un selector complejo es simplemente un selector simple, compuesto o simple, o una cadena completa de selectores compuestos separados por combinadores. En resumen, todo lo que ves arriba.

Esto significa que los ejemplos de jQuery anteriores se convertirán en selectores de nivel 4 válidos, lo que hará que la pseudoclase sea mucho, mucho más útil cuando las implementaciones de CSS comiencen a respaldarla en los próximos años.


1 Aunque este artículo dice que puedes pasar una lista de selectores separados por comas a :not() en Firefox 3, no se supone que puedas hacerlo. Si funciona en Firefox 3 como dice el artículo, se debe a un error en Firefox 3 para el cual ya no puedo encontrar el ticket, pero no debería funcionar hasta que los navegadores futuros implementen estándares futuros. Al ver con qué frecuencia se cita ese artículo hasta la fecha, he dejado un comentario en este sentido, pero viendo también la antigüedad del artículo y la poca frecuencia con la que se actualiza el sitio, no cuento con que el autor vuelva a corregirlo. eso.