Это один из самых известных примеров недопонимания авторами того, как работает :first-child
. Представленный в CSS2 , псевдокласс :first-child
представляет самого первого потомка своего родителя . Вот и все. Существует очень распространенное заблуждение, что он выбирает тот дочерний элемент, который первым соответствует условиям, заданным остальной частью составного селектора. Из-за того, как работают селекторы (см. здесь для объяснения), это просто не соответствует действительности.
Селектор уровня 3 вводит псевдокласс :first-of-type
, который представляет первый элемент среди элементов одного типа этого типа. Этот ответ объясняет с иллюстрациями разницу между :first-child
и :first-of-type
. Однако, как и в случае :first-child
, в нем не рассматриваются никакие другие условия или атрибуты. В HTML тип элемента представлен именем тега. В вопросе этот тип p
.
К сожалению, нет аналогичного :first-of-class
псевдокласса для сопоставления первого дочернего элемента данного класса. Один из обходных путей, который Lea Verou и который я придумала для этого (хотя и совершенно независимо), - это сначала применить ваши желаемые стили к всем вашим элементам с этим классом:
/*
* 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
отменяет первое, тем самым «отменяя» его, так сказать.
В качестве бонуса, хотя он был представлен в Selectors 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 или Selenium CSS-локаторами.
Стоит отметить, что в Selectors 4 введено расширение для :nth-child()
нотации (изначально совершенно новый псевдокласс :nth-match()
), который позволит вам использовать что-то вроде :nth-child(1 of .red)
вместо гипотетического .red:first-of-class
. Это сравнительно недавнее предложение, поэтому недостаточно совместимых реализаций, чтобы его можно было использовать на производственных площадках. Надеюсь, это скоро изменится. Между тем предложенный мной обходной путь должен работать в большинстве случаев.
Имейте в виду, что этот ответ предполагает, что вопрос ищет каждый первый дочерний элемент, который имеет данный класс. Для n-го совпадения комплексного селектора по всему документу не существует ни псевдокласса, ни даже общего CSS-решения - вопрос о том, существует ли решение, сильно зависит от структуры документа. 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
элементом.