¿Por qué los navegadores coinciden con los selectores CSS de derecha a izquierda?

560

Los selectores de CSS coinciden con los motores del navegador de derecha a izquierda. Entonces, primero encuentran a los niños y luego verifican a sus padres para ver si coinciden con el resto de las partes de la regla.

  1. ¿Por qué es esto?
  2. ¿Es solo porque la especificación dice?
  3. ¿Afecta el diseño final si se evaluó de izquierda a derecha?

Para mí, la forma más sencilla de hacerlo sería utilizar los selectores con el menor número de elementos. Entonces, los ID primero (ya que solo deberían devolver 1 elemento). Entonces, tal vez clases o un elemento que tenga el menor número de nodos, por ejemplo, puede haber solo un intervalo en la página, así que vaya directamente a ese nodo con cualquier regla que haga referencia a un intervalo.

Aquí hay algunos enlaces que respaldan mis afirmaciones

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. https://developer.mozilla.org/en/Writing_Efficient_CSS

Parece que se hace de esta manera para evitar tener que mirar a todos los hijos de los padres (que podrían ser muchos) en lugar de a todos los padres de un niño que debe ser uno. Incluso si el DOM es profundo, solo vería un nodo por nivel en lugar de múltiples en la coincidencia RTL. ¿Es más fácil / rápido evaluar los selectores CSS LTR o RTL?

tgandrews
fuente
55
3. No, no importa cómo lo lea, el selector siempre coincide con el mismo conjunto de elementos.
Šime Vidas
39
Por lo que vale, un navegador no puede asumir que sus ID son únicos. Podría pegar el mismo id = "foo" en todo su DOM, y un #fooselector necesitaría coincidir con todos esos nodos. jQuery tiene la opción de decir que $ ("# foo") siempre devolverá solo un elemento, porque están definiendo su propia API con sus propias reglas. Pero los navegadores necesitan implementar CSS, y CSS dice que haga coincidir todo en el documento con la ID dada.
Boris Zbarsky
44
@Quentin En un documento "no conforme" (a HTML), las identificaciones de los documentos pueden no ser únicas, y en esos documentos CSS requiere hacer coincidir todos los elementos con esa identificación. CSS en sí mismo no establece requisitos normativos para que los ID sean únicos; El texto que cita es informativo.
Boris Zbarsky
55
@Boris Zbarsky Lo que hace jQuery depende de la ruta del código dentro de jQuery. En algunos casos, jQuery usa la API NodeSelector (nativa querySelectorAll). En otros casos, se usa Sizzle. Sizzle no coincide con múltiples ID pero QSA sí (AYK). La ruta tomada depende del selector, el contexto y el navegador y su versión. La API de consulta de jQuery utiliza lo que he denominado "Native First, Dual Approach". Escribí un artículo sobre eso, pero está abajo. Aunque puede encontrar aquí: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
55
Ni siquiera estoy seguro de que nadie, pero ustedes dos, puedan descubrir qué está pasando con esta larga conversación. Tenemos chat para estas discusiones extendidas. Cualquier cosa que realmente desee mantener debe incluirse en la pregunta o respuesta, especialmente si se trata de información aclaratoria. Stack Overflow no maneja bien la discusión en los comentarios.
George Stocker

Respuestas:

824

Tenga en cuenta que cuando un navegador está haciendo una selección de selector, tiene un elemento (el que está tratando de determinar el estilo) y todas sus reglas y sus selectores y necesita encontrar qué reglas coinciden con el elemento. Esto es diferente de lo habitual de jQuery, por ejemplo, donde solo tiene un selector y necesita encontrar todos los elementos que coincidan con ese selector.

Si solo tenía un selector y solo un elemento para comparar con ese selector, entonces de izquierda a derecha tiene más sentido en algunos casos. Pero definitivamente esa no es la situación del navegador. El navegador está tratando de representar Gmail o lo que sea y tiene el <span>que está tratando de diseñar y las más de 10,000 reglas que Gmail pone en su hoja de estilo (no estoy inventando ese número).

En particular, en la situación en que el navegador está mirando la mayoría de los selectores que está considerando no coinciden con el elemento en cuestión. Entonces, el problema se convierte en decidir que un selector no coincide lo más rápido posible; si eso requiere un poco de trabajo extra en los casos que coinciden, aún ganas debido a todo el trabajo que ahorras en los casos que no coinciden.

Si comienza haciendo coincidir la parte más a la derecha del selector con su elemento, entonces es probable que no coincida y ya está. Si coincide, tiene que hacer más trabajo, pero solo proporcional a la profundidad de su árbol, que no es tan grande en la mayoría de los casos.

Por otro lado, si comienza haciendo coincidir la parte más a la izquierda del selector ... ¿con qué lo hace coincidir? Debe comenzar a recorrer el DOM, buscando nodos que puedan coincidir con él. Descubrir que no hay nada que coincida con la parte izquierda puede llevar un tiempo.

