Написать пост

Как работают псевдоклассы в CSS. Подробное объяснение с примерами и диаграммами

Аватар Евгений Туренко

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

Прим. перев. Псевдоклассы предназначены для изменения стиля существующих элементов страницы в зависимости от их динамического состояния, например, при работе со ссылками (:link, :visited, :hover, :active, :focus).

В этой статье мы познакомимся с двумя типами псевдоклассов:

  • селекторы *-of-type;
  • селекторы *-child.

Иногда сложно понять, что такое псевдоклассы и как они работают. Главная сложность состоит в том, что они представлены в абстрактном виде. Поэтому для упрощения восприятия использованы схемы с DOM-деревьями.

Разметка и DOM-дерево

Обратите внимание на HTML-код, представленный ниже. Он будет использоваться во всех примерах в этой статье.

			<body>
  <div class=”main”>
     <a href=”#”>Внутренняя ссылка 1</a>
     <a href=”#”>Внутренняя ссылка 2</a>
     <ul>
       <a href=”#”>Вложенная в список ссылка 1</a>
       <li>
         <a href=”#”>Элемент списка 1</a>
       </li>
       <li>
         <a href=”#”>Элемент списка 2</a>
       </li>
     </ul>
     <a href=”#”>Внутренняя 3</a>
  </div>
  <a href=”#”>Внешняя ссылка 1</a>
  <a href=”#”>Внешняя ссылка 2</a>
</body>
		

А теперь давайте преобразуем этот код в нечто, понятное визуально и интуитивно — в DOM-дерево.

Давайте рассмотрим подробнее элемент body. В нём расположено 3 дочерних элемента: main и два элемента <a>.

			<body>
  <div class=”main”>
   ...
  </div>
  <a href=”#”>Внешняя ссылка 1</a>
  <a href=”#”>Внешняя ссылка 2</a>
</body>
		

На схеме представлено отношение между body и дочерними элементами.


Важен порядок, в котором дочерние элементы размещаются в дереве. В коде дочерние элементы располагаются сверху вниз, а в дереве — слева направо.

Теперь давайте посмотрим на div-контейнер с классом main:

			<div class=”main”>
   <a href=”#”>Внутренняя ссылка 1</a>
   <a href=”#”>Внутренняя ссылка 2</a>
   <ul>
     ...
   </ul>
   <a href=”#”>Внутренняя ссылка 3</a>
</div>
		

Контейнер с классом main имеет 4 дочерних элемента: сначала два элемента <a>, затем элемент ненумерованного списка ul и снова <a>-элемент.

Аналогичным образом спускаемся вниз по схеме согласно порядку вложенности элементов, отрисовывая полноценное дерево HTML-кода.

Внимательно изучите структуру DOM-дерева, чтобы легче воспринимать оставшуюся часть статьи.

Псевдокласс only-of-type (единственный из выбранного типа)

Для всех псевдоклассов действует один и тот же формат:

			выбираемый-элемент:селектор { /* стиль */ }
		

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

			a:only-of-type {
border: 2px solid black;
}
		

В приведённом выше фрагменте кода выбираемый элемент  — тег <a>, а его селектор — one-of-type.

Данный пример можно изучить в работе на codepen.io. Уберите символы комментариев, чтобы посмотреть, как работает тот или иной селектор, а лучше продолжите чтение статьи, чтобы не запутаться.

Начнём с того, что выберем всё, что есть в DOM, а затем отфильтруем.

Обратите внимание, как был произведён выбор: в каждой секции (от 1 до 5) у элементов есть общий родительский элемент. Пример: родительский элемент для первой секции — body, для второй секции — контейнер с классом main, и т. д. Каждая секция соответствует уровню вложенности кода.

Так как в нашем случае выбираемым элементом являются элементы <a>, то мы можем сделать следующее:

Мы выбрали все элементы <a> в каждой секции и последовательно их пронумеровали слева направо. Не забывайте, что порядок очень важен.

Теперь мы завершаем часть работы, связанную с выбираемым элементом, и приступаем к фильтрации.

 

Only-of-type проходит через все секции, выбирая только те элементы <a>, которые являются единственными для своей секции.

Обратите внимание, что <a>-элементы для первой и второй секций не были выбраны из-за того, что они не являются единственными для своих секций. Иначе говоря: одна секция — один элемент <a>.

Псевдокласс first-of-type (первый из выбранного типа)

Давайте продолжим с того, на чём остановились — с выбираемого элемента (речь идёт об <a>-теге).

Селектор first-of-type выбирает каждый первый элемент <a> в секции.

Код, который выполняет условия приведённой выше схемы:

			a:first-of-type {
border: 2px solid black;
}
		

Вы можете посмотреть как работает этот код в браузе на codepen.io.

Псевдокласс last-of-type (последний из выбранного типа)

Last-of-type — полная противоположность псевдокласса first-of-type. Этот селектор выбирает последние элементы <a> во всех секциях.

Элемент <a> — первый и последний для секций, в которых он представлен в единственном числе.

Псевдокласс nth-of-type (n-й из выбранного типа)

