Como utilizar o novo seletor has() em CSS

Como utilizar o novo seletor has() em CSS

Um dos mais aguardados seletores do CSS 4 agora já é suportado pela maioria dos browsers modernos: :has(). Até a escrita deste post, no Chrome, Edge e Safari já podem ser usados, o Firefox apenas ativando o parâmetro layout.css.has-selector.enabled.

Agora, o que é o seletor :has()?


O :has() te permite estilizar de acordo com uma regra, é como se tivéssemos um IF na hora de selecionar um elemento, pois só vai adicionar determinado estilo caso atenda uma determinada condição. Vamos por partes =).

Por que eu preciso dele do :has()?

Muitas vezes que precisamos adicionar um estilo em um elemento específico de acordo com uma condição, precisamos recorrer ao JavaScript para apenas incluir uma classe CSS em uma determinada div por exemplo, pois o seu conteúdo é dinâmico e só vai aparecer em determinada condição.

Então faríamos algo assim, dado o HTML:

Com o resultado:

Nesse caso, a problemática seria adicionar uma borda preta na Div que contenha um H1 dentro.

Para ter o seguinte resultado:

Como poderíamos fazer?


Não quero colocar uma borda no H1, mas sim na primeira Div. Partindo da premissa que não podemos simplesmente adicionar uma classe em cada Div, pois elas são geradas automaticamente pela minha aplicação, precisaríamos de um código JavaScript que procuraria todas as Divs que possuem um H1 dentro, e então adicionaríamos a nossa classe CSS com o estilo (ou um estilo in-line com o style).

Esse é o ponto principal do :has(), pois com ele, podemos criar o seguinte código:


E o resultado no browser:

Agora que entendemos a premissa, vamos para a parte divertida, pois o :has() é muito poderoso e pode ser combinado de várias formas.

Explorando o seletor :has()


Como o :has() é uma pseudo-class (tais como :not, :before, :hover ou :host) ela pode ser combinada com outras e criar combinações que vão te ajudar. É bem parecida com o has() do jQuery para quem está habituado.

Na sintaxe mais básica, você vai poder localizar tag names, ids e classes, por exemplo:

E como disse, pode ser combinado com outros :has(), como:

E tal como você seleciona vários itens em lista no CSS separando por virgula, você também pode passar para um :has():

💡
Obs: Se caso passar um elemento inválido na lista de parâmetros do :has(), esse item simplesmente será ignorado e o CSS vai continuar funcionando para os outros itens, pois o has vai simplesmente considerar que não achou, por exemplo abaixo não vai encontrar os seletores alvaro e ::beornottobe.

Um caso de uso simples para exemplificar, quero colocar uma borda vermelha em todos os links que tiverem uma imagem dentro:

Ou então selecionar todas as tags da aplicação que possuam uma classe filho específica:

Nesse exemplo acima, quem tiver um filho com a class child, vai receber um estilo.

Combinando com o :not()


Para quem não conhece o :not(), ele já é um seletor amplamente suportado por todos os browsers, no qual você pode fazer negações na hora de selecionar.
Então ao invés de sempre procurarmos selecionar uma tag com base em uma condição VERDADEIRA, podemos negá-la, selecionando todas as tags contrárias. Meio abstrato né? Vamos aos exemplos.

Primeiro, usando como exemplo aquele HTML que já estávamos usando acima em que temos dois blocos DIV, no qual o primeiro tem um H1 e o segundo tem uma lista (ul/li):

Como já explicamos, se quisermos estilizar o h1 usando o :has, só precisamos fazer:

Porém, se quisermos selecionar todas as DIVs que não tenham um H1, só precisamos negar a nossa condição com o :not, veja:

Esse recurso pode ser extremamente útil quando você está tentando colocar um estilo em vários itens de uma lista por exemplo, mas quer que o primeiro ou o último item tenham um estilo diferente, por exemplo dado o seguinte HTML abaixo (aumentei o HTML anterior e incluí outra lista no final, que não está dentro de uma DIV):


Se queremos:

● Adicionar uma margem abaixo de cada item.
● Não selecionar o último item.
● E só incluir o estilo em listas que estejam dentro de uma DIV.

Podemos selecionar dessa maneira:


Explicado em detalhes ficaria assim:

● O div:has(ul) vai selecionar apenas as DIVs com lista dentro.
● Então vai buscar todas as tags li dentro dessa div (eu poderia ser mais específico e utilizar o > para buscar as tags filhas, mas não convém deixar o exemplo mais complexo).
● O :not(:last-of-type) vai selecionar todas as li e excluir a última li encontrada, que no caso vai ser o último item da lista que não queremos margem.


Compatibilidade


Você pode testar diretamente no CSS se o :has está disponível, para então aplicar um determinado estilo, através do @supports do CSS, assim pode aplicar códigos diferentes se for o caso, por exemplo:

Você também pode usar a Supports API do JavaScript para verificar se o browser é compatível ou não:

Status atual de compatibilidade segundo o Can I Use:

Para verificar o status atual entre os browsers, veja em: https://caniuse.com/?search=%3Ahas


Conclusão


Nesse artigo, vimos como o novo seletor :has do CSS 4 pode ser usado e combinado com outros :has. Em um cenário real, muitas vezes quando precisamos adicionar um estilo a um elemento com base em uma condição, recorremos ao Javascript, apenas para adicionar ou remover uma classe CSS, com o novo pseudo elemento muito código poderá ser removido (ou então nem criado), facilitando a vida dos programadores.

Também vimos como combinar a utilização do :has com o :not, que pode ser muito útil na hora de não selecionar elementos indesejados. Veja que, por mais que mostramos o :not, o CSS é livre para você criar combinações, então você também pode usar o :is, :where, :after ou :before caso seja necessário. O :not nos dá uma visão melhor do que podemos fazer por ser a exata negação do :has.

Sobre a compatibilidade, você deve se atentar caso a sua aplicação ou site precise ser compatível com o Samsung Internet ou Firefox, que ainda não estão prontos out-of-the-box, então pode ser que você tenha que utilizar o @supports por um tempo até esse código poder ser removido (quando você vir que no seu analytics as pessoas não usam mais a versão antiga desses browsers).

É isso e até a próxima \o/

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

💡
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.