Combinadores y selectores de atributos

Octubre 2020: Puede ver una recopilación de los ejemplos interactivos de estos temas incluyendo los nuevos de CSS Selectores nivel 4 en

En el tema Selectores CSS nivel4 encontrará una recopilación de todos los selectores incluyendo los nuevos de CSS nivel 4.

Combinadores de CSS3 (selección por contexto)

Los combinadores permiten aplicar una selección por contexto entre varios selectores simples. En la selección por contexto en CSS2-1 teníamos los siguientes:

  • Combinador descendente E F: Selecciona un elemento F que desciende de E. Los descendientes de E son todos sus elementos hijos, nietos, etc. El espacio resaltado en amarillo es el elemento combinador.
  • Combinador hijo E>F: Selecciona un elemento F que es hijo de E, pero no los siguientes descendientes nietos, etc.. El caracter > es el signo combinador.
  • Combinador hermanos adyacentes E+F: Dados dos elementos E y F que comparten el mismo padre selecciona F cuando es precedido inmediatamente por E. El caracter + es el signo combinador.

Ahora con los combinadores CCS3 se incorpora

  • Combinador hermanos general E~F: Siendo E y F hermanos, se selecciona F cuando es precedido no necesariamente de forma inmediata por E. El caracter combinador es ~.

En estos ejemplos reproducimos cada caso dentro de un elemento <div id="contenX"> con X entre 1 y 4. Dentro disponemos elementos con color y borde azul. Los seleccionados por cada regla aparecen en color rojo.

Ejemplo:

Combinador descendente: div#conten1 div { ... }. Selecciona todos los descendientes de div#conten1 que sean elementos <div> en cualquier nivel donde se encuentren. Aunque no selecciona el descendiente <blockquote>.
DIV id="conten1"
DIV1
DIV2
BLOCKQUOTE
DIV3
Combinador hijo: div#conten2 > div { ... }. Selecciona todos hijos de div#conten2 que sea un <div> pero no otros descendientes en niveles inferiores. Seleccionará DIV1 y DIV5.
DIV id="conten2"
DIV1
DIV2
DIV3
DIV4
DIV5
DIV6
Combinador hermano adyacente: div#conten3 div + p { ... }. Selecciona P1 porque le precede de forma inmediata su hermano DIV3. Mientras que no selecciona P2 porque el hermano que le precede inmediatamante BLOCKQUOTE no es un <div>.
DIV id="conten3"
DIV1
DIV2
BLOCKQUOTE
DIV3

P1

P2

DIV4
Combinador hermano general: div#conten4 div ~ p { ... }. Ahora selecciona P1 porque le precede de forma inmediata su hermano DIV3. También selecciona P2 porque su hermano DIV2 le precede aunque no de forma inmediata.
DIV id="conten4"
DIV1
DIV2
BLOCKQUOTE
DIV3

P1

P2

DIV4

El código del ejemplo anterior es el siguiente:

Selectores de atributos en CSS3

La selección por atributo permite seleccionar un elemento atendiendo a algún atributo. Estos ya fueron establecidos en los selección por atributo de CSS2-1:

  • E[att] selecciona el elemento E que tenga un atributo att.
  • E[att=val] selecciona el elemento E que tenga un atributo att con valor val.
  • E[att~=val] selecciona E si su atributo att es una lista de palabras separadas por espacios y una de esas palabras es val.
  • E[att|=val] selecciona E si su atributo att es exactamente val o es una lista de palabras separadas por guiones y donde la primera de esa lista de palabras es val.

Los nuevos selectores de atributo CSS3 son:

  • E[att^=val] selecciona el elemento E cuyo atributo att empieza con val.
  • E[att$=val] selecciona el elemento E cuyo atributo att finaliza con val.
  • E[att*=val] selecciona el elemento E cuyo atributo att contiene al menos una vez val.

Este ejemplo interactúa con la hoja de estilo ubicada en el elemento <style> de la cabecera de esta página para agregar y eliminar reglas. Poco hay que explicar, pues cada uno de los selectores realiza la selección sobre los elementos <li> en color azul, resaltando en amarillo la selección, a la vez que se expone una explicación de lo que está pasando. El elemento E se refiere a ese elemento li en el ejemplo. Mientras que el atributo att se refiere al atributo data-att en el ejemplo con objeto de usar un nombre de atributo válido en HTML. El valor val es el mismo en ambos casos.

Ejemplo:

  • Un elemento <li> sin atributos NO es seleccionado.
  • Un elemento <li data-att="Esto es un atributo">.
  • Un elemento <li data-att="val">.
  • Un elemento <li data-att="val lista palabras">.
  • Un elemento <li data-att="lista val palabras">.
  • Un elemento <li data-att="lista palabras val">.
  • Un elemento <li data-att="val lista palabras val">.
  • Un elemento <li data-att="val-lista-palabras">.
  • Un elemento <li data-att="Esto es un valor">.
