Псевдоклассы - это часть селектора после двоеточия, которая описывает не тег и не класс в разметке, а состояние или положение элемента: наведён ли на него курсор, в фокусе ли он, каким по счёту стоит среди соседей. В HTML для этого ничего добавлять не нужно - псевдокласс сам следит за нужным условием.
В этом уроке
Попробуй прямо в этом окне: наведи курсор на кнопку - она сразу поменяет цвет, а как только уберёшь курсор, вернётся обратно, без единой строчки JavaScript. Кликни в поле ввода - вокруг появится рамка, это работает :focus. А среди пунктов списка жирным окажется именно "Второй" - это ровно второй элемент среди соседей, к которому применилось правило li:nth-child(2).
:hover - при наведении курсора
Псевдокласс :hover применяет стиль, пока указатель мыши находится над элементом. Раньше он работал только со ссылками, но сейчас - с любым элементом на странице: кнопкой, картинкой, целым блоком.
button:hover { background: dodgerblue; } - кнопка меняет цвет, пока курсор над ней, и без всякого JavaScript.
.menu-item:hover .submenu { display: block; } как единственный способ показать важное меню - на сенсорных экранах :hover срабатывает непредсказуемо: может не сработать вообще, сработать ненадолго после касания или "залипнуть" после того, как палец уже убрали.
:focus - когда элемент в фокусе
Псевдокласс :focus применяется, пока элемент находится в фокусе - то есть готов принимать ввод. Элемент попадает в фокус кликом мыши или клавишей Tab, что особенно важно для тех, кто пользуется сайтом с клавиатуры, а не мышью.
input:focus { outline: 3px solid tomato; } - явно показывает, какое поле сейчас активно.
*:focus { outline: none; } без какой-либо замены - убирает стандартную рамку фокуса у браузера, и человек, который перемещается по странице клавишей Tab, перестаёт видеть, на каком элементе он сейчас находится.
:nth-child() - по порядковому номеру
Псевдокласс :nth-child(N) выбирает элемент, который стоит N-м по счёту СРЕДИ ВСЕХ соседних элементов у одного родителя - независимо от тега. Вместо конкретного числа можно писать odd (нечётные) или even (чётные) - удобно, например, для полосатых таблиц.
tr:nth-child(odd) { background: #eee; } - классическая "зебра" для строк таблицы: подсвечивает нечётные строки.
p:nth-child(2), ожидая, что это "второй абзац" - на самом деле nth-child считает ВСЕХ соседей подряд, а не только абзацы. Если перед абзацами стоит заголовок, вторым ребёнком окажется первый же абзац, а не второй. Чтобы выбрать именно "второй элемент именно этого тега", нужен другой псевдокласс - :nth-of-type(2).
И другие полезные псевдоклассы
Кроме трёх разобранных выше, часто пригождаются ещё несколько.
li:first-child { margin-top: 0; } - выбирает самый первый элемент среди соседей, удобно убрать лишний отступ у первого пункта списка.
li:last-child { border-bottom: none; } - выбирает самый последний элемент, например чтобы убрать нижнюю границу у последней строки списка.
li:not(.active) { opacity: 0.6; } - выбирает всё, КРОМЕ элементов с классом active, - удобно, когда проще описать исключение, чем перечислять всё остальное.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Делать важный контент доступным только через :hover | На сенсорных экранах у :hover нет надёжного эквивалента - контент может стать неудобным или вовсе недоступным | Дублировать доступ к важному контенту кликом/тапом, а не только наведением |
| Писать outline: none; для :focus без какой-либо замены | Пользователь, который ходит по странице клавишей Tab, перестаёт видеть, на каком элементе он находится сейчас | Если стандартная рамка не устраивает по дизайну, заменить её своей - но не убирать совсем |
| Путать :nth-child(N) с "N-м элементом этого же тега" | :nth-child(N) считает ВСЕХ соседей подряд, независимо от тега - легко ошибиться в расчёте, если среди соседей есть элементы другого типа | Для "N-й элемент именно такого тега" использовать :nth-of-type(N) |
| Думать, что :hover/:focus/:active работают только со ссылками | Ещё с CSS2 1998 года они применяются к любому элементу - кнопке, картинке, целому блоку | Смело использовать эти псевдоклассы с любыми элементами, не только с <a> |
Шпаргалка
| Псевдокласс | Когда срабатывает |
|---|---|
| :hover | Пока курсор наведён на элемент |
| :focus | Пока элемент в фокусе - клик или клавиша Tab |
| :nth-child(N) | N-й по счёту среди ВСЕХ соседей, независимо от тега |
| :nth-child(odd/even) | Все нечётные/чётные по счёту соседи - удобно для "зебры" |
| :first-child / :last-child | Самый первый / самый последний среди соседей |
| :not(селектор) | Все элементы, КРОМЕ подходящих под указанный селектор |
Коротко: отвечаем на вопросы
В чём разница между :nth-child и :nth-of-type?
:nth-child(2) выбирает элемент, если он одновременно и нужный тег, и второй по счёту среди ВСЕХ соседей. :nth-of-type(2) считает только элементы того же тега и не сбивается, если рядом есть элементы другого типа - например, второй именно <p>, даже если перед абзацами есть заголовок.
Почему :hover плохо работает на телефоне?
На сенсорных экранах нет наведения курсора в привычном смысле - в зависимости от браузера :hover может вообще не сработать, сработать ненадолго после касания или "залипнуть" уже после того, как палец убрали. Поэтому важный контент нельзя делать доступным только через :hover.
Можно ли комбинировать псевдокласс с обычным селектором, например с классом?
Да, псевдокласс просто дописывается через двоеточие сразу после остальной части селектора: .btn:hover сработает только для элемента с классом btn, когда на него навели курсор.
Обязательно ли убирать рамку фокуса, если она не нравится по дизайну?
Нет, и лучше не убирать совсем - если стандартная рамка браузера не подходит по стилю, её стоит заменить своей собственной через :focus, а не просто спрятать через outline: none.