✅El selector :has() en CSS

Hoy quiero hablarte del selector :has() en CSS, una herramienta increíble que me permite seleccionar elementos según su contenido o los elementos hijos que posean. Esto abre posibilidades fascinantes para escribir reglas CSS más flexibles y efectivas. Veamos cómo funciona paso a paso, incluyendo múltiples ejemplos y variantes como :has() y :not(:has()).

¿Qué es el selector :has() en CSS?

El selector :has() permite seleccionar elementos en función de lo que contienen. Es decir, puedo elegir un elemento porque contiene (o no) a otro dentro de él.

La sintaxis básica es la siguiente:

selector:has(selector-interno) {
    /* propiedades */
}
Lenguaje del código: CSS (css)

Ejemplos prácticos del selector :has()

1. Seleccionar un div que contenga un título h2

div:has(h2) {
    background-color: lightblue;
}
Lenguaje del código: CSS (css)

Aquí, cualquier <div> que tenga un <h2> dentro cambiará su fondo a azul claro.

2. Seleccionar elementos que NO tengan cierta clase usando :not(:has())

Esto es genial para excluir elementos que contengan algo específico:

section:not(:has(.advertencia)) {
    border: 2px solid green;
}
Lenguaje del código: CSS (css)

En este caso, selecciono todas las secciones (<section>) que NO contienen ningún elemento con la clase .advertencia.

3. Combinar múltiples selectores dentro de :has()

Una ventaja maravillosa es que puedo incluir varios selectores dentro del mismo :has() separados por comas:

ul:has(li.destacado, li.importante) {
    padding: 20px;
    background-color: #fffae6;
}
Lenguaje del código: CSS (css)

Con esto, cualquier lista (<ul>) que contenga al menos un elemento con clase .destacado o .importante tendrá relleno adicional y fondo claro.

4. Seleccionar un elemento en función de atributos internos

Imagina que quiero destacar un formulario solo si tiene un campo obligatorio:

form:has(input[required]) {
    box-shadow: 0 0 10px red;
}
Lenguaje del código: CSS (css)

Aquí selecciono formularios que incluyen al menos un campo input que es obligatorio (atributo required).

¿Qué puedo poner dentro de los paréntesis del selector :has()?

Dentro de :has(), puedo colocar prácticamente cualquier selector válido de CSS:

  • Selectores por clase: .clase
  • Selectores por id: #identificador
  • Selectores de atributos: [atributo="valor"]
  • Selectores combinados con comas: .clase1, .clase2
  • Selectores compuestos: div > p

Ejemplo complejo combinando selectores

article:has(header h1, header h2, .intro) {
    margin-bottom: 40px;
}
Lenguaje del código: CSS (css)

En este ejemplo, cualquier <article> que contenga un <header> con <h1> o <h2> o un elemento con la clase .intro tendrá más margen inferior.

Ejemplo con negación avanzada

También puedo usarlo combinado con :not() para selecciones más avanzadas:

nav:has(ul:not(:has(li.activo))) {
    opacity: 0.5;
}
Lenguaje del código: CSS (css)

Aquí reduzco la opacidad de un menú (<nav>) si contiene una lista (<ul>) que NO tiene ningún elemento activo (li.activo).

Uso avanzado del selector :has() con selectores relacionales (>, <, +, ~)

Una de las grandes fortalezas del selector :has() es que puedo combinarlo con otros selectores avanzados de CSS que expresan relaciones específicas entre elementos. Esto me da una precisión increíble al definir mis reglas CSS. Veamos cómo funciona cada uno:

1. Seleccionar un elemento que tenga un hijo directo (>)

Usando el símbolo > dentro de :has(), selecciono únicamente elementos que contengan directamente a otro elemento específico como hijo inmediato.

Ejemplo:

div:has(> h2) {
    background-color: yellow;
}Lenguaje del código: CSS (css)

Aquí, solo aquellos <div> que tengan un <h2> directamente dentro (sin elementos intermedios) tendrán el fondo amarillo.

2. Seleccionar elementos que tengan un hijo que a su vez tenga otro elemento hijo ( espacio)

Si no uso > y simplemente pongo un espacio, selecciono elementos que contienen otro elemento en cualquier nivel (no necesariamente hijo directo).

