Псевдоэлементы

Вы не создавали этот элемент? А он есть! Всё благодаря CSS.

Время чтения: 6 мин

Кратко

Скопировано

Псевдоэлементы — это элементы, которых не существует в HTML-разметке. Они создаются и позиционируются исключительно при помощи CSS. Чаще всего используются для создания различных декоративных элементов (которые не несут содержательного смысла).

Также псевдоэлементы приходят на помощь, когда нужно наложить поверх картинки так называемый оверлей (перекрывающий слой). На этом польза от псевдоэлементов не заканчивается.

Пример

Скопировано

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

        
          
          <header class="header">  <h1 class="header__title">Good evening, Clarice.</h1></header>
          <header class="header">
  <h1 class="header__title">Good evening, Clarice.</h1>
</header>

        
        
          
        
      
        
          
          .header {  background: #999999    url("background.svg")    no-repeat center / cover;}.header__title {  color: #ffffff;  font-size: 82px;  text-transform: uppercase;  text-align: center;}
          .header {
  background: #999999
    url("background.svg")
    no-repeat center / cover;
}

.header__title {
  color: #ffffff;
  font-size: 82px;
  text-transform: uppercase;
  text-align: center;
}

        
        
          
        
      
Открыть демо в новой вкладке

Чтобы цвет картинки был не таким ярким и текст лучше читался, наложим поверх всего header полупрозрачную заливку чёрного цвета.

Добавляем позиционирование родителю и указываем порядок наложения:

        
          
          .header {  position: relative;  z-index: 0;}
          .header {
  position: relative;
  z-index: 0;
}

        
        
          
        
      

content — обязательное свойство псевдоэлементов ::before и ::after. Позиционируем псевдоэлемент относительно родителя и растягиваем на всю ширину и высоту. После чего задаём полупрозрачный чёрный фон и убираем под текст при помощи z-index:

        
          
          .header::before {  content: "";  position: absolute;  top: 0;  left: 0;  width: 100%;  height: 100%;  background-color: rgba(0 0 0 / 0.7);  z-index: -1;}
          .header::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0 0 0 / 0.7);
  z-index: -1;
}

        
        
          
        
      
Открыть демо в новой вкладке

Не трогая HTML-разметку, мы добавили ещё один элемент в декоративных целях. Разметка осталась чистой. Псевдоэлементом легко управлять, добавлять или удалять его по необходимости, даже если нет доступа к HTML.

Как понять

Скопировано

Определение приставки «псевдо» в словаре: «...Приставка, означающая ложность, ненастоящесть следующего за ней».

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

Как пишется

Скопировано

Существует несколько псевдоэлементов. Рассмотрим каждый из них.

::before и ::after

Скопировано

Два самых часто встречающихся псевдоэлемента. Они очень похожи. Единственная разница заключается в том, что ::before по умолчанию располагается перед содержимым элемента, для которого задаётся, а ::after — после. Эта разница отражена в названии: слово before переводится с английского как «до» или «перед»; слово after переводится как «после».

Для обоих элементов обязательным является свойство content — содержимое псевдоэлемента. С его помощью можно, например, вставить какое-нибудь слово до или после текста. Без свойства content псевдоэлемент не отобразится.

Создадим текстовый элемент с именем пользователя.

        
          
          <span class="incoming">Гордон</span>
          <span class="incoming">Гордон</span>

        
        
          
        
      

Поприветствуем пользователя и скажем ему комплимент:

        
          
          .incoming::before {  content: "Привет, ";}.incoming::after {  content: ", ты отлично выглядишь!";}
          .incoming::before {
  content: "Привет, ";
}

.incoming::after {
  content: ", ты отлично выглядишь!";
}

        
        
          
        
      
Открыть демо в новой вкладке

Как видно в примере, текст из свойства content псевдоэлемента ::before встал перед именем пользователя, а из псевдоэлемента ::after — после.

Эти псевдоэлементы по умолчанию являются строчными. Но это легко изменить при помощи стилей.

Подчеркнём декоративной линией текст.

