JASoft.org

El blog de José Manuel Alarcón Aguín. Programación web y mucho más...

MENÚ - JASoft: JM Alarcón

Los selectores CSS se procesan de derecha a izquierda (y por qué eso te debe importar)

CSS3Las hojas de estilo en cascada o CSS son indispensables en cualquier aplicación Web no trivial. Nos permiten definir el estilo visual (e incluso el comportamiento) que mostrarán los diferentes elementos de contenido de una página o aplicación Web. Sin ellas serían imposibles la separación entre contenido y visualización o la adaptación de un mismo diseño de página a diferentes dispositivos.

Todos estamos acostumbrados a escribir CSS con sus diversas combinaciones de selectores, y cuando adquieren su verdadera potencia es, precisamente, al combinar las definiciones de selectores para poder asignar estilos con mucha exactitud.

Por ejemplo, imaginemos que tenemos una página con multitud de listas no ordenadas (por ejemplo colecciones de recursos en Internet) y queremos darle un estilo particular a los elementos de determinadas de estas listas. Podríamos especificar un selector como este:

 1: .recursos li {
 2:     font-weight: bold;
 3:     font-size: large;
 4: }

Con ello estamos indicando al navegador que cuando se encuentre con un elemento de lista que esté contenido dentro de un elemento que tenga aplicada la clase “recursos”, ponga su letra en tamaño grande y con negrita.

Así que si tenemos una lista como esta en la página:

 1: <ul class="recursos">
 2:     <li>Krasis</li>
 3:     <li>MAILCast</li>
 4:     <li>campusMVP</li>
 5:     <li>SELF</li>
 6: </ul>

Lo que obtendríamos sería algo similar a esto:

ListaRecursos

Hasta aquí todo muy simple y normal. No parece haber nada raro ¿verdad?

Bueno, en realidad no hay nada raro pero lo cierto es que en páginas grandes en las que debiésemos aplicar un estilo así podríamos tener problemas de rendimiento. Ahora explico por qué.

El “extraño” comportamiento de los navegadores

Cuando un programador ve un selector de estilos como el anterior lo primero que piensa, como es normal, es que el navegador buscará todos los elementos que tengan aplicada la clase “recursos” y luego dentro de cada uno de esos elementos buscará los elementos de lista, de modo que le aplicará el estilo indicado a los que encuentre ¿verdad?

Sin embargo los navegadores no trabajan de esa manera, sino de otra que puede parecer anti-intuitiva para la mayor parte de las situaciones.

La forma de proceder es justo la contraria. El navegador no carga la CSS y la procesa para aplicarla, sino que simplemente la procesa para crear una “base de datos en memoria” de las reglas que tiene definidas. Es más tarde al ir procesando el árbol de elementos de la página donde para cada elemento examina qué reglas le pueden corresponder y se las aplica.

Este comportamiento es lógico ya que las hojas CSS pueden contener cientos o miles de selectores, y una página sin embargo contiene habitualmente muchos menos elementos. Cuánto más tarde el navegador en procesar la página completa (incluidos los estilos) peor será la experiencia de usuario y menos útil y peor valorado estará. Así que es crucial que todo se procese a la mayor velocidad posible, y es más eficiente hacerlo a partir de los elementos que a partir de las definiciones contenidas en la CSS.

Así que lo normal es asumir que la mayor parte de los selectores CSS no van a coincidir con la mayoría de los elementos, lo cual es cierto para la mayoría de las páginas. Ciertas aplicaciones Web grandes pueden tener perfectamente más de mil selectores en sus hojas CSS (si no me crees echa un vistazo a SkyDrive o a GMail por ejemplo), por lo que buscar en la página los elementos que coincidan con el selector de cada una de ellas puede tardar muchísimo, y se hace justo al revés que es mucho más rápido y eficiente.

Esto implica que para un selector CSS como el del ejemplo:

 1: .recursos li

lo que se hace es primero buscar todos los li y luego ver cuáles están contenidos dentro de algún elemento con la clase “recursos”.

Es decir, en HTML los selectores CSS se procesan de derecha a izquierda y no al revés que parece más intuitivo.

En realidad, para ser más exactos, cuando el procesador de la página se encuentra con un elemento <li> lo que hace es ver qué reglas CSS existen que afecten a los elementos de este tipo. En nuestro ejemplo el selector le fuerza comprobar si cada <li> está o no contenido en algún nivel de la jerarquía dentro de un elemento con la clase “recursos” y si es así le aplica lo que indica el estilo para ese selector.

Si nos imaginamos una página con muy pocos estilos parece poco razonable actuar así. Sin embargo cuando la página contiene relativamente pocos elementos y muchos selectores CSS este modo de proceder hace que el procesamiento gane mucho en velocidad y eficiencia.

