Las nuevas pseudoclases de CSS :is(), :where() y :has()

pseudoclases :is(), :where() y :has()

Las nuevas pseudoclases de CSS :is(), :where() y :has()

Estas «nuevas» pseudoclases ya llevan una temporada disponibles, pero según los datos de caniuse, empieza a ser sensato usarlas sin temor a que no funcionen correctamente en muchos navegadores. De las tres, tan solo la pseudoclase :has() necesita aún un poco de tiempo para estar bien soportada por el grueso de los navegadores de los usuarios.

La pseudoclase :is() ya la vimos en un anterior artículo, así que no vamos a profundizar en ella. Por lo que en este artículo nos vamos a centrar en sus dos hermanas, :where() y :has(). Ahora bien, como la pseudoclase :where() es muy similar a :is() es importante antes de continuar que leas dicho artículo si todavía no conoces bien como funciona :is().

La pseudoclase :where()

La pseudoclase :where() básicamente es idéntica a la pseudoclase :is(), la diferencia está en lo especifica que se considera.

Veamos el siguiente ejemplo:

p { color: red; }
p { color: blue; }

Ante instrucciones contradictorias, en CSS «manda lo último» por lo que en el ejemplo anterior los párrafos aparecerán de color azul. Ahora bien, si usamos diferentes selectores…

#rojo { color: red; }
.ejemplo { color: blue; }
p { color: green; }

Si tenemos un párrafo con la clase «ejemplo» y el atributo id «rojo», ese párrafo será de color rojo, ya que al usar diferentes selectores, manda el más especifico. El selector «p» es muy genérico (todos los párrafos). El selector «.ejemplo» es más especifico (todos los elementos de la clase «ejemplo»). Finalmente el selector «#rojo» es muy especifico (el elemento con el id=»rojo»).

En el caso que nos ocupa, :is() tiene el nivel de especificidad indicado por el más especifico de sus argumentos. Por lo que:

:is(h1, h2, h3, h4, h5, h6)

Es poco especifico, ya que sus argumentos son selectores de etiqueta que son muy genéricos. Pero si hacemos esto:

:is(h1, h2, h3, h4, h5, h6, #rojo)

Ahora este selector es MUY especifico, ya que entre sus argumentos tenemos un selector de id (que es muy especifico).

Puedes probarlo con el siguiente ejemplo:

.azul { color: blue; }
:is(h1, h2, h3, h4, h5, h6, #rojo) { color: red; }

Usando un titulo <h1> con la clase «azul», y verás que si quitas el selector #rojo de los argumentos de :is(), manda la clase, pero si lo dejas puesto, manda el selector :is() y el titulo <h1> aparece de color rojo.

Pues bien, si usamos el selector :where() en lugar de :is(), esto no sucede, ya que la especificidad de :where() es zero. 

.azul { color: blue; }
:where(h1, h2, h3, h4, h5, h6, #rojo) { color: red; }

En esta ocasión, el titulo <h1> de la clase azul aparecerá de color azul y no de color rojo, ya que :where() es menos especifico que el selector de clase, independientemente de lo específicos que sean sus argumentos.

Y ya está. Por lo demás :where() funciona del mismo modo y tiene la misma utilidad que :is().

La pseudoclase :has()

Esta pseudoclase es más interesante que :where(), pero según los datos de caniuse, recomiendo esperar unos pocos meses antes de empezar a usarla en producción.

Pero esto no impide que podamos ir familiarizándonos ya con ella!

La sintaxis es muy similar a la de :is() y :where(), pero en este caso sirve para seleccionar elementos teniendo en cuenta su contenido. Lo mas fácil es ver un ejemplo:

article:has(img) { color: red; }

En este ejemplo, estamos seleccionado todos los elementos <article> que tengan un elemento <img> en su interior (ya sea hijo, nieto, bisnieto…).

Y aunque pueda parecer poca cosa, lo cierto es que se trata de un selector extremadamente interesante.

Con CSS es lo mas normal del mundo seleccionar una serie de elementos que estan dentro de otro elemento gracias al selector de elementos descendientes:

#menu a { color: red; } 

En este ejemplo, estamos seleccionado todos los enlaces de nuestro menú. Es decir, estamos seleccionando una serie de elementos a partir de su ascendencia.

Pero hasta esta nueva pseudoclase :has(), no era posible hacerlo al revés. Es decir, seleccionar una serie de elementos a partir de su descendencia.

Además, podemos conbinarla con la pseudoclase :not(), por lo que también podemos seleccionar una serie de elementos a partir de lo que NO tiene de descendencia.

article:not(:has(img)) { color: red; }

En el anterior ejemplo, estamos seleccionando todos los <article> que NO tengan dentro algún elemento <img>.

También, y al igual que sucede con :is() y :where(), podemos indicar varios argumentos.

article:has(img, video) { color: red; }

En este último ejemplo, estamos seleccionado todos los elementos <article> que tengan un elemento <img> o un elemento <video> en su interior.

Este pseudoclase abre toda una serie de posibilidades que está entusiasmando a muchos desarrolladores. Veamos un ejemplo algo más avanzado de como usarla, para mostrar contenido o no según una selección del usuario.

HTML:

<div id="selector">
  <p>Selecciona:</p>
  <input type="radio" id="opcion-a" name="opc" value="a">
  <input type="radio" id="opcion-b" name="opc" value="b">
  <input type="radio" id="opcion-c" name="opc" value="c">
  <div class="opcion-a">
    El usuario ha elegido la primera opción.
  </div>
  <div class="opcion-b">
    El usuario ha elegido la segunda opción.
  </div>  
  <div class="opcion-c">
    El usuario ha elegido la tercera opción.
  </div>    
</div>  

CSS:

#selector [class^="opcion-"] { display: none; }
#selector:has(#opcion-a:checked) .opcion-a { display: block; }
#selector:has(#opcion-b:checked) .opcion-b { display: block; }
#selector:has(#opcion-c:checked) .opcion-c { display: block; }

Dándole un poco más de estilo podríamos tener algo así como un sistema de pestañas sin necesidad de JavaScript (y sin otra serie de limitaciones del ejemplo enlazado).

Y nada más. Espero que os haya resultado útil este artículo y os sirva para ir familiarizaros con estas pseudoclases.

Escribe un comentario