Entonces los navegadores coinciden desde la derecha; proporciona un punto de partida obvio y le permite deshacerse de la mayoría de los selectores candidatos muy rápidamente. Puede ver algunos datos en http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (aunque la notación es confusa), pero el resultado es el de Gmail en particular hace dos años, para el 70% de los pares (regla, elemento), podría decidir que la regla no coincide después de solo examinar las partes de etiqueta / clase / id del selector más a la derecha de la regla. El número correspondiente para el conjunto de pruebas de rendimiento de carga de página de Mozilla fue del 72%. Por lo tanto, realmente vale la pena tratar de deshacerse de esos 2/3 de todas las reglas lo más rápido posible y luego solo preocuparse por igualar el 1/3 restante.

Tenga en cuenta también que hay otros optimizadores que los navegadores ya hacen para evitar incluso intentar igualar reglas que definitivamente no coincidirán. Por ejemplo, si el selector más a la derecha tiene una identificación y esa identificación no coincide con la identificación del elemento, entonces no habrá ningún intento de hacer coincidir ese selector con ese elemento en Gecko: el conjunto de "selectores con ID" que se intentan proviene de una búsqueda de tabla hash en la ID del elemento. Entonces, este es el 70% de las reglas que tienen una muy buena posibilidad de coincidencia que aún no coinciden después de considerar solo la etiqueta / clase / id del selector más a la derecha.

Boris Zbarsky
fuente
55
Como un pequeño bono, tiene más sentido leerlo RTL que LTR incluso en inglés. Un ejemplo: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
66
Tenga en cuenta que la coincidencia RTL solo se aplica a los combinadores . No profundiza en el nivel de selector simple. Es decir, un navegador toma el selector compuesto más a la derecha , o la secuencia de selectores simples e intenta emparejarlo atómicamente. Luego, si hay una coincidencia, sigue al combinador hacia la izquierda hasta el siguiente selector compuesto y comprueba el elemento en esa posición, y así sucesivamente. No hay evidencia de que un navegador lea cada parte de un selector compuesto RTL; de hecho, el último párrafo muestra exactamente lo contrario (las comprobaciones de identificación siempre son lo primero).
BoltClock
55
En realidad, cuando coinciden los selectores, al menos en Gecko, el nombre de la etiqueta y el espacio de nombres son lo primero. La identificación (así como el nombre de etiqueta y los nombres de clase) se considera en un paso de prefiltrado que elimina la mayoría de las reglas sin realmente tratar de hacer coincidir los selectores.
Boris Zbarsky
Eso podría ayudar dependiendo de qué optimizaciones esté haciendo el UA, pero no ayudará al paso de prefiltrado en Gecko que describo anteriormente. Sin embargo, hay un segundo paso de filtrado que funciona en ID y clases que se usa para combinadores descendientes solo que podría ayudar.
Boris Zbarsky
@Benito Ciaro: Sin mencionar los problemas de especificidad también.
BoltClock
33

El análisis de derecha a izquierda, también llamado análisis de abajo hacia arriba, es realmente eficiente para el navegador.

Considera lo siguiente:

#menu ul li a { color: #00f; }

El navegador primero busca a, luego li, luego uly luego #menu.

Esto se debe a que mientras el navegador escanea la página, solo necesita mirar el elemento / nodo actual y todos los nodos / elementos anteriores que ha escaneado.

Lo que hay que tener en cuenta es que el navegador comienza a procesar el momento en que obtiene una etiqueta / nodo completo y no necesita tener que esperar toda la página, excepto cuando encuentra un script, en cuyo caso se detiene temporalmente y completa la ejecución del script y luego sigue adelante

Si lo hace al revés, será ineficiente porque el navegador encontró el elemento que estaba escaneando en la primera verificación, pero luego se vio obligado a continuar buscando en el documento todos los selectores adicionales. Para esto, el navegador debe tener todo el html y es posible que deba escanear toda la página antes de comenzar a pintar con CSS.

Esto es contrario a cómo la mayoría de las bibliotecas analizan dom. Allí se construye el dom y no necesita escanear toda la página, solo encuentra el primer elemento y luego sigue haciendo coincidir otros dentro de él.

aWebDeveloper
fuente
20

Permite la conexión en cascada de lo más específico a lo menos específico. También permite un corto circuito en la aplicación. Si la regla más específica se aplica en todos los aspectos a los que se aplica la regla principal, se ignoran todas las reglas principales. Si hay otros bits en el padre, se aplican.

Si fue al revés, formateará de acuerdo con el padre y luego sobrescribirá cada vez que el niño tenga algo diferente. A la larga, esto es mucho más trabajo que ignorar los elementos en las reglas que ya se tienen en cuenta.

Gregory A Beamer
fuente
11
Esa es una cuestión aparte. Puede hacer la cascada ordenando las reglas por especificidad y luego haciendo coincidirlas en orden de especificidad. Pero la pregunta aquí es por qué para una regla dada, usted hace coincidir sus selectores de una manera particular.
Boris Zbarsky