Ir directamente al contenido de esta página
Aunque en el curso que se puede consultar en este sitio expliqué lo que significa la especificidad de una regla de CSS, quisiera ampliar lo dicho tratando un poco más extensamente lo referente a los pseudoselectores y a los selectores avanzados.
Los pseudoselectores se dividen en dos grupos, las pseudoclases —:first-child
, :link
, :visited
, :hover
, :active
, :focus
y :lang
— y los pseudoelementos —:first-line
, :first-letter
, :before
y :after
—. Las pseudoclases añaden su especificidad al valor de las clases, y los pseudoelementos al de los elementos1.
Siendo así, veamos el valor de especificidad de algunos selectores de ejemplo con pseudoclases:
Selector (con y sin pseudoclases) | Identificadores | Clases | Elementos |
---|---|---|---|
| |||
#contenido p | 1 | 0 | 1 |
#contenido p:first-child | 1 | 1 | 1 |
#contenido_adicional a | 1 | 0 | 1 |
#contenido_adicional a:link | 1 | 1 | 1 |
#contenido_adicional a:visited | 1 | 1 | 1 |
#contenido_adicional a:hover | 1 | 1 | 1 |
#contenido_adicional a:active | 1 | 1 | 1 |
#contenido_adicional a.externo | 1 | 1 | 1 |
#contenido_adicional a.externo:hover | 1 | 2 | 1 |
#contacto input | 1 | 0 | 1 |
#contacto input:focus | 1 | 1 | 1 |
a.externo:visited | 0 | 2 | 1 |
a:visited:hover | 0 | 2 | 1 |
Ahora, veamos unos ejemplos de la especificidad de algunos selectores con pseudoelementos:
Selectores (con y sin pseudoelementos) | Identificadores | Clases | Elementos |
---|---|---|---|
| |||
#contenido p | 1 | 0 | 1 |
#contenido p:first-line | 1 | 0 | 2 |
#contenido p.nota:first-letter | 1 | 1 | 2 |
ul li:before | 0 | 0 | 3 |
a:visited:after | 0 | 1 | 2 |
Como puede verse, la especificidad de los pseudoselectores no ofrece ninguna complicación especial; sólo es necesario recordar cuáles son pseudoclases y cuáles pseudoelementos para saber a qué valor de la especificidad se añaden.
En cuanto al futuro, en el último boceto —hasta ahora— del módulo de selectores de CSS 3 (inglés) se añaden varias pseudoclases nuevas manteniendo su sintaxis. Los pseudoelementos se mantienen, aunque precedidos por dobles dos puntos —::first-letter—. En cuanto a su especificidad, las pseudoclases siguen añadiéndose a las clases, pero los pseudoelementos vuelven a ser ignorados2.
Una nota sobre el soporte de psudoelementos de Internet Explorer: Hay que tener en cuenta unos cuantos datos a la hora de emplear estos cuatro pseudoelementos.
IE6 soporta :first-letter
y :first-line
, pero con un pequeño error en la implementación de su sintaxis, que hace necesario que incluir un espacio en blanco detrás del selector. Así, estas reglas no se aplicarían:
p:first-line{background-color:#FF0;}
p:first-line,.siglos{font-variant:small-caps;}
pero estas sí:
p:first-line {background-color:#FF0;}
p:first-line ,.siglos{font-variant:small-caps;}
En cuanto a :before
y :after
, no los soporta.
Por su parte, IE7 corrige el error de sintaxis para :first-letter
y :first-line
, aunque sigue sin soportar :before
y :after
.
Por último, IE8 Beta 2 sí parece dar un soporte completo.
CSS 2.1 permite seleccionar algunos elementos según las relaciones de parentesco que establecen con otros. La selección de descendientes sigue las reglas generales de la especificidad, pero hay que tener en mente lo que puede suponer que los combinadores para selección de hijos y de hermanos no añaden especificidad a una regla.
Primero, la tabla de ejemplos:
Selectores (con y sin parentesco) | Identificadores | Clases | Elementos |
---|---|---|---|
| |||
#contenido>p | 1 | 0 | 1 |
#contenido p | 1 | 0 | 1 |
#contenido p+p | 1 | 0 | 2 |
#contenido div p | 1 | 0 | 2 |
Revisemos los dos primeros selectores de ejemplo. Supongamos el siguiente código:
<div id="contenido">
<p>Lorem ipsum…</p>
<div>
<p>Ut enim ad minim…</p>
</div>
</div>
Apliquemos un estilo siguiendo el orden que he mostrado en la tabla:
#contenido>p{background-color:#FF0;}
#contenido p{background-color:#DDD;}
Literalmente estamos diciendo «aplica un fondo amarillo a los párrafos que sean hijos directos de contenido
y gris para cualquier párrafo dentro de ese mismo elemento». Desde el punto de vista lógico, parece que con la primera regla estamos apuntando a unos elementos más específicos que con la segunda; sin embargo, desde la definición de CSS no, por lo que el resultado es el que muestra la imagen, con ambos párrafos luciendo un suave fondo gris:
Así pues, si queremos lograr el efecto correcto, tenemos que aplicar las reglas no según su especificidad, sino basándonos en el orden de la cascada:
#contenido p{background-color:#DDD;}
#contenido>p{background-color:#FF0;}
Ahora sí, como se ve en la siguiente ilustración el efecto es el deseado:
Con el combinador para seleccionar hermanos ocurre lo mismo. Sobre el siguiente marcado:
<div id="contenido">
<p>Lorem ipsum…</p>
<p>Ut enim ad minim…</p>
<div>
<p>Lorem ipsum…</p>
<p>Ut enim ad minim…</p>
</div>
</div>
aplico las reglas siguientes esperando que todo párrafo precedido por otro tenga el fondo amarillo, pero que además los párrafos dentro de un div
adicional que no cumplan la condición presenten un fondo gris:
#contenido p+p{background-color:#FF0;}
#contenido div p{background-color:#DDD;}
Como la primera regla no tiene más especificidad que la segunda, todos los párrafos dentro del segundo div
presentan el fondo gris:
Al igual que para el ejemplo de selector de hijos, invirtiendo el orden de las declaraciones…
#contenido div p{background-color:#DDD;}
#contenido p+p{background-color:#FF0;}
…los estilos se aplican como se pretendía:
Los selectores de atributos se consideran clases en el cálculo de la especificidad de una regla, independientemente de que se indique o no un valor para el atributo:
Selectores (con y sin atributos) | Identificadores | Clases | Elementos |
---|---|---|---|
| |||
abbr | 0 | 0 | 1 |
abbr.destacar | 0 | 1 | 1 |
abbr[title] | 0 | 1 | 1 |
abbr[title].destacar | 0 | 2 | 1 |
a.externo | 0 | 1 | 1 |
a[class="externo"] | 0 | 1 | 1 |
a[class~="externo"] | 0 | 1 | 1 |
a[hreflang] | 0 | 1 | 1 |
a[hreflang="es"] | 0 | 1 | 1 |
a[href|="es"] | 0 | 1 | 1 |
Como se ve, tampoco tiene mayor misterio. Sólo un pequeño detalle, y es que el valor de clase se aplica a cualquier atributo, incluido id
. Esto significa que aplicadas las siguientes reglas:
#contenido{color:black;}
div[id="contenido"]{color:orange;}
el color del texto del contenido será negro, puesto que la especificidad de la primera regla es 1,0,0
, y la de la segunda 0,1,1
. Véalo.
Por último, un poco de información adicional para los obsesos del conocimiento: ¿cuál es el valor de especificidad del selector universal? En principio, su valor es cero:
Selectores (con y sin * ) | Identificadores | Clases | Elementos |
---|---|---|---|
| |||
* | 0 | 0 | 0 |
.error | 0 | 1 | 0 |
*.error | 0 | 1 | 0 |
div | 0 | 0 | 1 |
div * | 0 | 0 | 1 |
Podríamos terminar aquí, si no fuese porque hace poco leí un libro de Eric Meyer donde se planteaba la siguiente pregunta: ¿tiene el mismo valor una especifidad de cero, que ningún valor de especificidad?3 La pregunta puede parecer propia de una discusión meramente académica, pero tiene algunos efectos prácticos que hay que conocer a la hora de depurar hojas de estilo.
Como se sabe, las propiedades heredadas por un elemento no tienen valor alguno en el cómputo de la especificidad de una regla que se le aplique. Así, si tenemos:
<p>Un párrafo con un elemento <em>enfatizado</em>.</p>
y una regla:
p{color:#000;}
el color negro del texto que hereda em
como tal no tiene especificidad alguna. ¿Pero que ocurre si nuestra CSS es como la que sigue?:
*{color:#5072BF;}
p{color:#000;}
A primera vista podría parece que el texto enfatizado debería ser de color negro, puesto que es el que heredaría de la declaración aplicada al párrafo que lo contiene. Sin embargo:
¿A qué de debe? A que em
también es un objetivo del valor de color del selector universal —que es 0,0,0
—, que es suficiente para superar el valor de la «negritud» heredada de p
—que es ninguno—. En conclusión, 0 > ∅.