Это один из самых известных примеров недопонимания авторами того, как :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 селекторов или CSS-локаторами Selenium .
Стоит отметить, что в Selectors 4 введено расширение для :nth-child()
обозначения (изначально это был совершенно новый псевдокласс :nth-match()
), которое позволит вам использовать что-то вроде :nth-child(1 of .red)
гипотетического .red:first-of-class
. Это сравнительно недавнее предложение, поэтому недостаточно совместимых реализаций, чтобы его можно было использовать на производственных площадках. Надеюсь, это скоро изменится. В то же время предложенный мной обходной путь должен работать в большинстве случаев.
Имейте в виду, что этот ответ предполагает, что вопрос ищет каждый первый дочерний элемент, который имеет данный класс. Не существует ни псевдокласса, ни даже общего CSS-решения для n-го совпадения сложного селектора по всему документу - вопрос о том, существует ли решение, сильно зависит от структуры документа. JQuery предоставляет :eq()
, :first
, :last
и многое другое для этой цели, но еще раз отметить , что они работают очень по- разному с :nth-child()
соавт . Используя API селекторов, вы можете использовать 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
элементом.