¿En qué dirección leen los motores de los selectores, exactamente?

Siempre he creído (aunque ahora dudo de la validez de estas creencias) que:

div.name 

Fue más rápido que:

 .name 

Sin embargo, he leído recientemente que la mayoría de los motores de selección de CSS leen de derecha a izquierda, en cuyo caso ¿no sería el primer ejemplo realmente más lento? Como el motor selector simplemente encontraría cada elemento con una clase de nombre, y luego tendría que identificar cuáles de esos eran div s?

¿De qué manera leen los motores de selección de CSS en general? ¿De izquierda a derecha o de derecha a izquierda? Y si generalmente leen de derecha a izquierda, ¿podría alguien ofrecerme una explicación de por qué (no veo cómo tiene sentido leer de derecha a izquierda en términos de un motor de selección)?

Sin embargo, he leído recientemente que la mayoría de los motores de selección de CSS leen de derecha a izquierda, en cuyo caso ¿no sería el primer ejemplo realmente más lento?

¿Cuál es la forma en que los motores de selección de CSS leen en general? ¿De izquierda a derecha o de derecha a izquierda? Y si generalmente leen de derecha a izquierda, ¿podría alguien ofrecerme una explicación de por qué (no veo cómo tiene sentido leer de derecha a izquierda en términos de un motor de selección)?

Francamente, es casi imposible saber qué selector será más lento en un navegador determinado, y mucho menos en todos los navegadores. El rendimiento tiende a fluctuar y ser impredecible, especialmente a esas escalas microscópicas y con estructuras de documentos impredecibles. Incluso si hablamos de rendimiento teórico, en última instancia, depende de la implementación.

Habiendo dicho eso, como se muestra en la respuesta de Boris Zbarsky a esta otra pregunta y en la respuesta de Guffa a la suya , un navegador típico (esto es actualmente cierto para todos los principales motores de diseño) toma un elemento y evalúa todos los selectores candidatos para ver cuáles coincide , en lugar de encontrar un conjunto de elementos que coincidan con un selector determinado. Esta es una diferencia sutil pero muy importante. Boris ofrece una explicación técnica que no solo es increíblemente detallada, sino también autoritaria (ya que trabaja en Gecko, el motor utilizado por Firefox), así que sugiero leerla.

Pero pensé que debería abordar lo que parece ser otra preocupación en su pregunta:

Como el motor selector simplemente encontraría cada elemento con una clase de nombre, y luego tendría que identificar cuáles de esos eran div s?

Además del comentario de Patrick McElhaney :

La pregunta vinculada explica por qué los selectores se leen de derecha a izquierda en general, por lo que #foo ul.round.fancy li.current se lee li.current, ul.round.fancy, #foo , pero ¿realmente se lee right-to? -izquierda dentro de cada elemento ( .current, li, .fancy, .round, ul, #foo )? ¿Deberia ser?

Nunca he implementado CSS, ni he visto cómo otros navegadores lo implementan. Sabemos por las respuestas vinculadas anteriormente que los navegadores utilizan la coincidencia de derecha a izquierda para desplazarse a través de los combinadores dentro de los selectores, como los combinadores en este ejemplo:

 section > div.second > div.third 

Si un elemento no es un div.third , entonces no tiene sentido verificar si su padre es un div.second cuyo padre es una section .

Sin embargo, no creo que este orden de derecha a izquierda llegue hasta el nivel de selector simple. En otras palabras, no creo que los navegadores utilicen la evaluación de derecha a izquierda para cada parte de una secuencia de selector simple (también conocida como un selector compuesto) dentro de la evaluación de derecha a izquierda a través de una serie de selectores compuestos separados por combinators.

Por ejemplo, considere este selector artificial y altamente exagerado:

 div.name[data-foo="bar"]:nth-child(5):hover::after 

Ahora, no hay garantía de que un navegador verifique necesariamente estas condiciones para un elemento en el siguiente orden:

  1. ¿Está el puntero sobre este elemento?
  2. ¿Este elemento es el quinto hijo de su padre?
  3. ¿Este elemento tiene un atributo de data-foo con la bar valores?
  4. ¿Este elemento tiene una clase de name ?
  5. ¿Es esto un elemento div ?

Tampoco este selector, que es funcionalmente idéntico al anterior, excepto con sus simples selectores mezclados, necesariamente será evaluado en el siguiente orden:

 div:hover[data-foo="bar"].name:nth-child(5)::after 
  1. ¿Este elemento es el quinto hijo de su padre?
  2. ¿Este elemento tiene una clase de name ?
  3. ¿Este elemento tiene un atributo de data-foo con la bar valores?
  4. ¿Está el puntero sobre este elemento?
  5. ¿Es esto un elemento div ?

Simplemente no hay razón por la cual dicha orden se imponga por motivos de rendimiento. De hecho, creo que el rendimiento mejoraría eligiendo primero ciertos tipos de selectores simples, sin importar dónde se encuentren en una secuencia. (También notará que el ::after no se tiene en cuenta, porque los pseudo-elementos no son simples selectores y nunca entran en la ecuación correspondiente).

Por ejemplo, es muy conocido que los selectores de ID son los más rápidos. Bueno, Boris dice esto en el último párrafo de su respuesta a la pregunta vinculada:

Tenga en cuenta también que hay otras optimizaciones que los navegadores ya hacen para evitar incluso tratar de hacer coincidir las reglas que definitivamente no coincidirán. Por ejemplo, si el selector situado más a la derecha tiene una identificación y esa identificación no coincide con la identificación del elemento, no se intentará hacer coincidir ese selector con ese elemento en Gecko: el conjunto de “selectores con ID” que se intentan proviene de una búsqueda de hashtable en la identificación del elemento. Así que esto es el 70% de las reglas que tienen una buena posibilidad de coincidencia que aún no coinciden después de considerar solo la etiqueta / clase / id del selector de la derecha.

En otras palabras, si tiene un selector que se ve así:

 div#foo.bar:first-child 

O esto:

 div.bar:first-child#foo 

Gecko siempre verificará primero el ID y la clase, independientemente de dónde se encuentre en la secuencia. Si el elemento no tiene una ID y una clase que coincida con el selector, se descarta al instante. Bastante rápido si me preguntas.

Eso fue solo Gecko como un ejemplo. Esto también puede diferir entre las implementaciones (p. Ej., Gecko y WebKit pueden hacerlo de forma diferente a Trident o incluso a Prest). Hay estrategias y enfoques generalmente aceptados por los proveedores, por supuesto (no es probable que exista una diferencia en la verificación de las identificaciones primero), pero los detalles pueden diferir.

El motor de selección no busca elementos para aplicar una regla, busca reglas que se apliquen a un elemento específico. Por lo tanto, tiene sentido leer los selectores de derecha a izquierda.

Un selector como este:

 div span.text a.demo 

Haría que el motor selector hiciera estas comprobaciones para ver si el selector se aplica a un elemento:

  • ¿Es un elemento con la demo la clase?
  • ¿Tiene un ancestro que es un elemento de span con el text la clase?
  • ¿Ese elemento tiene un ancestro que es un elemento div ?