El selector inválido de CSS hace que se descarte la regla: ¿Cuál es el razonamiento?

Estoy buscando más enlaces a discusiones de listas de correo, etc. en lugar de especulaciones.

¿Alguien puede ayudarme a encontrar la razón de ser de las reglas de manejo de errores citadas de la especificación CSS Select Level 3 ?

Los agentes de usuario deben observar las reglas para manejar los errores de análisis:

  • un selector simple que contiene un prefijo de espacio de nombre no declarado no es válido
  • un selector que contiene un selector simple no válido, un combinador no válido o un token no válido no es válido.
  • un grupo de selectores que contiene un selector no válido no es válido.

Las especificaciones que reutilizan Selectores deben definir cómo manejar los errores de análisis. (En el caso de CSS, se descarta la regla completa en la que se usa el selector.)

Tenía la siguiente regla:

#menu li.last, #menu li:last-child { ... } 

Para compensar la falta de soporte de último hijo de IE8, utilicé una clase y un complemento de JavaScript. Sin embargo, esto no funcionó porque IE8 cumple con la especificación de CSS sobre el manejo de errores y descarta toda la regla porque no reconoce un selector. Esto puede solucionarse separando los dos selectores en reglas individuales.

¿Por qué es esto deseable? ¿Por qué la especificación no sugiere simplemente descartar el selector no reconocido, sino mantener el rest de la regla?

Me gustaría saber la razón, ya que las reglas actualmente parecen contraintuitivas.

¿Por qué es esto deseable? ¿Por qué la especificación no sugiere simplemente descartar el selector no reconocido, sino mantener el rest de la regla?

La respuesta corta es porque sería demasiado difícil para las implementaciones descubrir qué constituye exactamente “el rest de la regla” (o “el rest de la lista de selectores” para el caso) sin equivocarse e inadvertidamente estropeando los diseños. así como también por la coherencia en el manejo de errores y la compatibilidad con futuras especificaciones.


Prefacio mi respuesta larga con un enlace a otra respuesta mía , sobre el manejo de selectores inválidos. Un comentario sobre esa respuesta apunta directamente a la sección 4.1.7 de la especificación CSS2.1 sobre el tratamiento de errores en selectores dentro de conjuntos de reglas, que menciona comas en selectores como ejemplo. Creo que lo resume bastante bien:

CSS 2.1 le da un significado especial a la coma (,) en los selectores. Sin embargo, dado que no se sabe si la coma puede adquirir otros significados en futuras actualizaciones de CSS, toda la statement se debe ignorar si hay un error en algún lugar del selector, aunque el rest del selector parezca razonable en CSS 2.1.

Mientras que la coma en sí todavía significa agrupar dos o más selectores en lo que respecta a los selectores, resulta que los Selectores 4 introducen nuevas pseudo-clases funcionales que aceptan grupos de selector (o listas de selector) como argumentos, tales como :matches() ( incluso cambia :not() entonces acepta una lista, haciéndola similar a :matches() , mientras que en el nivel 3 solo acepta un solo selector simple).

Esto significa que no solo encontrará grupos de selectores separados por comas asociados a las reglas, sino que también los encontrará dentro de las pseudo-clases funcionales (tenga en cuenta que esto está dentro de una hoja de estilo solamente; fuera de CSS, los selectores pueden aparecer en Código JavaScript, utilizado por las bibliotecas de selector y la API de Selectores nativos).

Aunque no es la única razón de lejos, esto solo es suficiente para complicar demasiado las reglas de manejo de errores de un analizador con un gran riesgo de romper el selector, el conjunto de reglas o incluso el diseño. En el caso de un error de análisis con una coma, el analizador tendrá problemas para determinar si este grupo de selector corresponde a un conjunto de reglas completo, o parte de otro grupo de selector, y cómo manejar el rest del selector y su conjunto de reglas asociadas en consecuencia . En lugar de tratar de adivinar, arriesgarse a adivinar incorrectamente y romper la regla de alguna manera (por ejemplo, al unir y diseñar todos los elementos incorrectos), la apuesta más segura es descartar la regla y seguir adelante.

Como ejemplo, considere la siguiente regla, cuyo selector es válido en el nivel 4 pero no en el nivel 3, tomado de esta pregunta mía :

 #sectors > div:not(.alpha, .beta, .gamma) { color: #808080; background-color: #e9e9e9; opacity: 0.5; } 

Un analizador ingenuo que no comprende Selectors 4 puede tratar de dividir esto en tres selectores distintos que comparten el mismo bloque de statement, en lugar de un selector único con una pseudoclase que acepta una lista, basada solo en las comas:

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