¿Y esto en qué me afecta?

Todo lo anterior parece mucho más ineficiente que procesar el selector y por lo tanto buscar todos los <li> contenidos en elementos con la clase “recursos”. Y de hecho lo es para este caso considerado aisladamente. Pero como ha quedado demostrado en páginas con muchos selectores es mucho más rápido en realidad que hacerlo de la forma que nos parece más intuitiva a priori.

En realidad en una página común todo esto no tiene demasiada importancia más allá de ser una curiosidad teórica, ya que en cualquier caso el procesamiento será muy rápido. Sin embargo en aplicaciones o sitios webs complejos como los mencionados, cada décima de segundo cuenta para dar una respuesta rápida a los usuarios y hacerlos más agradables de usar. Si cada vez que se cargase una carpeta de correo en Hotmail se tardase medio segundo más en visualizar la página la gente huiría en masa del servicio.

Ahora considera de nuevo nuestro sencillo ejemplo de las listas de recursos con el selector anterior, pero que actúa dentro de una página muy grande, que contenga miles de elementos <li> en nuestras listas de recursos. Podría ser por ejemplo una página auto-generada con listas enormes de datos generadas desde una base de datos, algunos de los cuales son recursos y se les debe aplicar el selector de ejemplo anterior, y otros son elementos de otros tipos de listas que no llevan ese estilo.

Con el nuevo conocimiento que hemos adquirido sobre cómo funciona el procesamiento de selectores lo que ocurriría es que por cada elemento <li> que se encuentra al procesar el DOM, el navegador tiene que mirar qué estilo le debe aplicar, y por lo tanto para cada uno de ellos debe comprobar en nuestro ejemplo si tiene un padre en algún nivel superior de la página que tenga aplicada la clase “recursos”. Esto hará que la aplicación de los estilos sea muy ineficiente y costosa. Y más si consideramos que puede haber listas anidadas y otros selectores CSS que le afecten.

Sabiendo esto podemos solucionar el posible problema cambiando el modo de seleccionar los elementos. Por ejemplo, sería mucho mejor aplicarles un identificador o una clase específica para que el navegador localice el estilo a aplicarles de manera directa.

Como digo, en cualquier página normal (incluso aunque tenga mil elementos <li>) el impacto de una ineficiencia así será inapreciable. Sin embargo en páginas complejas con muchos estilos solapados y muchos elementos a los que aplicárselos (piensa en GMail o SkyDirve) el impacto puede ser muy apreciable. Así que si el rendimiento nos importa debemos analizar más a fondo nuestros selectores CSS.

Obviamente esta es una sola de las muchas optimizaciones que podemos aplicar al CSS, pero como es muy poco conocida me ha parecido interesante resaltarla aquí.

¡Espero que te sea útil!

José Manuel Alarcón José Manuel Alarcón
Fundador de campusMVP.es, el proyecto de referencia en formación on-line para programadores en lengua española. Autor de varios libros y cientos de artículos. Galardonado como MVP de Microsoft desde 2004. Gallego de Vigo, amante de la ciencia y la tecnología, la música y la lectura. Ayudando a la gente en Internet desde 1996.
Descarga GRATIS mi último libro (no técnico): "Tres Monos, Diez Minutos".
Banner

Comentarios (4) -

Info muy útil, por un lado es curioso para cualquier desarrollador web, y por otro muy interesante para las grandes webs donde llega un momento en el que hay que arañar de donde se pueda para optimizar.

Enhorabuena por tu artículo (y ya de paso, por tu blog) :)

Responder

Spain José Manuel Alarcón

Gracias Antón :-)

Responder

Buenas José Manuel,

sabrías decirme causas por las que un determinado selector que se refiere a clases anidadas (tipo .clase1 .clase2{estilos}) puede no seleccionar lo que intuitivamente se espera que seleccione?

Reformulo a ver si me explico. Tengo ese selector definiendo una serie de estilos para aplicar a una capa con class='aaa bbb ccc clase2' que se encuentra dentro de otra (no es hija sino descendiente en general) con class=''ddd eee fff clase1'? En ambos casos hay otras clases que acompañan los class de cada capa.

En algún caso me he encontrado con esta rareza (al menos para mi) y compruebo con firebug que a la capa interior no se le está aplicando los estilos de ese selector.

Alguna teoría?

Enhorabuena por el artículo, entra tanto en profundidad en el funcionamiento del navegador y css que he pensado que los tiros pueden ir por ahí y deberse a alguna particularidad de esa interpretación por parte del navegador.

GRacias por adelantado!!

Responder

Spain José M. Alarcón

Lo siento, no puedo ayudarte con eso, no se me ocurre así a bote pronto :-S

Responder

Agregar comentario