Ejemplo:

div:has(section h2) {
    border: 2px solid red;
}Lenguaje del código: CSS (css)

Este ejemplo afectará solo a los <div> que contengan un <section> que a su vez tenga un <h2> dentro.

3. Seleccionar elementos que contengan un elemento seguido inmediatamente por otro (+)

El selector + me permite seleccionar elementos basados en hermanos inmediatos dentro del selector :has().

Ejemplo:

div:has(h2 + p) {
    padding: 15px;
}Lenguaje del código: CSS (css)

Aquí selecciono únicamente aquellos <div> que tengan un título <h2> seguido directamente por un párrafo <p>.

4. Seleccionar elementos que contengan un elemento seguido por otro elemento en cualquier posición posterior (~)

El selector ~ me permite elegir elementos que contengan un hermano general posterior a otro.

Ejemplo:

div:has(h2 ~ p) {
    box-shadow: 0 0 10px blue;
}Lenguaje del código: CSS (css)

Esto aplicará una sombra azul solo a los <div> que tengan al menos un párrafo <p> en cualquier posición después de un título <h2>.

5. Seleccionar elementos padres desde el hijo directo con < (solo válido dentro de :has())

Aunque el selector < no se utiliza directamente fuera de :has(), su lógica inversa está implícita en el propio selector. Es decir, al usar > dentro de :has(), automáticamente selecciono el elemento padre que cumple la condición. Este concepto se denomina «selección hacia arriba».

Por ejemplo:

section:has(> header) {
    background-color: #f9f9f9;
}Lenguaje del código: CSS (css)

Aquí selecciono una <section> únicamente si contiene un <header> directamente como hijo.

Ejemplo combinado muy claro

Veamos un ejemplo claro que combina varios de estos selectores juntos:

article:has(header > h2 + p, section ~ footer) {
    border-left: 4px solid green;
}Lenguaje del código: CSS (css)

En este ejemplo, selecciono únicamente los elementos <article> que cumplan al menos una de estas condiciones:

  • Que tengan un <header> que contenga directamente (>) un <h2> seguido inmediatamente (+) por un <p>.
  • O que tengan un <section> y posteriormente (~) un <footer> como hermanos generales dentro del <article>.

Combinar el selector :has() con estos selectores relacionales (>, +, ~) incrementa significativamente el poder expresivo del CSS. Esto me permite definir reglas muy precisas basadas en estructuras HTML complejas, sin necesidad de recurrir a JavaScript adicional.

Conclusión

El selector :has() me facilita muchísimo la vida al crear diseños dinámicos y adaptativos. Gracias a él, puedo evitar el uso excesivo de JavaScript para tareas básicas de estilo, haciendo mi código más limpio y eficiente. Espero que estos ejemplos te ayuden a aprovechar al máximo el poder del selector :has() en CSS.

Nos vemos en el siguiente tutorial. Un saludo.

5/5 - (3 votos)
Compartir Artículo

Un comentario

  1. Fran Salas Master WP 1 año atrás

    ¿Conocías el selector :has()?

¿Tienes una duda o sugerencia sobre este artículo?

Puedes dejar un comentario de forma anónima, sin necesidad de registrarte ni proporcionar ningún dato personal. Tu mensaje se publicará como “Usuario Anónimo No Premium”, un perfil que agrupa a todos los visitantes no registrados.

Importante: para evitar el spam automático, todos los comentarios anónimos deben comenzar con el símbolo #. Ejemplo: # Tengo una duda sobre los shortcodes personalizados...

Este espacio está destinado exclusivamente a preguntas concretas, dudas o sugerencias relacionadas con el contenido del artículo. Por respeto a los demás lectores, no se publicarán temas sin relación.

Privacidad y normas:
  • No tienes que agregar tu nombre, correo ni ningún otro dato identificativo.
  • No se permiten enlaces en comentarios anónimos.
  • No se permiten símbolos especiales como < o > (menor y mayor que), ya que se consideran intentos de insertar código y serán bloqueados.
  • Todos los comentarios anónimos pasan por revisión antes de ser publicados y solo se aprobarán si aportan valor al contenido del artículo.
¿Quieres comentar con tu nombre y que tu mensaje se publique al instante? Inicia sesión o regístrate.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

0%