Теперь переходим к наиболее интересной части статьи. Рассмотрим простой CSS с элементами математики из школьной программы.

Давайте определим следующий стиль, чтобы посмотреть на селектор в действии:

			a:nth-of-type(1) {
border: 2px solid black;
}
		

Вместо nth подставьте число из скобок в порядковой форме, чтобы прочитать правильно селектор.

Итак, давайте вернёмся к селектору. a:nth-of-type(1) может читаться точно так же, как и a:first-of-type. В данном случае эти селекторы работают одинаково: каждый из них выбирает только те элементы <a>, которые являются первыми в своих секциях.

А теперь давайте попробуем кое-что другое:

			a:nth-of-type(0) {
border: 2px solid black;
}
		

Надеюсь, вы догадались, что произошло. Если нет, то объясняю: в этом случае ни один элемент <a> не будет выбран, так как счёт начинается с 1, а не с 0. То же самое произойдёт, если вы напишете a:nth-of-type(5) или a:nth-of-type(6/7/8) вместо a:nth-of-type(0). Это легко можно объяснить: в DOM нет 5-го, 6-го, 7-го или 8-го элемента <a>, поэтому в данном случае селектор ничего не выберет.

А если мы пойдём немного глубже и напишем следующее условие:

			a:nth-of-type(2) {
border: 2px solid black;
}
		

то селектор выберет каждый второй элемент <a> в первой и во второй секциях.

Для полного понимания картины приведу ещё один пример:

			a:nth-of-type(3) {
border: 2px solid black;
}
		

В этом случае будет выбран третий (не перепутайте с a:last-of-type) элемент <a> во второй секции, так как эта секция — единственная, в которой есть три элемента <a>.

Достаточно просто, не так ли? Но вставлять в скобки вы можете не только числа, но и формулы. Например, (a*n)+b (то же самое, что и an + b), где a и b — константы, а n —значение, которое больше или равно нулю. Не переживайте, если вам что-то непонятно, сейчас я всё объясню.

Для начала применим следующий стиль:

			a:nth-of-type(n) {  
border: 2px solid black; 
}
		

Формула, переданная в селектор выглядит следующим образом: (1 * n) + 0 [=n], где а = 1, b = 0, n — переменная. Теперь давайте разберёмся, что идёт дальше. Значение n последовательно вставляется в формулу, начиная с 0, после этого селектор делает выбор. Поэтому a:nth-of-type можно представить следующим образом:

			a:nth-of-type(0) {  border: 2px solid black; } // n = 0
a:nth-of-type(1) {  border: 2px solid black; } // n = 1
a:nth-of-type(2) {  border: 2px solid black; } // n = 2
a:nth-of-type(3) {  border: 2px solid black; } // n = 3
a:nth-of-type(4) {  border: 2px solid black; } // n = 4...
		

В соответствии с данными результатами и будет выбран элемент <a>.

Давайте приведём ещё один пример:

			a:nth-of-type(2n + 1) {  
border: 2px solid black; 
}
		

При постепенном заполнении значений n с нуля будут генерироваться следующие селекторы:

			// n = 0 подразумевает эту операцию: (2 * 0) + 1 = 1
a:nth-of-type(1) { border: 2px solid black; }// n = 1 подразумевает эту операцию:  (2 * 1) + 1 = 3
a:nth-of-type(3) { border: 2px solid black; }// n = 2 подразумевает эту операцию:  (2 * 2) + 1 = 5 – Но в данном случае ни один элемент выбран не будет, так как у нас нет 5-го элемента <a> ни в одной из секций.
a:nth-of-type(5) { border: 2px solid black; }...
		

 

Помимо чисел и формул, генерирующих числа, вы можете также выбирать чётные или нечётные номера элементов. Even выбирает все чётные номера элемента в соответствующих секциях. Предположим, что у нас есть секция с 4-мя элементами <a>. Из этой секции селектор выберет второй и четвёртый элементы <a>. Аналогичным образом селектор работает с нечётными числами, только следует заменить even  на odd — a:nth-of-type(odd) {style}

Псевдокласс nth-last-of-type (n-й с конца из выбранного типа)

Этот селектор работает точно так же, как и предыдущий, но с небольшим отличием:

			a:nth-last-of-type(1) {  
border: 2px solid black; 
}
		

 

 

 

Обратите внимание, что в каждой секции нумерация элементов <a> идёт справа налево. Это и есть единственное отличие от предыдущего селектора. В :last-of-type также можно использовать числа, формулы и выбор по чётным и нечётным номерам элементов. Главное запомнить, что для этого селектора работает обратный отбор, его следует читать справа налево. Иначе говоря, последний элемент превращается в первый, предпоследний превращается во второй и т. д.

На этом часть статьи о селекторах *-of-type подходит к концу. Надеюсь, что вам понравилось. Мы проделали большой путь от only-of-type до nth-last-of-type, углубившись в first-of-type, last-of-type и nth-of-type. Если где-то в середине вы что-то не до конца поняли, то предлагаю вам поэкспериментировать в codepen и перечитать эту часть.

