Cómo usar el nuevo selector has() en CSS

Cómo usar el nuevo selector has() en CSS

Uno de los selectores más esperados en CSS 4 ahora es compatible con la mayoría de los navegadores modernos: :has(). Hasta la redacción de este post, en Chrome, Edge y Safari ya se pueden usar, Firefox simplemente activando el parámetro layout.css.has-selector.enabled.

¿Qué es el selector :has()?


:has() te permite aplicar estilo de acuerdo con una regla: es como tener un IF al seleccionar un elemento, ya que solo agregará un cierto estilo si cumple con una determinada condición. Vamos por partes =).


¿Por qué necesito ese :has()?

Muchas veces cuando necesitamos agregar un estilo a un elemento específico según una condición, necesitamos recurrir a JavaScript para simplemente incluir una clase CSS en un div determinado, por ejemplo, porque tu contenido es dinámico y solo aparecerá en una determinada condición.

Entonces haríamos algo así, dado el HTML:

Con este resultado:

En este caso, el problema sería agregar un borde negro al div que contiene un H1 adentro, para tener el siguiente resultado:


¿Cómo hacemos?


No quiero poner un borde en H1, sino en la primera div. Suponiendo que no podemos simplemente agregar una clase a cada div (ya que mi aplicación los genera automáticamente), necesitaríamos un código JavaScript que buscaría todos los divs que tienen un H1 dentro y luego agregaríamos nuestra clase CSS con el estilo (o un estilo in-line con style).

Este es el punto principal de :has(), porque con él podemos crear el siguiente código:

El resultado en el browser sería:

Ahora que entendemos la premisa, vayamos a la parte divertida, ya que :has() es muy poderoso y se puede combinar de muchas maneras.

Explorando el selector :has()


Como :has() es una pseudoclase (tal como :not, :before, :hover o :host), puede combinarse con otras y crear así combinaciones que te van a ayudar mucho. Se parece mucho al has() de jQuery para quienes ya están habituados.

En la sintaxis más básica, puedes encontrar tag names, ids y classes, por ejemplo:

Como dije, puede combinarse con otros :has(), como:

Así como seleccionas varios ítems en una lista de CSS separándolos por comas, también puedes hacer algo similar con :has():

💡
Nota: Si se inserta un elemento inválido en la lista de parámetros de :has(), ese ítem simplemente será ignorado y CSS va a continuar funcionando con los demás, pues :has sencillamente va a considerar que no lo encontró. Por ejemplo, abajo verás los selectores alvaro y ::beornottobe.

Un caso de uso simple para ejemplificar: quiero poner un borde rojo en todos los enlaces que tienen una imagen dentro:

Entonces selecciono todas las tags de la aplicación que posean una clase hija específica:

En este ejemplo anterior, quien tenga un hijo con la class child recibirá un estilo.

Combinando con :not()


Para quien no conoce :not(), es un selector ampliamente soportado por todos los browsers, con el que puedes hacer negaciones a la hora de seleccionar.  

Entonces, en lugar de intentar seleccionar siempre una etiqueta en función de una condición VERDADERA, podemos negarla, seleccionando todas las etiquetas contrarias. Algo abstracto, ¿eh? Vamos a los ejemplos.

Primero, usando como ejemplo ese HTML de arriba en el que tenemos dos bloques div, en el que el primero tiene un H1 y el segundo tiene una lista (ul/li):

Como ya explicamos, si queremos diseñar h1 usando :has, solo necesitamos lo siguiente:

Sin embargo, si queremos seleccionar todos los div que no tienen un H1, solo necesitamos negar nuestra condición con :not, así:

Esta característica puede ser extremadamente útil cuando tratas de diseñar varios elementos en una lista, por ejemplo, pero deseas que el primer o el último elemento tengan un estilo diferente, dado el siguiente HTML (aumenté el HTML anterior e incluí otra lista al final, que no está dentro de un div):

Si queremos:

● Añadir un margen debajo de cada ítem,
● No seleccionar el último ítem o
● Solo incluir el estilo en listas que estén dentro de una div,

Podemos proceder de esta manera:

Explicado en detall quedaría de la siguiente forma:

div:has(ul) va a seleccionar divs con una lista adentro.
● Entonces va a buscar todas las tags li dentro de esa div (podría ser más específico y utilizar > para buscar las tags hijas, pero no conviene complicar más el ejemplo).
:not(:last-of-type) va a seleccionar todas las li y excluir la última li encontrada que, en ese caso, va a ser el último ítem de la lista donde no queremos margen.

Compatibilidad


Puedes probar directamente en CSS si :has está disponible, para luego aplicar un determinado estilo a través de @supports de CSS. De esa forma, puedes aplicar códigos diferentes si fuera el caso, por ejemplo:

También puedes utilizar la API de Supports de JavaScript para comprobar si su navegador la admite o no:

Estatus actual de compatibilidad según Can I Use:

Para verificar el estatus atual entre los browsers, ingresa en: https://caniuse.com/?search=%3Ahas

Conclusión


En este artículo, vimos cómo el nuevo selector :has de CSS 4 se puede usar y combinar con otros :has. En un escenario real, muchas veces cuando necesitamos agregar un estilo a un elemento basado en una condición, recurrimos a JavaScript, solo para agregar o quitar una clase CSS. Con el nuevo pseudoelemento se puede quitar mucho código (o ni siquiera crearlo), haciendo la vida más fácil para los programadores.

También vimos cómo combinar el uso de :has con :not puede ser muy útil al no seleccionar elementos no deseados. Por mucho que mostremos :not, CSS es libre para que crees combinaciones, así que también puedes usar :is, :where, :after o :before si es necesario. :not nos da una mejor idea de lo que podemos hacer siendo la negación exacta de :has.

Con respecto a la compatibilidad, debes prestar atención a si tu aplicación o sitio web es compatible con Samsung Internet o Firefox, los cuales aún no están listos para usarlo, por lo que es posible que debas emplear @supports por un tiempo hasta que el código se pueda eliminar (cuando veas que en tus análisis las personas ya no usan la versión anterior de estos navegadores).

¡Hasta la próxima!

Álvaro Junqueira: https://github.com/alvarocjunq

💡
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Listopro.

Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.