Зададим родителю строчно-блочное отображение, чтобы линия была по ширине текста:

        
          
          .incoming {  display: inline-block;}.incoming::before {  content: "Привет, ";}.incoming::after {  content: "";  display: block;  width: 100%;  height: 2px;  background-color: #F498AD;}
          .incoming {
  display: inline-block;
}

.incoming::before {
  content: "Привет, ";
}

.incoming::after {
  content: "";
  display: block;
  width: 100%;
  height: 2px;
  background-color: #F498AD;
}

        
        
          
        
      
Открыть демо в новой вкладке

::first-letter

Скопировано

При помощи этого псевдоэлемента можно выбрать первую букву в строке или абзаце текста. Это позволяет создать так называемую буквицу — большую декоративную букву в начале текста.

        
          
          <p class="text">  В траве, около высоких муравейников...</p>
          <p class="text">
  В траве, около высоких муравейников...
</p>

        
        
          
        
      

Увеличиваем размер первой буквы и красим её в красный цвет:

        
          
          .text::first-letter {  font-size: 52px;  color: #F498AD;}
          .text::first-letter {
  font-size: 52px;
  color: #F498AD;
}

        
        
          
        
      
Открыть демо в новой вкладке

::first-line

Скопировано

Этот псевдоэлемент выбирает первую строку текста. Обратите внимание, что он сработает только для блочных элементов. Со строчными этот трюк не пройдёт.

Например, можно сделать «красную строку», правда, не так, как учили в школе =)

        
          
          <p class="text">  В далекой и бледной глубине неба...</p>
          <p class="text">
  В далекой и бледной глубине неба...
</p>

        
        
          
        
      

Красим первую строку в красный:

        
          
          .text::first-line {  background-color: #F498AD;}
          .text::first-line {
  background-color: #F498AD;
}

        
        
          
        
      
Открыть демо в новой вкладке

::selection

Скопировано

С помощью псевдоэлемента ::selection можно управлять стилем текста, который пользователь выделяет при помощи мыши.

Внешний вид псевдоэлемента ::selection

Текст на сайте Smashing Magazine выделяется красным цветом. Использование псевдоэлемента ::selection позволяет выдержать стиль оформления во всём, вплоть до такой мелочи, как выделение текста.

Целесообразнее всего задавать этот псевдоэлемент не какому-то отдельному блоку, а всей странице целиком.

        
          
          ::selection {  background-color: #F498AD;}
          ::selection {
  background-color: #F498AD;
}

        
        
          
        
      
Открыть демо в новой вкладке

Если выделить текст в этом примере, то станет видно, что фон выделения ярко-красного цвета.

::placeholder

Скопировано

Он позволяет стилизовать подсказку, которая выводится в поле ввода текста (<input>).

Текст подсказки задаётся при помощи атрибута placeholder у тега <input>.

        
          
          <input type="email" placeholder="test@example.com">
          <input type="email" placeholder="test@example.com">

        
        
          
        
      
Внешний вид псевдоэлемента ::placeholder

По умолчанию цвет текста подсказки серый. Но иногда по дизайну требуется другой цвет.

Используйте псевдоэлемент ::placeholder и задайте нужные стили для подсказки. Можно изменить всё, вплоть до шрифта. При этом стили текста, который будет вводить пользователь, не будут затронуты.

        
          
          input::placeholder {  color: #2E9AFF;}
          input::placeholder {
  color: #2E9AFF;
}

        
        
          
        
      
Открыть демо в новой вкладке

::marker

Скопировано

::marker — псевдоэлемент, отвечающий за маркерное поле. В нём находятся, например, маркеры списка.

Подсказки

Скопировано

💡 Обязательно проверяйте поддержку псевдоэлемента в нужных браузерах. Для этого можно использовать сайт Can I use.

💡 Для свойства content есть несколько полезных трюков. Например, можно с помощью data-атрибута у тега (которому можно задать значение при помощи JavaScript) и значения attr(data-атрибут) вывести количество непрочитанных сообщений на лейбле. Или количество товаров в корзине. Такой способ будет «дешевле» в том плане, что реальная HTML-разметка не изменяется.

💡 В качестве значения свойства content можно вставлять юникод. Например, можно добавить символ копирайта при помощи записи content: "&#169;".

💡 Можно встретить в коде написание псевдоэлементов с одним двоеточием в начале. Такой синтаксис допустим почти всегда, кроме как с псевдоэлементами ::selection и ::placeholder. Но в последних редакциях спецификации рекомендуется писать все псевдоэлементы с двумя двоеточиями, чтобы визуально отделить их от псевдоклассов.

На практике

Скопировано

Андрей Пилюгин советует

Скопировано

🛠 Так как псевдоэлементы ::before и ::after отсутствуют в дереве DOM, то невозможно на них повесить JavaScript-событие. В большинстве случаев достаточно отслеживать события на самом элементе и через него менять свойства псевдоэлемента. Если необходимо отследить, например, клик именно по псевдоэлементу, то можно использовать хак со сравнением offsetX.

Например, создадим управляющий контрол, позволяющий менять количество товаров в корзине. При этом кнопки «+» и «-» у нас будут псевдоэлементами.

        
          
          <span class="quantity-change">0</span>
          <span class="quantity-change">0</span>

        
        
          
        
      

Зададим стили для поля ввода:

        
          
          .quantity-change {  display: flex;  align-items: center;  justify-content: space-between;  width: 160px;  height: 44px;  border-radius: 6px;  background-color: #2E9AFF;  color: #000000;  font-size: 24px;  cursor: text;  user-select: none;}
          .quantity-change {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 160px;
  height: 44px;
  border-radius: 6px;
  background-color: #2E9AFF;
  color: #000000;
  font-size: 24px;
  cursor: text;
  user-select: none;
}

        
        
          
        
      

Для данного хака нужно внимательно следить за шириной элемента, так как событие будет вычисляться именно по ширине. В данном случае ширина элемента 160 px.

Управляющие контролы (в роли которых у нас выступят ::before и ::after) будут по 40 px:

        
          
          .quantity-change::before, .quantity-change::after {  display: flex;  align-items: center;  justify-content: center;  width: 40px;  font-size: 34px;  cursor: pointer;}.quantity-change::before {  content: '–';}.quantity-change::after {  content: '+';}
          .quantity-change::before, .quantity-change::after {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  font-size: 34px;
  cursor: pointer;
}

.quantity-change::before {
  content: '–';
}

.quantity-change::after {
  content: '+';
}

        
        
          
        
      

Теперь, зная ширину элементов и контролов, считаем, что нажатие первых 40 px является событием для ::before, а нажатие последних 40 px — для ::after:

        
          
          const quantity = document.querySelector('.quantity-change')let counter = Number(quantity.textContent)quantity.addEventListener('click', (event) => {  if(event.offsetX <= 40 && counter > 0) {    counter--  } else if(event.offsetX >= 120) {    counter++  }  quantity.textContent = counter})
          const quantity = document.querySelector('.quantity-change')
let counter = Number(quantity.textContent)

quantity.addEventListener('click', (event) => {
  if(event.offsetX <= 40 && counter > 0) {
    counter--
  } else if(event.offsetX >= 120) {
    counter++
  }
  quantity.textContent = counter
})

        
        
          
        
      

Бинго! Теперь мы можем отследить нажатия на псевдоэлементы и выполнить нужные действия.

Открыть демо в новой вкладке

Данный метод стоит применять с осторожностью, с оглядкой на медиавыражения, которые могут изменить размеры элемента и всё сломать. Если есть возможность спроектировать свой код без связки «Псевдоэлементы + JavaScript», то это будет хорошим решением.

Алёна Батицкая советует

Скопировано

🛠 Очень интересный и полезный трюк — задавать кастомный счётчик спискам. Бывает необходимость добавить скобки после цифры вместо стандартной точки.

        
          
          <ol class="list">  <li class="list__item">Определение.</li>  <li class="list__item">Свойства.</li>  <li class="list__item">Списки в языках программирования.</li>  <li class="list__item">См. также.</li>  <li class="list__item">Примечания.</li></ol>
          <ol class="list">
  <li class="list__item">Определение.</li>
  <li class="list__item">Свойства.</li>
  <li class="list__item">Списки в языках программирования.</li>
  <li class="list__item">См. также.</li>
  <li class="list__item">Примечания.</li>
</ol>

        
        
          
        
      

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

        
          
          .list {  list-style-type: none;}
          .list {
  list-style-type: none;
}

        
        
          
        
      

Теперь придумаем и пропишем имя нового счётчика. Имя счётчика будет использоваться дальше.

        
          
          .list {  list-style-type: none;  counter-reset: new-counter;}
          .list {
  list-style-type: none;
  counter-reset: new-counter;
}

        
        
          
        
      

Говорим браузеру, что считать нужно именно пункты списка:

        
          
          .list__item {  counter-increment: new-counter;}
          .list__item {
  counter-increment: new-counter;
}

        
        
          
        
      

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

        
          
          .list__item::before {  content: counter(new-counter) ") ";}
          .list__item::before {
  content: counter(new-counter) ") ";
}

        
        
          
        
      
Открыть демо в новой вкладке

Подобным образом можно менять стили маркеров как заблагорассудится.

🛠 Псевдоэлементы — лучшее, что придумали в CSS 😆 С ними жить и верстать гораздо проще. Уделите время и поищите интересные трюки и фишки, которые можно сделать при помощи псевдоэлементов.

Эти маленькие друзья верстальщика могут удивить вас своим могуществом.

Их возможности заслуживают отдельной статьи. Например, такой, которую написал Крис Койер: «A Whole Bunch of Amazing Stuff Pseudo Elements Can Do».

На собеседовании

Скопировано
Задать вопрос в рубрику
🤚 Я знаю ответ

Это вопрос без ответа. Вы можете помочь! Почитайте о том, как контрибьютить в Доку.