Jest to jeden z najbardziej znanych przykładów nieporozumień autorów dotyczących sposobu :first-child
działania. Wprowadzony w CSS2 The :first-child
pseudoklasa oznacza pierwszego dziecka jego rodzica . Otóż to. Istnieje bardzo powszechne nieporozumienie, że wybiera on dowolny element potomny, który jako pierwszy spełnia warunki określone przez resztę selektora złożonego. Ze względu na pracę w sposób selektorów (patrz tutaj o wyjaśnienie), że nie jest po prostu nieprawda.
Poziom selektorów 3 wprowadza :first-of-type
pseudoklasę , która reprezentuje pierwszy element wśród rodzeństwa tego typu elementu. Ta odpowiedź wyjaśnia ilustracją różnicę między :first-child
i :first-of-type
. Jednak, podobnie jak w przypadku :first-child
, nie uwzględnia żadnych innych warunków ani atrybutów. W HTML typ elementu jest reprezentowany przez nazwę znacznika. W pytaniu tym typem jest p
.
Niestety, nie ma podobnej :first-of-class
pseudoklasy do dopasowania pierwszego elementu potomnego danej klasy. Jednym z obejść tego problemu, które opracowaliśmy wspólnie z Leą Verou (choć całkowicie niezależnie), jest zastosowanie pożądanych stylów do wszystkich elementów w tej klasie:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
... następnie „cofnij” style elementów z klasą następującą po pierwszej , używając ogólnego kombinatora rodzeństwa~
w nadrzędnej regule:
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
Teraz tylko pierwszy element z class="red"
będzie miał obramowanie.
Oto przykład zastosowania reguł:
<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>
Nie stosuje się żadnych zasad; żadna ramka nie jest renderowana.
Ten element nie ma klasy red
, więc został pominięty.
Stosowana jest tylko pierwsza reguła; renderowana jest czerwona ramka.
Ten element ma klasę red
, ale nie jest poprzedzony żadnymi elementami z klasą red
w jej rodzicu. Zatem druga zasada nie jest stosowana, tylko pierwsza, a element zachowuje swoją granicę.
Obie zasady są stosowane; żadna ramka nie jest renderowana.
Ten element ma klasę red
. Poprzedza go także co najmniej jeden inny element z klasą red
. W ten sposób stosowane są obie reguły, a druga border
deklaracja zastępuje pierwszą, a tym samym „cofając” ją, że tak powiem.
Jako bonus, mimo że został wprowadzony w Selectors 3, ogólny kombinator rodzeństwa jest w rzeczywistości dość dobrze obsługiwany przez IE7 i nowsze, w przeciwieństwie :first-of-type
do tych, :nth-of-type()
które są obsługiwane tylko przez IE9. Jeśli potrzebujesz dobrego wsparcia przeglądarki, masz szczęście.
W rzeczywistości fakt, że kombinator rodzeństwa jest jedynym ważnym elementem tej techniki i ma tak niesamowite wsparcie przeglądarki, czyni tę technikę bardzo wszechstronną - możesz ją dostosować do filtrowania elementów innymi rzeczami, poza selektorami klas:
Możesz użyć tego do obejścia :first-of-type
w IE7 i IE8, po prostu podając selektor typu zamiast selektora klasy (ponownie, więcej o jego niewłaściwym użyciu tutaj w dalszej części):
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 */
}
Możesz filtrować według selektorów atrybutów lub innych prostych selektorów zamiast klas.
Możesz także łączyć tę nadrzędną technikę z pseudoelementami, nawet jeśli pseudoelementy technicznie nie są prostymi selektorami.
Pamiętaj, że aby to zadziałało, musisz wcześniej wiedzieć, jakie będą domyślne style dla pozostałych elementów rodzeństwa, abyś mógł zastąpić pierwszą regułę. Dodatkowo, ponieważ wiąże się to z nadpisywaniem reguł w CSS, nie można osiągnąć tego samego za pomocą jednego selektora do użycia z Selectors API lub lokalizatorami Selenium CSS.
Warto wspomnieć, że Selektory 4 wprowadzają rozszerzenie :nth-child()
notacji (pierwotnie zupełnie nowa pseudoklasa zwana :nth-match()
), które pozwoli ci użyć czegoś :nth-child(1 of .red)
w miejsce czegoś hipotetycznego .red:first-of-class
. Ponieważ jest to stosunkowo nowa propozycja, nie ma wystarczającej liczby interoperacyjnych implementacji, aby można ją było zastosować w zakładach produkcyjnych. Mam nadzieję, że wkrótce się to zmieni. Tymczasem zaproponowane przeze mnie obejście powinno działać w większości przypadków.
Należy pamiętać, że ta odpowiedź zakłada, że pytanie szuka każdego pierwszego elementu potomnego, który ma daną klasę. Nie ma ani pseudoklasy, ani nawet ogólnego rozwiązania CSS dla n-tego dopasowania złożonego selektora w całym dokumencie - to, czy rozwiązanie istnieje, zależy w dużej mierze od struktury dokumentu. jQuery zapewnia :eq()
, :first
, :last
i więcej do tego celu, ale znowu pamiętać, że działają one bardzo różnie od :nth-child()
et al . Korzystając z interfejsu API Selektorów, możesz użyć pierwszego, document.querySelector()
aby uzyskać pierwsze dopasowanie:
var first = document.querySelector('.home > .red');
Lub użyj document.querySelectorAll()
z indeksem, aby wybrać dowolne określone dopasowanie:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
Chociaż .red:nth-of-type(1)
rozwiązanie w pierwotnie zaakceptowanej odpowiedzi Philipa Daubmeiera działa (pierwotnie napisane przez Martyn, ale od tego czasu usunięte), nie zachowuje się tak, jak byś tego oczekiwał.
Na przykład, jeśli chcesz tylko wybrać p
w oryginalnym znaczniku:
<p class="red"></p>
<div class="red"></div>
... wtedy nie możesz użyć .red:first-of-type
(odpowiednika .red:nth-of-type(1)
), ponieważ każdy element jest pierwszym (i jedynym) jednym z jego typów ( p
i div
odpowiednio), więc oba zostaną dopasowane przez selektor.
Gdy pierwszy element pewnej klasy jest również pierwszym tego typu , pseudoklasa będzie działać, ale dzieje się tak tylko przez przypadek . To zachowanie zostało wykazane w odpowiedzi Filipa. Gdy tylko włożysz element tego samego typu przed tym elementem, selektor zawiedzie. Biorąc zaktualizowany znacznik:
<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>
Zastosowanie reguły z .red:first-of-type
będzie działać, ale gdy dodasz inną p
bez klasy:
<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>
... selektor natychmiast zawiedzie, ponieważ pierwszy .red
element jest teraz drugim p
elementem.