Este é um dos exemplos mais conhecidos de autores que não entendem como :first-child
funciona. Introduzida no CSS2 , a :first-child
pseudo-classe representa o primeiro filho de seu pai . É isso aí. Há um equívoco muito comum de que ele seleciona o elemento filho o primeiro a corresponder às condições especificadas pelo restante do seletor composto. Devido à maneira como os seletores funcionam (veja aqui uma explicação), isso simplesmente não é verdade.
Os seletores de nível 3 introduzem uma :first-of-type
pseudo-classe , que representa o primeiro elemento entre irmãos de seu tipo de elemento. Esta resposta explica, com ilustrações, a diferença entre :first-child
e :first-of-type
. No entanto, assim como :first-child
acontece, ele não analisa outras condições ou atributos. Em HTML, o tipo de elemento é representado pelo nome da marca. Na pergunta, esse tipo é p
.
Infelizmente, não há :first-of-class
pseudo-classe semelhante para corresponder ao primeiro elemento filho de uma determinada classe. Uma solução alternativa que Lea Verou e eu propusemos para isso (embora de forma totalmente independente) é primeiro aplicar os estilos desejados a todos os seus elementos com essa classe:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
... então "desfaça" os estilos dos elementos da classe que se seguem ao primeiro , usando o combinador geral de irmãos~
em uma regra primordial:
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
Agora, apenas o primeiro elemento com class="red"
terá uma borda.
Aqui está uma ilustração de como as regras são aplicadas:
<div class="home">
<span>blah</span> <!-- [1] -->
<p class="red">first</p> <!-- [2] -->
<p class="red">second</p> <!-- [3] -->
<p class="red">third</p> <!-- [3] -->
<p class="red">fourth</p> <!-- [3] -->
</div>
Nenhuma regra é aplicada; nenhuma borda é renderizada.
Este elemento não possui a classe red
, por isso é ignorado.
Somente a primeira regra é aplicada; uma borda vermelha é renderizada.
Este elemento tem a classe red
, mas não é precedido por nenhum elemento com a classe red
em seu pai. Assim, a segunda regra não é aplicada, apenas a primeira, e o elemento mantém sua borda.
Ambas as regras são aplicadas; nenhuma borda é renderizada.
Este elemento tem a classe red
. Também é precedida por, pelo menos, um outro elemento com a classe red
. Assim, ambas as regras são aplicadas e a segunda border
declaração substitui a primeira, "desfazendo-a", por assim dizer.
Como um bônus, embora tenha sido introduzido nos Seletores 3, o combinador geral de irmãos é realmente muito bem suportado pelo IE7 e mais recente, ao contrário :first-of-type
e :nth-of-type()
que são suportados apenas pelo IE9 em diante. Se você precisar de um bom suporte ao navegador, está com sorte.
De fato, o fato de o combinador de irmãos ser o único componente importante nessa técnica, e ter um suporte incrível ao navegador, torna essa técnica muito versátil - você pode adaptá-lo para filtrar elementos por outras coisas, além dos seletores de classe:
Você pode usar isso para contornar o :first-of-type
IE7 e o IE8, simplesmente fornecendo um seletor de tipo em vez de um seletor de classe (novamente, mais sobre o uso incorreto aqui em uma seção posterior):
article > p {
/* Apply styles to article > p:first-of-type, which may or may not be :first-child */
}
article > p ~ p {
/* Undo the above styles for every subsequent article > p */
}
Você pode filtrar por seletores de atributos ou quaisquer outros seletores simples em vez de classes.
Você também pode combinar essa técnica de substituição com pseudoelementos, mesmo que tecnicamente os pseudoelementos não sejam seletores simples.
Observe que, para que isso funcione, você precisará saber com antecedência quais serão os estilos padrão para os outros elementos irmãos, para que você possa substituir a primeira regra. Além disso, como isso envolve a substituição de regras no CSS, você não pode conseguir a mesma coisa com um único seletor para usar com a API Selectors ou com os localizadores CSS do Selenium .
Vale ressaltar que os Seletores 4 introduzem uma extensão da :nth-child()
notação (originalmente uma pseudo classe totalmente nova chamada :nth-match()
), que permitirá que você use algo como um :nth-child(1 of .red)
substituto hipotético .red:first-of-class
. Sendo uma proposta relativamente recente, ainda não há implementações interoperáveis suficientes para que possam ser utilizadas em locais de produção. Espero que isso mude em breve. Enquanto isso, a solução que sugeri deve funcionar na maioria dos casos.
Lembre-se de que esta resposta pressupõe que a pergunta esteja procurando por todos os elementos do primeiro filho que tenham uma determinada classe. Não existe uma pseudo-classe nem mesmo uma solução CSS genérica para a enésima correspondência de um seletor complexo em todo o documento - se uma solução existe depende muito da estrutura do documento. jQuery fornece :eq()
, :first
, :last
e mais para o efeito, mas nota mais uma vez que eles funcionam de forma muito diferente de :nth-child()
et al . Usando a API Selectors, você pode usar document.querySelector()
para obter a primeira correspondência:
var first = document.querySelector('.home > .red');
Ou use document.querySelectorAll()
com um indexador para escolher qualquer correspondência específica:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
Embora a .red:nth-of-type(1)
solução na resposta original aceita por Philip Daubmeier funcione (que foi originalmente escrita por Martyn, mas excluída desde então), ela não se comporta da maneira que você esperaria.
Por exemplo, se você deseja apenas selecionar a p
marcação original:
<p class="red"></p>
<div class="red"></div>
... então você não pode usar .red:first-of-type
(equivalente a .red:nth-of-type(1)
), porque cada elemento é o primeiro (e somente) um de seu tipo ( p
e div
respectivamente), para que ambos sejam correspondidos pelo seletor.
Quando o primeiro elemento de uma determinada classe também é o primeiro de seu tipo , a pseudo-classe funciona, mas isso acontece apenas por coincidência . Esse comportamento é demonstrado na resposta de Philip. No momento em que você colar um elemento do mesmo tipo antes desse elemento, o seletor falhará. Tomando a marcação atualizada:
<div class="home">
<span>blah</span>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
A aplicação de uma regra com .red:first-of-type
funcionará, mas depois de adicionar outra p
sem a classe:
<div class="home">
<span>blah</span>
<p>dummy</p>
<p class="red">first</p>
<p class="red">second</p>
<p class="red">third</p>
<p class="red">fourth</p>
</div>
... o seletor falhará imediatamente, porque o primeiro .red
elemento agora é o segundo p
.