Настало время перейти к новой категории псевдоклассов — *-child. В этой части статьи мы рассмотрим и попытаемся понять, как работает эта категория селекторов. После того, как вы изучили предыдущие селекторы, эта категория псевдоклассов не покажется вам трудной. Давайте начнём!

Псевдокласс only-child (единственный из выбранного типа дочернего элемента)

Примените следующий стиль к HTML-коду, который дан в самом начале .

			a:only-child {
border: 2px solid black;
}
		

Как вы видите, селектор выбрал два <a>-элемента, каждый из которых вложен в элемент «список» li. Почему так и как это работает: only child дословно переводится как «единственный ребёнок / единственный дочерний элемент», поэтому селектор выбирает только те <a>-элементы, у которых родительский элемент имеет только один дочерний элемент —<a>. В нашем случае каждый li имеет по одному родительскому и по одному дочернему элементу, поэтому они и были выбраны.

 

Почему не выбран <a>-элемент, единственный дочерний элемент <a> ненумерованного списка ul? Ответ прост: несмотря на то, что он является единственным дочерним <a>-элементом, у него есть ещё два соседа, не являющихся элементами <a>, для которых ul является родительским. Получается три дочерних элемента, что, разумеется, не попадает под наше правило: один родительский элемент — один дочерний.

Псевдокласс first-child (первый дочерний элемент)

Примените следующий стиль:

			a:first-child {
border: 2px solid black;
}
		

Селектор first-child выбирает все первые дочерние элементы <a> своих родителей. Следует помнить, что он выбирает только первые дочерние <a>-элементы. Это означает, что элемент <a> должен занимать первую позицию в своей секции, а не быть первым после какого-либо другого дочернего элемента.

Если вы не поняли, почему первый дочерний <a>-элемент в body не был выбран, то сейчас объясню. Дело в том, что он является первым только среди дочерних <a>-элементов в этой секции. Среди всех дочерних элементов он — второй, так как первый — это контейнер с классом main.

Псевдокласс last-child (последний дочерний элемент)

Думаю, что после всего материала, который мы изучили, вы примерно понимаете, как работает этот селектор. Если нет — объясняю: last-child выбирает последний элемент <a> в каждой секции. Правило прежнее: <a>-элемент должен быть именно первым в списке всех дочерних элементов, иначе он не будет выбран.

Обратите внимание на схему после кода.

			a:last-child {
border: 2px solid black;
}
		

 

 

Псевдокласс nth-child (n-й дочерний элемент)

Надеюсь, вы усвоили небольшой урок математики в первой части статьи. Потому что сейчас это произойдёт снова, но с небольшими изменениями.

Обратите внимание на следующий пример:

			a:nth-child(1) {  
border: 2px solid black; 
}
		

Всё работает так же, как и с селектором nth-of-type.

Чтобы начать использовать nth-child, нужно повторить все те действия, которые мы произвели с селектором nth-of-type — поместить в скобки любое число. В примере выше селектор сработает точно так же, как и first-child — выберет все первые элементы <a> в секциях.

Итак, давайте перейдём к новому примеру:

			a:nth-child(2n - 1) {  
border: 2px solid black; 
}
		

Постепенно добавляем значения n в формулу начиная с 0, и это даёт нам понять, что селектор сверху во многом схож тем, что ниже.

			// n = 0 Подразумевает эту операцию: (2 * 0) - 1 = 0 - 1 = -1
a:nth-child(-1) { border: 2px solid black; }  | не выбирает ничего// n = 1 подразумевает эту операцию: (2 * 1) - 1 = 2 - 1 = 1
a:nth-child(1) { border: 2px solid black; }// n = 2 подразумевает эту операцию: (2 * 2) - 1 = 4 - 1 = 3
a:nth-child(3) { border: 2px solid black; }// n = 3 подразумевает эту операцию: (2 * 3) - 1 = 6 - 1 = 5
a:nth-child(5) { border: 2px solid black; } | не выбирает ничего
		

Если селектор получает числа, выходящие за пределы секторов (как -1, 5, 6), он просто их игнорирует.

На схеме ниже показано действие селектора a:nth-child(2n-1):

На сайте CSS Tricks вы сможете найти очень полезную и исчерпывающую статью о селекторе :nth-child.

А пока что давайте перейдём к последнему селектору в нашем руководстве.

Псевдокласс nth-last-child (n-й дочерний элемент с конца)

Этот селектор работает точно так же, как и :nth-child, за исключением одной тонкости: он видит и читает дерево DOM в обратном направлении — справа налево.

Дочерние элементы в каждой секции для этого селектора выстраиваются в ряд справа налево.

			a:nth-last-child(1) {  
border: 2px solid black; 
}
		

Обратите внимание на то, как расположены и выбраны дочерние элементы на схеме:

 

Как видите, всё довольно просто — выбор элемента происходит с конца.

Заключение

На этом наше знакомство с псевдоклассами окончено. Но вы всегда можете найти полезный материал по CSS у нас на сайте. Желаем вам удачи в освоении CSS!

Следите за новыми постами
Следите за новыми постами по любимым темам
17К открытий17К показов