$ sudo teach IT

Представьте список карточек или строк таблицы, где каждую вторую нужно подкрасить для удобства чтения, у последней — убрать разделительную линию, а самую первую — выделить. Можно навешать классы вручную на каждый элемент, но это утомительно и легко ошибиться: добавили карточку — и разметка «поехала». Структурные псевдоклассы решают это иначе: они выбирают элементы по их положению среди соседей, ничего не добавляя в HTML. Список остаётся чистым, а стили сами применяются к нужным элементам.

После этого урока вы сможете выбирать первый, последний и каждый n-й элемент, а также исключать элементы из выборки — и всё это одними селекторами, без лишних классов и без правок разметки при изменении числа элементов.

Что уже умеем / что добавим

На прошлом уроке вы научились стилизовать элементы по состоянию — наведению, фокусу, нажатию. Теперь научимся выбирать элементы по их месту в структуре: первый, последний, каждый чётный. В сквозном проекте мы наведём порядок в списке карточек услуг: сделаем «зебру» и уберём лишний отступ у последней карточки — без единого нового класса.

Что вы узнаете в рамках статьи

  • Как выбрать первый и последний элемент через :first-child и :last-child.
  • Как выбирать каждый n-й элемент с помощью :nth-child().
  • Как задавать чередование через 2n, odd и even.
  • Как исключать элементы из выборки псевдоклассом :not().

Ключевые термины

  • Структурный псевдокласс — псевдокласс, выбирающий элемент по его положению среди соседей.
  • :first-child — первый дочерний элемент в своём родителе.
  • :last-child — последний дочерний элемент в своём родителе.
  • :nth-child(n) — элемент с определённым порядковым номером (или по формуле).
  • :not(...) — элемент, который не подходит под указанный селектор.
  • Формула an+b — выражение для :nth-child, задающее шаг и сдвиг выборки.

Первый и последний: :first-child и :last-child

Псевдоклассы :first-child и :last-child выбирают, соответственно, первый и последний элемент внутри его родителя. Частая задача — убрать разделительную границу или отступ у крайнего элемента:

<ul class="menu">
  <li>Главная</li>
  <li>Услуги</li>
  <li>Контакты</li>
</ul>
.menu li {
  border-bottom: 1px solid #ddd;
}

.menu li:last-child {
  border-bottom: none;
}

Все пункты меню получат нижнюю границу, а :last-child уберёт её у последнего — список не будет заканчиваться лишней чертой. Красота в том, что при добавлении или удалении пунктов ничего править не нужно: «последним» всегда автоматически становится актуальный последний элемент. Аналогично :first-child удобен, чтобы, например, убрать верхний отступ у самого первого заголовка в блоке.

Каждый n-й: :nth-child()

:nth-child() — самый гибкий из структурных псевдоклассов. В скобках можно указать конкретный номер, ключевое слово или формулу. Простейший вариант — номер: :nth-child(2) выберет второй элемент. Ключевые слова odd и even выбирают нечётные и чётные элементы — так делают «зебру»:

.row:nth-child(even) {
  background: #f5f7fa;
}

Каждая чётная строка получит светлый фон — классический приём для читаемости длинных списков и таблиц. Здесь тоже всё автоматически: добавили строки — чередование продолжится само. odd и even — это удобные псевдонимы: even эквивалентно формуле 2n, а odd — формуле 2n+1.

Формула an+b

Для более сложных выборок :nth-child() принимает формулу вида an+b, где браузер подставляет вместо n числа 0, 1, 2, 3 и так далее и выбирает элементы с получившимися номерами. Это звучит абстрактно, поэтому разберём на примерах:

/* Каждый третий элемент: 3, 6, 9, ... */
.item:nth-child(3n) { }

/* Каждый третий, начиная с первого: 1, 4, 7, ... */
.item:nth-child(3n+1) { }

В первом случае при n = 1, 2, 3 получаем номера 3, 6, 9 — каждый третий. Во втором 3n+1 даёт 1, 4, 7 — тоже с шагом три, но со сдвигом на один. Так можно, например, оформлять сетку по три в ряд, выделяя первый элемент каждого ряда. Формула кажется непривычной, но принцип один: a — это шаг чередования, b — сдвиг начала. Начните с odd/even, а к формулам переходите, когда нужен шаг больше двух.

Исключаем элементы: :not()

Псевдокласс :not() выбирает элементы, которые не подходят под указанный внутри селектор. Это удобно, когда правило нужно применить ко всем, кроме некоторых:

.btn:not(.btn-primary) {
  background: #eee;
  color: #333;
}

Здесь стиль получат все кнопки, кроме главной (.btn-primary) — не придётся заводить отдельный класс «второстепенная кнопка». :not() часто комбинируют с другими селекторами: например, li:not(:last-child) — «все пункты, кроме последнего» — удобная альтернатива для отступов между элементами. Внутрь :not() можно поместить класс, тег или даже другой псевдокласс, что делает его очень гибким инструментом для исключений.

Применяем в проекте

Наведём порядок в списке карточек услуг сайта компании. Сделаем «зебру», чтобы длинный список читался легче, и уберём нижний отступ у последней карточки, чтобы блок заканчивался ровно. Всё — без новых классов в разметке.

.card {
  margin-bottom: 16px;
  background: white;
}

.card:nth-child(even) {
  background: #f5f7fa;
}

.card:last-child {
  margin-bottom: 0;
}

Обновите страницу: чётные карточки получили лёгкий серый фон — список стало приятнее просматривать, а последняя карточка больше не создаёт лишнего отступа снизу. Мы не тронули HTML: добавляем и удаляем карточки свободно, а чередование и «ровный низ» подстраиваются сами. Это чистый и надёжный способ оформления повторяющихся элементов.

Частые ошибки

  • Путаница :nth-child и порядка.
    Нумерация начинается с 1, а не с 0; :nth-child(1) — это первый элемент.
  • :first-child, когда элемент не первый.
    Псевдокласс сработает, только если элемент действительно первый среди всех соседей, а не первый своего типа; для выбора по типу есть :first-of-type.
  • Пробел внутри :not().
    Селектор пишут слитно: li:not(:last-child), а не li :not(...) — иначе смысл меняется на потомка.
  • Слишком сложные формулы.
    Если можно обойтись odd/even, не усложняйте формулой — код будет понятнее.

Итоги

Отличная работа — вы научились выбирать элементы по их месту в структуре! Теперь вы умеете обращаться к первому и последнему элементу через :first-child и :last-child, выбирать каждый n-й с помощью :nth-child() и формул an+b, делать «зебру» через odd/even и исключать элементы псевдоклассом :not(). В сквозном проекте вы навели порядок в списке карточек, не добавив ни одного класса, — и список теперь сам подстраивается под любое число элементов.

Так держать — ваш CSS становится всё выразительнее! В следующем уроке мы займёмся псевдоэлементами ::before и ::after: научимся добавлять декоративные детали — линии, значки, метки — прямо из стилей, без лишних тегов в разметке.

На следующий урок