Це один із найвідоміших прикладів непорозуміння авторів, як це :first-child
працює. Введений у CSS2 , :first-child
псевдоклас представляє першу дитину свого батька . Це воно. Існує дуже поширена помилка, що він підбирає той дочірній елемент, який перший відповідає умовам, визначеним рештою селектора. З - за способу селектор роботи (див тут для пояснення), що просто не відповідає дійсності.
Селектори 3 рівня представляють :first-of-type
псевдоклас , який представляє перший елемент серед братів і сестер свого типу елементів. Ця відповідь пояснює, за допомогою ілюстрацій, різницю між :first-child
та :first-of-type
. Однак, як і у випадку :first-child
, він не дивиться на будь-які інші умови чи атрибути. У HTML тип елемента представлений іменем тегу. У питанні, такий тип є p
.
На жаль, не існує подібного :first-of-class
псевдокласу для відповідності першому дочірньому елементу даного класу. Одне рішення, яке ми з Леєю Веру придумали (хоч і абсолютно незалежно) - це спершу застосувати потрібні стилі до всіх своїх елементів у цьому класі:
/*
* Select all .red children of .home, including the first one,
* and give them a border.
*/
.home > .red {
border: 1px solid red;
}
... потім "скасуйте" стилі для елементів класу, що надходять після першого , використовуючи загальний комбінатор сибсингу~
в переважаючому правилі:
/*
* Select all but the first .red child of .home,
* and remove the border from the previous rule.
*/
.home > .red ~ .red {
border: none;
}
Тепер лише перший елемент з class="red"
буде мати межу.
Ось ілюстрація того, як правила застосовуються:
<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>
Правила не застосовуються; кордон не надається.
Цей елемент не має класу red
, тому його пропускають.
Застосовується лише перше правило; нанесено червону облямівку.
Цей елемент має клас red
, але йому не передують жодні елементи з класом red
у його батьківському. Таким чином, друге правило не застосовується, тільки перше, і елемент зберігає свою межу.
Обидва правила застосовуються; кордон не надається.
Цей елемент має клас red
. Йому також передує хоча б ще один елемент із класом red
. При цьому застосовуються обидва правила, а друге border
оголошення відміняє перше, тим самим "скасовуючи" його, так би мовити.
В якості бонусу, хоча вона була введена в селектор 3, загальний споріднений комбінатор насправді дуже добре підтримується IE7 і вище, в відміну :first-of-type
та :nth-of-type()
які підтримуються тільки IE9 вперед. Якщо вам потрібна хороша підтримка браузера, вам пощастить.
Насправді той факт, що комбінатор братів і сестер є єдиним важливим компонентом у цій техніці, і він має таку дивовижну підтримку браузера, робить цю техніку дуже універсальною - ви можете адаптувати її для фільтрування елементів іншими речами, крім селекторів класів:
Ви можете використовувати це для обходу :first-of-type
в IE7 та IE8, просто надавши селектор типу замість селектора класів (знову докладніше про його неправильне використання тут у наступному розділі):
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 */
}
Ви можете фільтрувати за допомогою селекторів атрибутів або будь-яких інших простих селекторів замість класів.
Ви також можете поєднати цю переважну техніку з псевдоелементами, хоча технічно псевдоелементи не є простими селекторами.
Зауважте, що для того, щоб це працювало, вам потрібно буде заздалегідь знати, якими будуть стилі за замовчуванням для інших ваших елементів братів, щоб ви могли перекрити перше правило. Крім того, оскільки це включає переважаючі правила в CSS, ви не можете досягти того ж самого з одним селектором для використання з API Selectors або з CSS-локаторами Selenium .
Варто згадати, що селектори 4 вводять розширення до :nth-child()
позначень (спочатку абсолютно новий псевдокласс :nth-match()
, який називається ), що дозволить вам використовувати щось на зразок :nth-child(1 of .red)
гіпотетичного .red:first-of-class
. Будучи відносно недавньою пропозицією, поки що недостатньо сумісних реалізацій для її використання на виробничих майданчиках. Сподіваємось, це скоро зміниться. Тим часом рішення, яке я запропонував, має працювати в більшості випадків.
Майте на увазі, що ця відповідь передбачає, що питання шукає кожен перший дочірній елемент, який має даний клас. Не існує ані псевдокласу, ані навіть загального CSS рішення для n-го збігу складного селектора у всьому документі - чи існує рішення сильно залежить від структури документа. JQuery надає :eq()
, :first
, :last
і багато іншого для цієї мети, але ще раз відзначити , що вони працюють дуже по- різному з :nth-child()
співавт . Використовуючи API Selectors, ви можете або скористатися document.querySelector()
для отримання першої відповідності:
var first = document.querySelector('.home > .red');
Або скористайтеся document.querySelectorAll()
індексатором, щоб вибрати будь-яку конкретну відповідність:
var redElements = document.querySelectorAll('.home > .red');
var first = redElements[0];
var second = redElements[1];
// etc
Хоча .red:nth-of-type(1)
рішення в оригіналі прийнятої відповіді Філіпа Добмейера працює (яке спочатку було написано Мартином, але видалено з тих пір), воно не веде себе так, як ви цього очікували.
Наприклад, якщо ви хотіли вибрати лише p
оригінальну розмітку:
<p class="red"></p>
<div class="red"></div>
... тоді ви не можете використовувати .red:first-of-type
(еквівалентно .red:nth-of-type(1)
), тому що кожен елемент є першим (і єдиним) своїм типом ( p
і div
відповідно), тому обидва будуть відповідати селектору.
Коли перший елемент певного класу також є першим за своїм типом , псевдоклас працюватиме, але це відбувається лише за збігом обставин . Така поведінка продемонстрована у відповіді Філіпа. У той момент, коли ви вставите елемент одного типу перед цим елементом, селектор не вдасться. Здійснення оновленої розмітки:
<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>
Застосування правила з .red:first-of-type
буде працювати, але як тільки ви додасте інше p
без класу:
<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>
... селектор негайно вийде з ладу, тому що перший .red
елемент - це другий p
елемент.