Desmitificando Seletores Complexos
Se existem recursos no CSS que a total compreensão se restringe a uma parcela de desenvolvedores, esses são os combinadores filhos (>), irmãos adjacentes (+) e adjacentes gerais (~).
Sem dúvidas, esses 3 combinadores são tão poderosíssimos quanto mal-explicados. É importante compreendê-los integralmente e há dois bons motivos para isso: o seletor descendente não dá conta de tudo e, o óbvio: o CSS está evoluindo.
X > Y
Todo filho é necessariamente descendente, mas nem todo descendente é necessariamente filho.
Para que Y seja alvo da seleção, não importa a posição; basta que seja descendente direto de X - isso é, filho. Em outras palavras, basta que esteja interno diretamente ao elemento pai - e seja somente descendente dele. Isso quer dizer que Y não será alvo caso esteja interno a um elemento Z, mesmo que este esteja interno a X. Por essa razão o combinador “>” é também chamado de direto, pois não admite elementos internos indiretamente.
Seletor descendente vs. seletor filho
Lembrando da frase dita no início desse tópico, você já entende a diferença. Enquanto o descendente (X Y) herda as propriedades aos elementos direta e indiretamente internos (filhos, netos, bisnetos…), o alvo do combinador filho são os filhos unicamente diretos - sim, falar isso é redundante. O que faz todo sentido, afinal, um filho é tanto filho quanto descendente; e o neto, bisneto, trineto não é um filho, mas é descendente.
Na prática
Imaginemos um artigo e seus respectivos parágrafos. Dentro desse artigo, haverá uma seção de informações que não estará diretamente relacionada ao artigo. Como o que se quer destacar é a leitura do artigo, seus parágrafos terão mais ênfase de alguma forma.
HTML
<article>
<h1>Título do artigo</h1>
<p>Primeiro parágrafo</p>
<p>Segundo parágrafo</p>
<aside>
<h2>Informações</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</aside>
<p>Terceiro parágrafo</p>
</article>
CSS
article > p {
font-style: italic;
}
Assim, somente os parágrafos que são filhos diretos do elemento article serão estilizados.
X + Y
Se para ser alvo do seletor filho a posição era irrelevante, para ser alvo de um seletor irmão adjacente sua posição é critério decisivo. O elemento Y deve ser o primeiro elemento após X, com ambos dentro de um mesmo elemento Z (pai). O nome, portanto, é bem autoexplicativo: são irmãos por possuírem o mesmo pai (no caso, Z) e adjacentes por estarem necessariamente próximos.
Na prática 1
Supondo um artigo constituído por um título e 3 parágrafos. O primeiro parágrafo após o título servirá como uma introdução ao artigo e, portanto, deve ser destacado com um aumento no tamanho da fonte.
HTML
<article>
<!-- Z -->
<h1>Título do artigo</h1>
<!-- X -->
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
<!-- Y -->
<p>Debitis sint aperiam numquam nisi animi porro in reprehenderit!</p>
<p>Magnam atque placeat fuga sed eligendi maxime neque labore. Doloribus?</p>
</article>
CSS
h1 + p {
font-size: 20px;
}
Na prática 2
O checkbox hack funciona com o uso do combinador irmão adjacente.
HTML
<input type="checkbox" id="hider" />
<div>Hide me if you can!</div>
<label for="hider">Esconder div</label>
CSS
input[type="checkbox"] {
display: none; /* Esconde o checkbox */
}
input:checked + div {
display: none; /* Quando o checkbox for checado, a div será escondida */
}
X ~ Y
Seletor do CSS 3, o combinador adjacente geral tem uma definição bem semelhante ao irmão adjacente. Para que Y seja alvo, os elementos X e Y devem ser filhos de um mesmo elemento (irmãos) e X deve preceder Y, direta ou indiretamente - isso é, para que Y seja alvo, esse precedimento não precisa ser imediato.
Na prática
Esse combinador contorna algumas inflexibilidades do combinador irmão adjacente. Ainda com o exemplo do checkbox hack, podemos personalizar o elemento de forma não tão específica quanto à sua posição:
HTML
<input type="checkbox" id="shower" />
<label for="shower">Mostrar div</label>
<div>Hide me if you can!</div>
<div>Hide me too if you can!</div>
CSS
input[type="checkbox"],
div {
display: none; /* Esconde o checkbox e a div, por padrão */
}
input:checked ~ div {
display: block; /* Quando o checkbox for checado, a div aparecerá */
}
Conclusão
As linguagens CSS e HTML foram documentadas para serem intuitivas: os elementos formam famílias com outros elementos pais, filhos, descendentes, irmãos…. Isso fica claro no nome dos seletores, que têm papel importante na compreensão do combinador; afinal, caso o desenvolvedor entenda que irmãos tem o mesmo pai e que filhos são descendentes diretos do pai, ele poderá tirar um bom proveito do nome dos combinadores para compreender seus funcionamentos.
[…] In both cases, non-element nodes (e.g. text between elements) are ignored when considering adjacency of elements.
Há uma grande confusão - e com razão - entre os seletores irmãos adjacentes e irmãos gerais. Essa confusão se origina não só de suas classificações como seletores de combinação, mas em seus comportamento e definição semelhantes. Tanto é verdade que o combinador adjacente geral (~) se comportará muitas vezes como um irmão adjacente (+), com a diferença de que o adjacente geral é menos exigente quanto à posição do elemento-alvo.
O uso será, portanto, facultativo em diversas situações. E, nesse caso, a minha recomendação é dar prioridade ao combinador adjacente, visto que é um seletor do CSS 2.1 e, portanto, compatível com uma maior gama de browsers. :-)
Referências
- W3C. Selectors
- W3C. Selectors Level 3, 29 setembro de 2011