Si simplemente descarta los primeros y últimos selectores que obviamente no son válidos, dejando el segundo selector que es válido, ¿debería entonces intentar aplicar la regla a cualquier elemento con clase beta ? Claramente, no es lo que el autor tiene la intención de hacer, por lo que si un navegador hace eso, va a hacer algo inesperado en este diseño . Al descartar la regla con el selector no válido, el diseño se ve un poco más sano , pero eso es un ejemplo demasiado simplificado; las reglas con estilos que alteran el diseño pueden causar problemas incluso mayores si se aplican incorrectamente.

Por supuesto, también pueden ocurrir otras ambigüedades en el análisis del selector, que pueden llevar a las siguientes situaciones:

  • Sin saber dónde termina el selector complejo
  • Sin saber dónde termina la lista de selectores
  • Sin saber dónde comienza el bloque de statement
  • Una combinación de lo anterior

Todo lo cual, de nuevo, se resuelve más fácilmente descartando el conjunto de reglas en lugar de jugar al juego de adivinanzas.

En el caso de selectores aparentemente bien formados que no se reconocen, como por ejemplo :last-child como una pseudo-clase en su ejemplo, la especificación no hace distinción entre selectores y selectores no reconocidos que están simplemente malformados. Ambos resultan en un error de análisis. Desde la misma sección a la que enlazas:

La invalidez es causada por un error de análisis, por ejemplo, un token no reconocido o un token que no está permitido en el punto de análisis actual.

Y al hacer esa afirmación sobre :last-child supongo que el navegador puede analizar un solo punto seguido de un identificador arbitrario como una pseudoclase en primer lugar; en realidad no se puede asumir que una implementación sabrá analizar :last-child como una pseudo-clase correctamente, o algo así como :lang() o :not() con una notación funcional ya que las pseudo-clases funcionales no aparecieron hasta CSS2.

Selectors define un conjunto específico de pseudo-clases y pseudo-elementos conocidos, cuyos nombres probablemente estén codificados en cada implementación. Los analizadores sintácticos más ingenuos tienen toda la notación para cada pseudoclase y pseudoelemento, incluidos los dos puntos simples / dobles, codificados (no me sorprendería que los principales navegadores hagan esto con :before , :after , :first-letter y :first-line como caso especial ). Entonces, lo que puede parecer una implementación de pseudoclase a uno podría ser muy difícil para otro.

Debido a que hay muchas maneras en que fallan las implementaciones, la especificación no hace distinción, lo que hace que el manejo de errores sea mucho más predecible. Si un selector no se reconoce, no importa si es no compatible o está mal formado, la regla se descarta. Simple, directo y fácil de entender.


Dicho todo esto, hay al menos una discusión en la lista de correo pública de estilo www que sugiere que se modifique la especificación porque, después de todo, puede no ser tan difícil implementar el manejo de errores dividiendo los selectores.

También debo mencionar que algunos motores de diseño se comportan de manera diferente, como WebKit haciendo caso omiso de selectores con prefijo WebKit en una regla, aplicando sus propios prefijos, mientras que otros navegadores ignoran por completo la regla (puede encontrar más ejemplos en Desbordamiento de stack; aquí hay un poco diferente ). De alguna manera, podría decirse que WebKit está bordeando la regla tal como está, aunque trata de analizar inteligentemente los grupos de selector separados por comas a pesar de esos selectores prefijados.

No creo que el grupo de trabajo tenga una razón convincente para cambiar este comportamiento todavía. De hecho, en todo caso, tienen una razón convincente para no cambiarlo , y eso se debe a que los sitios han estado confiando en este comportamiento durante muchos años. En el pasado, teníamos hacks de selector para filtrar versiones anteriores de IE; hoy, tenemos selectores prefijados para filtrar otros navegadores. Todos estos hacks se basan en el mismo comportamiento de ciertos navegadores que descartan reglas que no reconocen, y otros navegadores los aplican si creen que son correctos, por ejemplo, al reconocer prefijos (o lanzar solo los no reconocidos, como hace WebKit). Los sitios podrían romperse en versiones más nuevas de esos navegadores si esta regla cambiara, lo cual no puede suceder en una web tan diversificada (léase: fragmentada) como la nuestra.

A partir de abril de 2013 , se decidió en una teleconferencia que este comportamiento no se modificará por la razón que he postulado anteriormente:

    - RESUELTO: No adopte la invalidación estilo MQ para Selectores
                debido a preocupaciones de compatibilidad web.

La invalidación del estilo de consulta de medios hace referencia a las consultas de medios no válidos en una lista separada por comas que no rompe la regla @media completa.