CSS селектор для первого элемента с классом - PullRequest
830 голосов
/ 27 апреля 2010

У меня есть набор элементов с именем класса red, но я не могу выбрать первый элемент с class="red", используя следующее правило CSS:

.red:first-child {
    border: 5px solid red;
}
<p class="red"></p>
<div class="red"></div>

Что не так в этом селекторе и как мне его исправить?

Благодаря комментариям я понял, что элемент должен быть первым потомком своего родителя, чтобы быть выбранным, а это не тот случай, который у меня есть. У меня есть следующая структура, и это правило не выполняется, как указано в комментариях:

.home .red:first-child {
    border: 1px solid red;
}
<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?

Ответы [ 18 ]

1297 голосов
/ 16 декабря 2011

Это один из самых известных примеров недопонимания авторами того, как работает :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>
  1. Правила не применяются; Граница не отображается.
    Этот элемент не имеет класса red, поэтому он пропущен.

  2. Применяется только первое правило; отображается красная рамка.
    Этот элемент имеет класс red, но ему не предшествуют элементы с классом red в родительском элементе. Таким образом, второе правило не применяется, только первое, и элемент сохраняет свою границу.

  3. Оба правила применяются; граница не отображается.
    Этот элемент имеет класс 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 элементом.

311 голосов
/ 27 апреля 2010

Селектор :first-child предназначен, как следует из названия, для выбора первого потомка родительского тега. Дочерние элементы должны быть встроены в один родительский тег. Ваш точный пример будет работать (только что попробовал здесь ):

<body>
    <p class="red">first</p>
    <div class="red">second</div>
</body>

Может быть, вы вложили свои теги в разные родительские теги? Ваши теги класса red действительно являются первыми тегами под родителем?

Обратите внимание, что это относится не только к первому такому тегу во всем документе, но каждый раз, когда вокруг него оборачивается новый родительский элемент, например:

<div>
    <p class="red">first</p>
    <div class="red">second</div>
</div>
<div>
    <p class="red">third</p>
    <div class="red">fourth</div>
</div>

first и third тогда будут красными.

Обновление:

Я не знаю, почему Мартин удалил свой ответ, но у него было решение, селектор :nth-of-type:

<html>
<head>
<style type="text/css">
.red:nth-of-type(1)
{
    border:5px solid red;
} 
</style>
</head>

<body>
    <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>
</body>
</html>

Кредиты Мартын . Больше информации, например, здесь . Имейте в виду, что это селектор CSS 3, поэтому не все браузеры распознают его (например, IE8 или старше).

68 голосов
/ 08 ноября 2012

Правильный ответ:

.red:first-child, :not(.red) + .red { border:5px solid red }

Часть I: Если элемент является первым по отношению к своему родителю и имеет класс "red", он получает границу.
Часть II: Если элемент «.red» не является первым по отношению к своему родительскому элементу, но сразу же следует за элементом без класса «.red», он также должен заслужить честь упомянутой границы.

Скрипка или этого не произошло.

Ответ Филиппа Добмайера, хотя и принят, неверен - см. Прилагаемую скрипку.
Ответ BoltClock будет работать, но излишне определяет и переписывает стили
(в частности, проблема, когда в противном случае он наследовал бы другую границу - вы не хотите объявлять другую границу: нет)

EDIT: В случае, если у вас есть «красный», следующий за не красным, каждый «первый» красный получит границу. Чтобы предотвратить это, нужно будет использовать ответ BoltClock. См скрипка

18 голосов
/ 13 января 2017

вы можете использовать first-of-type или nth-of-type(1)

.red {
  color: green;  
}

/* .red:nth-of-type(1) */
.red:first-of-type {
  color: red;  
}
<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>
9 голосов
/ 27 апреля 2010

Чтобы соответствовать вашему селектору, элемент должен иметь имя класса red и должен быть первым потомком своего родителя.

<div>
    <span class="red"> <!-- MATCH -->
</div>

<div>
    <span>Blah</span>
    <p class="red"> <!-- NO MATCH -->
</div>

<div>
    <span>Blah</span>
    <div><p class="red"></div> <!-- MATCH -->
</div>
8 голосов
/ 27 апреля 2010

Так как другие ответы охватывают то, что неправильно , я попробую другую половину, как это исправить. К сожалению, я не знаю, что у вас есть решение CSS only , по крайней мере, не то, что я могу придумать . Хотя есть и другие варианты ....

  1. Присвойте класс first элементу при его создании, например:

    <p class="red first"></p>
    <div class="red"></div>
    

    CSS:

    .first.red {
      border:5px solid red;
    }
    

    Этот CSS соответствует только элементам с классами first и red.

  2. В качестве альтернативы, сделайте то же самое в JavaScript, например, вот какой jQuery вы бы использовали для этого, используя тот же CSS, что и выше:

    $(".red:first").addClass("first");
    
5 голосов
/ 31 января 2018

Я получил это в моем проекте.

div > .b ~ .b:not(:first-child) {
	background: none;
}
div > .b {
    background: red;
}
<div>
      <p class="a">The first paragraph.</p>
      <p class="a">The second paragraph.</p>
      <p class="b">The third paragraph.</p>
      <p class="b">The fourth paragraph.</p>
  </div>
5 голосов
/ 21 января 2014

По вашей обновленной проблеме

<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>

как насчет

.home span + .red{
      border:1px solid red;
    }

Это выберет class home , затем элемент span и, наконец, все .red элементы , которые размещаются сразу после элементов span.

Ссылка: http://www.w3schools.com/cssref/css_selectors.asp

3 голосов
/ 14 октября 2015

Вы можете изменить свой код на что-то вроде этого, чтобы заставить его работать

<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>

Это делает работу за вас

.home span + .red{
      border:3px solid green;
    }

Вот ссылка CSS от SnoopCode об этом.

3 голосов
/ 03 июня 2013

Вы можете использовать nth-of-type (1), но убедитесь, что сайту не требуется поддержка IE7 и т. Д. Если это так, используйте jQuery для добавления класса тела, затем найдите элемент через класс тела IE7, а затем имя элемента , затем добавьте в него стиль nth-child.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...