Los selectores CSS nos permiten definir con mucha precisión el aspecto (¡y comportamiento!) que van a tener los elementos HTML en nuestras páginas. Así, utilizamos: estilos embebidos, identificadores, clases, pseudo-clases, etiquetas y atributos para definir exactamente cómo ha de funcionar cada elemento.
Las hojas de estilo .css suelen tener decenas o cientos de estilos que el navegador debe aplicar a cada elemento. Muchos de estos estilos entran en conflicto, por lo que ¿cómo decide el navegador qué estilo o estilos concretos debe aplicar a un determinado elemento de la página?
Pongamos un ejemplo sencillo y consideremos el siguiente fragmento de HTML:
1: <ul id="menu">
2: <li>Menu 001</li>
3: <li class="destacado">Menu 002</li>
4: <li>Menu 003</li>
5: </ul>
Al cual se le aplican los siguientes dos estilos:
1: ul#menu li {
2: font-weight:normal;
3: font-size:14px;
4: color: blue;
5: }
6:
7: .destacado {
8: font-weight:bold;
9: color:red;
10: text-decoration: underline;
11: }
Si leemos los selectores vemos que el primero se aplica a los elementos de lista (li) que se encuentran dentro de listas sin ordenar (ul) cuyo identificador es “menu”, es decir afecta a todos los nuestros. En ese caso se les aplica un tamaño de letra de 14px y el color azul para el texto. El segundo selector indica que los elementos que lleven la clase “destacado” aplicada, deberán mostrarse en negrita, con letra de color rojo y con una línea de subrayado.
Según esto, aparentemente, el aspecto que debería mostrar el HTML una vez renderizado en el navegador sería este:
Sin embargo, el verdadero aspecto que muestra es este otro:
Es decir, el selector CSS para la clase “destacado” no se está aplicando en su totalidad, sino que solamente aplica la parte del subrayado, pero no la negrita ni el color rojo para el texto.
¿A qué es debido esto?
La especificidad de los selectores CSS
Como decía, en una página típica hay decenas o cientos de selectores CSS y a un mismo elemento se le pueden aplicar varios de ellos. El navegador debe elegir en cada momento cuál es el apropiado, ya que en muchas ocasiones estos selectores pueden tener valores contradictorios. En el ejemplo anterior, a pesar de su sencillez, ya lo hemos experimentado. Ambos estilos se aplican a nuestro elemento destacado y tienen indicaciones contrarias para el peso del texto y su color, así que ¿cuál debe aplicar el navegador?
Para decidirlo los navegadores utilizan las reglas de la especificidad de los selectores. Es decir, cuanto mas específico es un selector mayor fuerza tiene y es el que prevalece.
Para calcular la especificidad se genera un número que dota de peso diferente a cada elemento, de acuerdo con el siguiente esquema de cálculo:
En este gráfico los elementos situados más a la izquierda se consideran más específicos que los de la derecha, por lo que tendrán más peso a la hora de decidir qué estilos aplicar a un determinado elemento cuando haya conflicto.
Así, los estilos que se especifiquen directamente en la etiqueta HTML siempre tendrán la mayor precedencia y se aplicarán siempre. Si hubiésemos escrito:
1: <li style="color:red;font-weight:bold;">Menu 002</li>
Da igual lo que indiquen el resto de selectores CSS incluidos en la cabecera o en un .css externo: se aplicará el estilo in-line que hemos indicado.
Para el resto de selectores se deben contar los elementos de cada tipo que haya en dicho selector y colocar el número resultante en cada uno de los recuadros que hay en la figura de arriba. El número resultante será la especificidad del selector, y cuanto mayor sea mayor precedencia tendrá.
Por ejemplo, para nuestro selector “.destacado” la especificidad es simplemente 0,0,1,0 (o, si lo pasamos a un número decimal normal (aunque no lo sea, OJO), sería simplemente un 10). ¿Por qué? Pues porque tiene simplemente una clase, es decir, un elemento para el tercer recuadro, en el que se deben sumar las clases, pseudo-clases y atributos.
Para el otro selector “ul#menu li”, la especificidad es 0,1,0,2 (o lo que es lo mismo, el número 102), ya que especifica un identificador (@menu) y dos elementos HTML (ul y li).
Dado que 102 es mayor que 10, el primero de los selectores es más específico y por lo tanto cuando haya reglas que entren en conflicto son las de éste las que aplican.
Es por eso que sale el texto con color azul, sin negrita,pero sin embargo sí que se aplica el subrayado también, ya que para esta regla no existe conflicto alguno.
Si abrimos las Developer Tools de Internet Explorer o de Chrome y seleccionamos el elemento destacado, podemos ver los estilos resultantes y comprobaremos que efectivamente se aplican ambos estilos pero uno prevalece sobre el otro (lo he marcado aquí en Chrome):
¿Como haríamos entonces para que se aplique realmente el estilo que deseábamos en nuestro elemento destacado?
Pues siendo más específicos, por ejemplo con este selector:
1: ul#menu li.destacado {
2: font-weight:bold;
3: color:red;
4: text-decoration: underline;
5: }
En este caso la especificidad es 0,1,1,2 (1 identificador, 1 clase y dos elementos HTML), es decir, 112, que es mayor que 102 y por lo tanto se aplicaría con mayor prioridad, consiguiendo el resultado que buscábamos.
Algunas cosas a tener en cuenta
La regla es bastante sencilla, pero además debemos tener en cuenta algunas reglas adicionales:
- A igualdad de especificidad siempre gana el selector que esté definido más tarde en la página. Por ejemplo, si tenemos un elemento al que se le aplican las clases “menu destacado” y tenemos dos selectores “.menu” y “.destacado” (ambos aplican exactamente igual al elemento), con estilos que entran en conflicto, ganará el último de los dos que esté definido en la página.
- Un elemento de mayor especificidad siempre gana a cualquier número de elementos de menor especificidad. Por ejemplo, imaginemos un selector muy loco que contiene una clase y mete 12 etiquetas HTML en la definición. La clase gana a los elementos HTML aunque si lo convirtiésemos en un número sería 1,12, y en decimal, al pasar de 10, habría que sumarlo a las decenas. Por eso decía que no debe considerarse como un número decimal, sino que se debe ver cuál de los elementos es superior empezando por la izquierda. Lo de convertirlo en un valor decimal sólo sirve como abstracción, para hacerlo más sencillo de comprender, y sirve para casi todos los casos porque realmente es muy raro que haya más de 9 elementos de un tipo especificados. Si los hay seguramente es que la CSS está bastante mal definida.
- El selector universal (*) no tiene especificidad, o para ser más exactos, su especificidad es 0,0,0,0, por lo que no aporta nada a la especificidad.
- El modificador “!important” aplicado a un atributo de estilo siempre gana. Lo podemos usar para cuando realmente sea muy importante que un estilo se aplique a un elemento o clase y nos queramos saltar las reglas de especificidad. No deberíamos usarlo más que en casos muy especiales, y desde luego no deberíamos abusar de él.
Calculadoras de especificidad
Aunque el cálculo es bastante fácil y además podemos usar las Developer Tools de los navegadores para cuando haya dudas sobre qué estilos prevalecen, puede ser útil tener a mano una calculadora de especificidad de selectores, que nos saque de dudas en algunas ocasiones.
Las dos más conocidas son:
- Specificity Calculator de Keegan Street: permite comparar de manera muy visual y atractiva dos selectores cualesquiera.
- CSS Specificity Calculator de Nicholas Bannister-Andrews: es mucho más espartana que la anterior, pero tiene la ventaja de que nos permite calcular la especificidad de muchos selectores de un solo golpe, por lo que es muy rápido si necesitamos muchos cálculos.
Ambos explican los resultados de sus cálculos para ayudarnos a entenderlos.
Este post está basado en material extraído de mi curso online de HTML(5) y CSS(3) de campusMVP. Si quieres aprender de verdad sobre este tema y tenerme como tutor para contestar tus dudas empieza hoy mismo.
Fácil, ¿no?. Pues la especificidad es tal vez el concepto más importante que debes conocer y tener claro sobre CSS. Te evitará muchas sorpresas y te ayudará a saber porqué fallan los estilos de tus páginas.
¡Espero que te sea útil!