Seleccionar un elemento dependiendo de lo que contiene ha sido durante años una de las limitaciones más conocidas de CSS, pero actualmente puedes hacerlo fácilmente utilizando la pseudoclase :has().
Gracias a este selector puedes aplicar estilos a un elemento padre basándote en sus hijos, algo que anteriormente solía requerir JavaScript o modificaciones adicionales en el HTML.
En este tutorial vas a aprender cómo seleccionar un elemento que contiene otro elemento con CSS, cómo funciona :has(), qué combinaciones puedes realizar y en qué situaciones puede resultarte especialmente útil.
¿Qué significa seleccionar un elemento que contiene otro?
Normalmente, los selectores CSS trabajan recorriendo el documento desde un elemento superior hacia sus descendientes.
Por ejemplo, puedes seleccionar fácilmente todos los párrafos que estén dentro de un elemento con la clase .contenido.
.contenido p {
color: #333;
}
En este caso CSS busca primero .contenido y después selecciona los elementos <p> que se encuentran dentro.
El problema aparece cuando quieres realizar justamente el recorrido contrario y seleccionar el elemento padre dependiendo de lo que exista dentro de él.
Imagina una estructura HTML como esta.
<div class="tarjeta">
<img src="imagen.jpg" alt="Ejemplo">
<p>Contenido de la tarjeta</p>
</div>
Puede que quieras modificar .tarjeta únicamente cuando contenga una imagen, algo que puedes conseguir actualmente mediante la pseudoclase :has().
Cómo funciona el selector :has() de CSS
La pseudoclase :has() permite seleccionar un elemento cuando dentro de él existe otro elemento que cumple determinadas condiciones.
La sintaxis básica es la siguiente.
padre:has(hijo) {
propiedad: valor;
}
Por ejemplo, para seleccionar cualquier .tarjeta que contenga una imagen puedes utilizar este código.
.tarjeta:has(img) {
border: 2px solid;
}
CSS comprobará cada elemento con la clase .tarjeta y aplicará el estilo únicamente a aquellos que contengan al menos un <img>.
Este comportamiento hace que :has() sea conocido habitualmente como un selector de padres, aunque técnicamente sus posibilidades son bastante más amplias.
Puedes consultar también la documentación de la pseudoclase :has() en MDN Web Docs si quieres profundizar en su funcionamiento.
Cómo seleccionar un padre que contiene una clase concreta
Una de las situaciones más habituales consiste en seleccionar un elemento cuando contiene otro elemento con una clase específica.
Supongamos que tienes este código HTML.
<div class="producto">
<span class="oferta">Oferta</span>
<h3>Producto de ejemplo</h3>
</div>
Puedes seleccionar solamente los productos que contengan .oferta utilizando lo siguiente.
.producto:has(.oferta) {
border: 2px solid;
padding: 20px;
}
De esta manera puedes cambiar completamente el aspecto del producto dependiendo de la presencia de determinados elementos internos.
Este sistema resulta especialmente interesante en webs dinámicas, tiendas online, constructores visuales y páginas creadas con WordPress.
Cómo seleccionar un DIV que contiene otro elemento
También puedes aplicar :has() directamente sobre etiquetas HTML sin necesidad de utilizar clases.
Si quieres seleccionar todos los <div> que contienen un formulario podrías escribir lo siguiente.
div:has(form) {
padding: 30px;
}
Todos los div que tengan algún elemento <form> dentro recibirán ese estilo.
Sin embargo, utilizar selectores excesivamente generales puede afectar a muchos elementos inesperados, por lo que normalmente es recomendable añadir clases que delimiten mejor la selección.
Por ejemplo, puedes hacer el selector más preciso de esta forma.
.contenedor:has(form) {
padding: 30px;
}
Cómo seleccionar un elemento que contiene un hijo directo
En algunos casos no quieres detectar cualquier descendiente, sino solamente un hijo directo del elemento.
Puedes combinar :has() con el combinador > de CSS.
.tarjeta:has(> img) {
border: 1px solid;
}
Este selector solamente afectará a .tarjeta cuando <img> sea hijo directo del elemento.
Por ejemplo, funcionaría con esta estructura.
<div class="tarjeta">
<img src="foto.jpg" alt="">
</div>
Pero no coincidiría si la imagen estuviera dentro de otro contenedor intermedio.
<div class="tarjeta">
<div class="imagen">
<img src="foto.jpg" alt="">
</div>
</div>
Esta diferencia puede resultar fundamental cuando trabajas con estructuras HTML complejas.
Cómo seleccionar un elemento dependiendo de varios elementos internos
La pseudoclase :has() puede combinarse con otros selectores para crear condiciones bastante específicas.
Por ejemplo, puedes seleccionar una tarjeta que contenga una imagen y además una etiqueta con determinada clase.
.tarjeta:has(img):has(.destacado) {
border: 3px solid;
}
El estilo solamente se aplicará cuando .tarjeta contenga una imagen y un elemento .destacado.
También puedes indicar diferentes alternativas dentro de un mismo :has().
.tarjeta:has(img, video) {
padding: 20px;
}
En este caso se seleccionará la tarjeta cuando contenga una imagen o un vídeo.
Cómo seleccionar un elemento si contiene un enlace
Otro ejemplo muy práctico consiste en detectar contenedores que tengan enlaces.
Puedes utilizar este selector.
.bloque:has(a) {
cursor: pointer;
}
También puedes seleccionar únicamente aquellos bloques que contienen enlaces con una clase concreta.
.bloque:has(a.boton) {
padding-bottom: 30px;
}
Esta técnica puede ayudarte especialmente cuando quieres modificar el espaciado, diseño o estructura visual dependiendo del contenido generado automáticamente.
Cómo seleccionar un elemento cuando contiene un campo de formulario
Los formularios son otro escenario en el que :has() puede ahorrar bastante código.
Por ejemplo, puedes detectar un contenedor que tenga un campo obligatorio.
.campo:has(input[required]) {
font-weight: bold;
}
También puedes cambiar el aspecto de un contenedor dependiendo del estado del campo.
.campo:has(input:focus) {
outline: 2px solid;
}
Cuando el usuario coloque el cursor dentro del input, será el contenedor completo el que recibirá el estilo.
Esto permite crear formularios mucho más interactivos utilizando únicamente CSS.
Cómo seleccionar un formulario cuando tiene un campo marcado
Puedes llevar esta técnica todavía más lejos utilizando estados como :checked.
Por ejemplo, puedes seleccionar un formulario completo cuando un checkbox esté activado.
form:has(input[type="checkbox"]:checked) {
background: #f5f5f5;
}
También puedes utilizar esta lógica para mostrar, ocultar o transformar determinadas partes de una interfaz sin necesidad de escribir JavaScript adicional.
Cómo seleccionar un elemento anterior con CSS
Una de las posibilidades más interesantes de :has() es que también permite trabajar indirectamente con elementos hermanos posteriores.
Imagina esta estructura.
<h2>Título</h2>
<p>Texto del apartado</p>
Puedes seleccionar el <h2> solamente cuando tenga inmediatamente después un párrafo.
h2:has(+ p) {
margin-bottom: 10px;
}
El combinador + indica que el párrafo debe encontrarse inmediatamente después del encabezado.
También puedes utilizar ~ para detectar hermanos posteriores aunque no sean consecutivos.
h2:has(~ p) {
margin-bottom: 20px;
}
Este tipo de combinación proporciona posibilidades que hace algunos años resultaban prácticamente imposibles de resolver utilizando exclusivamente CSS.
Ejemplo práctico paso a paso
Supongamos que tienes varias tarjetas de productos y quieres destacar solamente aquellas que contienen una etiqueta indicando que están en oferta.
Puedes hacerlo siguiendo estos pasos.
- 🔎 Localiza el elemento padre que quieres modificar, por ejemplo
.producto. - 🎯 Identifica el elemento interno que determinará si se aplica el estilo, por ejemplo
.oferta. - 🧩 Añade
:has()al elemento padre colocando dentro el selector que quieres detectar.
.producto:has(.oferta) {
border: 2px solid;
}
- 🎨 Añade las propiedades CSS que quieras aplicar exclusivamente a esos elementos.
.producto:has(.oferta) {
border: 2px solid;
padding: 20px;
border-radius: 8px;
}
- ✅ Comprueba que las tarjetas sin
.ofertamantienen su apariencia original y que solamente se modifican las que contienen ese elemento.
Tabla resumen de selectores con :has()
| Objetivo | Selector CSS | Resultado |
|---|---|---|
| Contiene una imagen | .caja:has(img) | Selecciona .caja si contiene un <img> |
| Contiene una clase | .caja:has(.destacado) | Selecciona el padre si existe .destacado |
| Tiene un hijo directo | .caja:has(> img) | Detecta solamente imágenes hijas directas |
| Contiene imagen o vídeo | .caja:has(img, video) | Detecta cualquiera de los dos elementos |
| Contiene un input obligatorio | .campo:has(input[required]) | Selecciona campos obligatorios |
| Contiene un input enfocado | .campo:has(input:focus) | Cambia el padre mientras el input tiene foco |
| Contiene un checkbox marcado | .campo:has(input:checked) | Detecta campos seleccionados |
| Tiene un párrafo inmediatamente después | h2:has(+ p) | Selecciona el encabezado anterior |
¿Es compatible :has() con todos los navegadores?
La pseudoclase :has() cuenta actualmente con compatibilidad en los principales navegadores modernos, por lo que puedes utilizarla con bastante tranquilidad en la mayoría de proyectos actuales.
Aun así, cuando desarrolles una web destinada a usuarios con navegadores antiguos conviene comprobar la compatibilidad concreta mediante herramientas como Can I Use.
También puedes utilizar @supports para aplicar determinados estilos únicamente cuando el navegador reconozca este selector.
@supports selector(.caja:has(img)) {
.caja:has(img) {
border: 2px solid;
}
}
De esta manera puedes crear una alternativa segura para navegadores que no sean compatibles.
Errores frecuentes al utilizar :has()
Uno de los errores más habituales consiste en crear selectores demasiado amplios como div:has(img), ya que podrías terminar modificando muchos contenedores diferentes de tu página.
Siempre que puedas, utiliza clases específicas como .tarjeta:has(img) para limitar claramente el alcance de tus reglas CSS.
Otro error frecuente consiste en confundir un descendiente con un hijo directo, porque .caja:has(img) y .caja:has(> img) pueden producir resultados completamente diferentes dependiendo de tu HTML.
También debes evitar crear selectores innecesariamente complejos, ya que una estructura sencilla será más fácil de mantener y modificar posteriormente.
Ventajas de seleccionar elementos mediante :has()
La principal ventaja es que puedes crear diseños que reaccionan automáticamente al contenido real de cada elemento.
También puedes reducir el uso de clases auxiliares que anteriormente tenías que añadir manualmente al HTML solamente para aplicar estilos diferentes.
En muchos casos incluso puedes evitar pequeños fragmentos de JavaScript destinados exclusivamente a comprobar si un elemento contiene determinados descendientes.
Además, :has() se combina con selectores como :checked, :focus, :invalid, atributos, clases y combinadores, convirtiéndose en una de las herramientas más potentes del CSS moderno.
Conclusión
Si necesitas seleccionar un elemento que contiene otro elemento con CSS, la pseudoclase :has() es actualmente la solución más directa y flexible.
Con un selector tan sencillo como .padre:has(.hijo) puedes aplicar estilos al padre dependiendo de los elementos que tenga dentro.
También puedes combinar :has() con hijos directos, hermanos, clases, atributos y estados de formularios para crear reglas mucho más avanzadas.
Dominar este selector te permitirá escribir CSS más limpio, reducir dependencias de JavaScript y crear interfaces capaces de adaptarse automáticamente a su contenido.
