← HTML & CSS/Псевдоклассы и псевдоэлементы#29 из 383← ПредыдущийСледующий →+15 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

Псевдоклассы и псевдоэлементы

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

Псевдоклассы — состояния элемента

Псевдоклассы начинаются с одного двоеточия :. Они описывают состояние или позицию элемента.

Интерактивные состояния

/* :hover — при наведении мыши */
.btn:hover { background-color: #6b21d4; }
a:hover { text-decoration: underline; }

/* :focus — при фокусе (Tab или клик) */
input:focus { border-color: #7b2ff7; outline: 2px solid rgba(123,47,247,0.3); }
button:focus { outline: 3px solid #7b2ff7; }

/* :active — в момент нажатия */
.btn:active { transform: scale(0.97); }

/* :visited — посещённые ссылки */
a:visited { color: #6b21d4; }

Состояния форм

input:disabled { opacity: 0.5; cursor: not-allowed; }
input:checked { accent-color: #7b2ff7; }
input:invalid { border-color: #e53e3e; }
input:valid { border-color: #38a169; }
input:placeholder-shown { border-color: #e2e8f0; }
input:required { border-left: 3px solid #e53e3e; }

Структурные псевдоклассы

/* Первый и последний дочерний элемент */
li:first-child { border-top: none; }
li:last-child  { border-bottom: none; }

/* nth-child(n) — каждый n-й элемент */
tr:nth-child(even) { background: #f7fafc; }  /* Чётные строки таблицы */
tr:nth-child(odd)  { background: white; }    /* Нечётные строки */
li:nth-child(3)    { font-weight: bold; }    /* Только третий */
li:nth-child(3n)   { color: red; }           /* Каждый третий */

/* Единственный ребёнок */
p:only-child { margin: 0; }

/* :not() — исключение */
.btn:not(.btn-disabled) { cursor: pointer; }
li:not(:last-child) { border-bottom: 1px solid #eee; }

Псевдоэлементы — виртуальные элементы

Псевдоэлементы начинаются с двух двоеточий ::. Они создают виртуальные дочерние элементы, которые не существуют в HTML.

::before и ::after

Вставляют виртуальный элемент до/после содержимого. Требуют свойство content.

/* Значок перед ссылкой */
.external-link::after {
  content: ' ↗';
  font-size: 0.8em;
  opacity: 0.7;
}

/* Декоративная линия под заголовком */
.section-title::after {
  content: '';  /* Пустой контент — только для декора */
  display: block;
  width: 40px;
  height: 3px;
  background: #7b2ff7;
  margin-top: 8px;
}

/* Счётчик */
.price::before {
  content: '₽ ';
}

/* Кавычки для цитат */
blockquote::before { content: '«'; }
blockquote::after  { content: '»'; }

Другие псевдоэлементы

/* ::placeholder — стиль для placeholder текста */
input::placeholder {
  color: #a0aec0;
  font-style: italic;
}

/* ::selection — стиль выделения текста */
::selection {
  background: #7b2ff7;
  color: white;
}

/* ::first-line — первая строка текста */
p::first-line {
  font-weight: bold;
  font-size: 1.1em;
}

Практические паттерны

Кнопка с hover-эффектом

.btn {
  background: #7b2ff7;
  transition: background 0.2s, transform 0.1s;
}
.btn:hover  { background: #6b21d4; }
.btn:active { transform: scale(0.97); }
.btn:focus  { outline: 3px solid rgba(123,47,247,0.4); outline-offset: 2px; }

Подчёркивание через ::after (анимированное)

.nav-link { position: relative; }
.nav-link::after {
  content: '';
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 2px;
  background: #7b2ff7;
  transition: width 0.3s;
}
.nav-link:hover::after { width: 100%; }

Типичные ошибки

Ошибка 1: Забыть content у ::before/::after

.el::before { display: block; width: 10px; }  /* Не отображается — нет content! */
.el::before { content: ''; display: block; width: 10px; }  /* Работает */

Ошибка 2: Одно двоеточие у псевдоэлементов

p:before { }   /* Старый синтаксис, работает, но не стандарт */
p::before { }  /* Правильно для CSS3+ */

Ошибка 3: :nth-child считает от родителя, а не от типа

/* <div> <p> <span> <p> </div> */
p:nth-child(2) { }  /* Выбирает <span>, потому что span — второй ребёнок div! */
p:nth-of-type(2) { }  /* Выбирает второй <p> — правильно */

В реальных проектах

Псевдоклассы — основа интерактивности без JavaScript. Hover-эффекты, состояния форм, полоска активной вкладки — всё это CSS. ::before и ::after часто заменяют лишние HTML-теги: декоративные линии, иконки, перекрытия (overlay).

Примеры

Псевдоклассы через JavaScript: состояния, nth-child и псевдоэлементы

// CSS с псевдоклассами и псевдоэлементами
const style = document.createElement('style')
style.textContent = `
  * { box-sizing: border-box; }
  body { font-family: Arial, sans-serif; padding: 16px; }

  /* Кнопка с состояниями */
  .btn {
    background: #7b2ff7;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.2s, transform 0.1s;
    margin: 4px;
  }
  .btn:hover { background: #6b21d4; }
  .btn:active { transform: scale(0.97); }
  .btn:focus { outline: 3px solid rgba(123,47,247,0.4); outline-offset: 2px; }
  .btn:disabled { opacity: 0.5; cursor: not-allowed; }

  /* Input с состояниями */
  .input {
    border: 2px solid #e2e8f0;
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 14px;
    width: 200px;
    display: block;
    margin: 8px 0;
    transition: border-color 0.2s;
  }
  .input:focus { border-color: #7b2ff7; outline: none; }
  .input::placeholder { color: #a0aec0; font-style: italic; }

  /* Список с nth-child */
  .list { list-style: none; padding: 0; margin: 16px 0; }
  .list li {
    padding: 8px 12px;
    border-bottom: 1px solid #e2e8f0;
  }
  .list li:nth-child(odd) { background: #f7fafc; }
  .list li:first-child { border-radius: 8px 8px 0 0; font-weight: bold; }
  .list li:last-child { border-bottom: none; border-radius: 0 0 8px 8px; }
  .list li:not(:first-child):hover { background: #ebf4ff; cursor: pointer; }

  /* Псевдоэлемент ::before для иконки */
  .price::before { content: '₽ '; color: #718096; }

  /* Декоративная линия через ::after */
  .section-title {
    display: inline-block;
    font-weight: 700;
    margin-bottom: 12px;
  }
  .section-title::after {
    content: '';
    display: block;
    width: 100%;
    height: 3px;
    background: #7b2ff7;
    margin-top: 4px;
    border-radius: 2px;
  }
`
document.head.appendChild(style)

// Создаём компоненты
const title = document.createElement('h3')
title.className = 'section-title'
title.textContent = 'Интерактивные кнопки'
document.body.appendChild(title)

const btn1 = document.createElement('button')
btn1.className = 'btn'
btn1.textContent = 'Активная'

const btn2 = document.createElement('button')
btn2.className = 'btn'
btn2.textContent = 'Неактивная'
btn2.disabled = true

document.body.appendChild(btn1)
document.body.appendChild(btn2)

// Инпут
const input = document.createElement('input')
input.className = 'input'
input.placeholder = 'Введите имя...'
input.type = 'text'
document.body.appendChild(input)

// Список с nth-child
const ul = document.createElement('ul')
ul.className = 'list'
;['Заголовок', 'Первый', 'Второй', 'Третий', 'Четвёртый'].forEach(text => {
  const li = document.createElement('li')
  li.textContent = text
  ul.appendChild(li)
})
document.body.appendChild(ul)

// Цена с ::before
const price = document.createElement('div')
price.className = 'price'
price.style.fontSize = '20px'
price.style.fontWeight = '700'
price.textContent = '1 490'
document.body.appendChild(price)

// Логирование
console.log('btn1.disabled:', btn1.disabled)   // false
console.log('btn2.disabled:', btn2.disabled)   // true
console.log('Кол-во li:', ul.children.length)  // 5

// Проверяем :nth-child через querySelectorAll
const oddItems = ul.querySelectorAll('li:nth-child(odd)')
console.log('Нечётных li:', oddItems.length)   // 3 (1-й, 3-й, 5-й)

Псевдоклассы и псевдоэлементы

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

Псевдоклассы — состояния элемента

Псевдоклассы начинаются с одного двоеточия :. Они описывают состояние или позицию элемента.

Интерактивные состояния

/* :hover — при наведении мыши */
.btn:hover { background-color: #6b21d4; }
a:hover { text-decoration: underline; }

/* :focus — при фокусе (Tab или клик) */
input:focus { border-color: #7b2ff7; outline: 2px solid rgba(123,47,247,0.3); }
button:focus { outline: 3px solid #7b2ff7; }

/* :active — в момент нажатия */
.btn:active { transform: scale(0.97); }

/* :visited — посещённые ссылки */
a:visited { color: #6b21d4; }

Состояния форм

input:disabled { opacity: 0.5; cursor: not-allowed; }
input:checked { accent-color: #7b2ff7; }
input:invalid { border-color: #e53e3e; }
input:valid { border-color: #38a169; }
input:placeholder-shown { border-color: #e2e8f0; }
input:required { border-left: 3px solid #e53e3e; }

Структурные псевдоклассы

/* Первый и последний дочерний элемент */
li:first-child { border-top: none; }
li:last-child  { border-bottom: none; }

/* nth-child(n) — каждый n-й элемент */
tr:nth-child(even) { background: #f7fafc; }  /* Чётные строки таблицы */
tr:nth-child(odd)  { background: white; }    /* Нечётные строки */
li:nth-child(3)    { font-weight: bold; }    /* Только третий */
li:nth-child(3n)   { color: red; }           /* Каждый третий */

/* Единственный ребёнок */
p:only-child { margin: 0; }

/* :not() — исключение */
.btn:not(.btn-disabled) { cursor: pointer; }
li:not(:last-child) { border-bottom: 1px solid #eee; }

Псевдоэлементы — виртуальные элементы

Псевдоэлементы начинаются с двух двоеточий ::. Они создают виртуальные дочерние элементы, которые не существуют в HTML.

::before и ::after

Вставляют виртуальный элемент до/после содержимого. Требуют свойство content.

/* Значок перед ссылкой */
.external-link::after {
  content: ' ↗';
  font-size: 0.8em;
  opacity: 0.7;
}

/* Декоративная линия под заголовком */
.section-title::after {
  content: '';  /* Пустой контент — только для декора */
  display: block;
  width: 40px;
  height: 3px;
  background: #7b2ff7;
  margin-top: 8px;
}

/* Счётчик */
.price::before {
  content: '₽ ';
}

/* Кавычки для цитат */
blockquote::before { content: '«'; }
blockquote::after  { content: '»'; }

Другие псевдоэлементы

/* ::placeholder — стиль для placeholder текста */
input::placeholder {
  color: #a0aec0;
  font-style: italic;
}

/* ::selection — стиль выделения текста */
::selection {
  background: #7b2ff7;
  color: white;
}

/* ::first-line — первая строка текста */
p::first-line {
  font-weight: bold;
  font-size: 1.1em;
}

Практические паттерны

Кнопка с hover-эффектом

.btn {
  background: #7b2ff7;
  transition: background 0.2s, transform 0.1s;
}
.btn:hover  { background: #6b21d4; }
.btn:active { transform: scale(0.97); }
.btn:focus  { outline: 3px solid rgba(123,47,247,0.4); outline-offset: 2px; }

Подчёркивание через ::after (анимированное)

.nav-link { position: relative; }
.nav-link::after {
  content: '';
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 2px;
  background: #7b2ff7;
  transition: width 0.3s;
}
.nav-link:hover::after { width: 100%; }

Типичные ошибки

Ошибка 1: Забыть content у ::before/::after

.el::before { display: block; width: 10px; }  /* Не отображается — нет content! */
.el::before { content: ''; display: block; width: 10px; }  /* Работает */

Ошибка 2: Одно двоеточие у псевдоэлементов

p:before { }   /* Старый синтаксис, работает, но не стандарт */
p::before { }  /* Правильно для CSS3+ */

Ошибка 3: :nth-child считает от родителя, а не от типа

/* <div> <p> <span> <p> </div> */
p:nth-child(2) { }  /* Выбирает <span>, потому что span — второй ребёнок div! */
p:nth-of-type(2) { }  /* Выбирает второй <p> — правильно */

В реальных проектах

Псевдоклассы — основа интерактивности без JavaScript. Hover-эффекты, состояния форм, полоска активной вкладки — всё это CSS. ::before и ::after часто заменяют лишние HTML-теги: декоративные линии, иконки, перекрытия (overlay).

Примеры

Псевдоклассы через JavaScript: состояния, nth-child и псевдоэлементы

// CSS с псевдоклассами и псевдоэлементами
const style = document.createElement('style')
style.textContent = `
  * { box-sizing: border-box; }
  body { font-family: Arial, sans-serif; padding: 16px; }

  /* Кнопка с состояниями */
  .btn {
    background: #7b2ff7;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.2s, transform 0.1s;
    margin: 4px;
  }
  .btn:hover { background: #6b21d4; }
  .btn:active { transform: scale(0.97); }
  .btn:focus { outline: 3px solid rgba(123,47,247,0.4); outline-offset: 2px; }
  .btn:disabled { opacity: 0.5; cursor: not-allowed; }

  /* Input с состояниями */
  .input {
    border: 2px solid #e2e8f0;
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 14px;
    width: 200px;
    display: block;
    margin: 8px 0;
    transition: border-color 0.2s;
  }
  .input:focus { border-color: #7b2ff7; outline: none; }
  .input::placeholder { color: #a0aec0; font-style: italic; }

  /* Список с nth-child */
  .list { list-style: none; padding: 0; margin: 16px 0; }
  .list li {
    padding: 8px 12px;
    border-bottom: 1px solid #e2e8f0;
  }
  .list li:nth-child(odd) { background: #f7fafc; }
  .list li:first-child { border-radius: 8px 8px 0 0; font-weight: bold; }
  .list li:last-child { border-bottom: none; border-radius: 0 0 8px 8px; }
  .list li:not(:first-child):hover { background: #ebf4ff; cursor: pointer; }

  /* Псевдоэлемент ::before для иконки */
  .price::before { content: '₽ '; color: #718096; }

  /* Декоративная линия через ::after */
  .section-title {
    display: inline-block;
    font-weight: 700;
    margin-bottom: 12px;
  }
  .section-title::after {
    content: '';
    display: block;
    width: 100%;
    height: 3px;
    background: #7b2ff7;
    margin-top: 4px;
    border-radius: 2px;
  }
`
document.head.appendChild(style)

// Создаём компоненты
const title = document.createElement('h3')
title.className = 'section-title'
title.textContent = 'Интерактивные кнопки'
document.body.appendChild(title)

const btn1 = document.createElement('button')
btn1.className = 'btn'
btn1.textContent = 'Активная'

const btn2 = document.createElement('button')
btn2.className = 'btn'
btn2.textContent = 'Неактивная'
btn2.disabled = true

document.body.appendChild(btn1)
document.body.appendChild(btn2)

// Инпут
const input = document.createElement('input')
input.className = 'input'
input.placeholder = 'Введите имя...'
input.type = 'text'
document.body.appendChild(input)

// Список с nth-child
const ul = document.createElement('ul')
ul.className = 'list'
;['Заголовок', 'Первый', 'Второй', 'Третий', 'Четвёртый'].forEach(text => {
  const li = document.createElement('li')
  li.textContent = text
  ul.appendChild(li)
})
document.body.appendChild(ul)

// Цена с ::before
const price = document.createElement('div')
price.className = 'price'
price.style.fontSize = '20px'
price.style.fontWeight = '700'
price.textContent = '1 490'
document.body.appendChild(price)

// Логирование
console.log('btn1.disabled:', btn1.disabled)   // false
console.log('btn2.disabled:', btn2.disabled)   // true
console.log('Кол-во li:', ul.children.length)  // 5

// Проверяем :nth-child через querySelectorAll
const oddItems = ul.querySelectorAll('li:nth-child(odd)')
console.log('Нечётных li:', oddItems.length)   // 3 (1-й, 3-й, 5-й)

Задание

Создай список из 5 пунктов и стилизуй его псевдоклассами: нечётные строки (`:nth-child(odd)`) — светло-синий фон, последний пункт (`:last-child`) — красный цвет текста, первый пункт (`:first-child`) — жирный шрифт. Добавь кнопке эффект наведения через `:hover` и нажатия через `:active`.

Подсказка

`:nth-child(odd)` выбирает нечётные элементы: 1-й, 3-й, 5-й. Для чётного фона используй `#ebf4ff`. `:last-child` — последний элемент, задай `color: #e53e3e`. `:hover` — потемни фон кнопки, `:active` — уменьши через `scale(0.97)`.

Загружаем среду выполнения...
Загружаем AI-помощника...