De CSS2.1
De CSS3
Combinados y con selector :not()

Acción que se lleva a cabo y estilo insertado:
 

Código completo de este ejemplo

La selección por atributo cuando éste es class será E[class=val], equivalente a realizar una selección por clase E.val. Como veremos en el siguiente apartado, tiene unas ventajas adicionales en clases HTML múltiples con listas de palabras separadas por espacio. Por otro lado la nueva pseudo-clase :not() nos permite una mayor capacidad de selección como se observa en el ejemplo:

  • E[att^=val][att$=val]. Al poner dos selectores de atributo seguidos estamos diciendo que se deben cumplir los dos. En este caso selecciona los elementos que empiecen y al mismo tiempo acaben con "val". Selecciona data-att="val" y data-att="val lista palabras val" que son los únicos que cumplen esto.
  • [data-att^=val][data-att$=val]:not([data-att=val]). Ahora además de lo anterior imponemos que no sea exactamente "val", por lo que seleccionará sólo el segundo que empieza y finaliza con "val".

Los navegadores actuales (diciembre 2012) soportan la selección por atributo de CSS2.1 y CSS3 (incluso IE8 aunque no soporta la pseudo-clase :not()).

Selección por clase con lista de clases

La selección por atributo [att~=val] usando una lista de palabras es algo que podemos aprovechar para configurar estilos múltiples para un elemento. En este ejemplo dotamos de múltiple estilo simplemente poniendo una cadena de clases separadas por espacio en el atributo class de un elemento:

Ejemplo:

Una palabra en negrita, otra en itálica y otra con fondo amarillo. Una negrita, itálica y fondo amarillo.

Hay muchas cosas que existen en CSS y que no usamos por desconocimiento. Esta utilidad de usar clases HTML múltiples lleva ya mucho tiempo existiendo pues era posible con la selección por atributo [att~=val] de CSS2.1. Yo mismo reconozco que no he sabido apreciarlo. Por ejemplo, en el archivo formatos.css tengo unos estilos básicos para algunos colores elementales de uso frecuente:

<style>
    /* Esto realmente estaba en el archivo formatos.css */
    .verde {
        color: green
    }
</style>
<span class="verde">letra color verde</span>

Ese estilo nos permite dar color sin tener que ponerlo en línea en un atributo style. Pero si además quería letra verde y negrita (bold) y tuviese una regla para la clase bold no podría poner los dos atributos class="verde" class="bold" en un elemento, pues sólo se reconocería el primero de ellos. La solución más fácil es agregarlo en línea:

<span class="verde" style="font-weight: bold">letra color verde bold</span>

El estilo en línea es el que menos trabajo nos ocupa. Y es algo a lo que tendemos cuando empezamos con HTML+CSS debido a que nos supone un esfuerzo buscar la hoja de estilo y hacer los cambios ahí pensando siempre en incorporar un recurso que podemos necesitar en otras páginas. Siempre que este enfoque sea cierto hemos de esforzarnos en agregarlo a la hoja de estilo y evitar el estilo en línea. ¿Algo como lo que sigue?:

<style>
    /* Esto es como modificaría el archivo formatos.css */
    .verde {
        color: green
    }
    .verde-bold {
        color: red;
        font-weight: bold;
    }
</style>
<span class="verde-bold">letra color verde bold</span>

Ahora tenemos las clases verde y verde-bold. Pero supongamos que no quiero engrosar en exceso el archivo formatos.css, entonces podría poner el estilo nuevo en un elemento <style> de la cabecera de esta página, pues consideraría que sólo afecta a la página actual. Podría poner la regla de la clase verde-bold ahí, pero lo he venido haciendo mediante el uso de un atributo data-bold en combinación con una selección por atributo sin valor de la forma [att]:

<style>
    /* Esto es como estaba el archivo formatos.css */
    .verde {
        color: green
    }
    /* Esto lo pondría en un elemento style del head de la
       página donde lo estuviera usando */
    [data-bold] {
        font-weight: bold;
    }
</style>
<span class="verde" data-bold>letra bold color verde</span> 

¿Y si mañana necesitamos verde, bold e itálica?. No parece nada practico tener que agregar un nuevo atributo data-italic por ejemplo. Es obvio que usar la selección por atributo de lista de palabras es la más eficiente:

<style>
    /* Así es como está ahora el estilo en el archivo formatos.css */
    [class~=verde] {
        color: green
    }
    [class~=bold] {
        font-weight: bold;
    }
    [class~=italic] {
        font-style: italic;
    }
</style>
<span class="verde bold italic">letra bold italic color verde</span>

Al cambiar ahora estas reglas de estilo del archivo formatos.css no he tenido que hacer nada en los elementos de los documentos HTML que usaban esos estilos. Pues si tenían y tienen class="verde" actuando antes con la regla .verde{...}, ahora la nueva regla [class~=verde]{...} realiza la misma selección para esos elementos y para los nuevos con lista de palabras que contengan "verde" como una de ellas.