Псевдокласс :target позволяет применять CSS к элементу, на который указывает фрагмент URL — часть адреса после символа #.
На первый взгляд это всего лишь удобный способ стилизовать так называемый якорь. Но на практике :target можно использовать гораздо интереснее: создавать вкладки, раскрывающиеся блоки, галереи и даже простые модальные окна без JavaScript.
В этой статье разберём :target от простого примера до таких CSS-only компонентов.
Здесь мы разберем такие примеры:
- Что такое псевдокласс :target
- Простой пример создания навигации на основе :target
- Используем :target для показа и скрытия элементов
- Вкладки/табы на :target
- Аккордеон на основе :target
- Что такое псевдокласс :target
- Галерея изображений без JavaScript
- Превращаем галерею в lightbox
- Popup-окно на :target
Что такое псевдокласс :target
:target - это особый псевдокласс, который применяется для стилизации ссылок наряду с :hover, :visited, :active. Однако последние описывают, как правило, внешний вид ссылок при изменении их состояния - при наведении, посещении страницы, указанной в ссылке, или нажатии, то :target - это связь с определенным элементом по указанному в нем id.
Предположим, на странице есть элемент <h2 id="contacts">Контакты</h2>. А также ссылка на него: <a href="#contacts">Перейти к контактам</a>. После клика на ссылке браузер добавит #contacts к URL и прокрутит страницу к элементу с таким id.
В адресной строке браузера при этом будет что-то вроде https://example.com/about/#contacts.
В этот момент элемент <h2 id="contacts"> становится target-элементом. Именно его можно выбрать с помощью псевдокласса:
1 2 3 | #contacts:target { color: red; } |
Теперь при переходе по ссылке заголовок станет красным.
Важно: :target применяется не к ссылке, по которой кликнули, а к элементу, чей id совпадает с fragment identifier в URL.
То есть:
1 2 3 4 5 6 | ↓ #contacts ↓ <h2 id="contacts"> ↓ :target |
:target и CSS-селекторы
Важно понимать, что :target можно комбинировать с другими селекторами. Например:
1 2 3 | .popup:target { display: flex; } |
означает, что указанный стиль надо применить к .popup, если именно этот элемент является target.
Чуть глубже:
1 2 3 | .popup:target .popup__content { transform: scale(1); } |
означает, что когда .popup является target, изменить нужно состояние его дочернего элемента .popup__content.
Можно использовать и соседние элементы:
1 2 3 | #section:target + .description { display: block; } |
Таким образом, :target становится своеобразным переключателем состояния для целой группы элементов.
Интересная особенность: состояние хранится в URL
Это одна из главных особенностей :target: вы видите указанный id в составе URL страницы, тогда как JavaScript обычно хранит состояние компонента внутри программы. Например, для JavaScript характерны такие переменные:
1 2 | let isOpen = true |
У :target всё иначе. Для URL https://example.com/page/ клик на элементе с #popup приведет к изменению адреса на https://example.com/page/#popup. Это даёт несколько интересных возможностей.
- Можно сохранить состояние, если открыть
example.com/page/#tab2и обновить страницу, fragment identifier остаётся в адресе. - Можно поделиться состоянием, отправив ссылку
example.com/gallery/#photo3и браузер сразу перейдёт кphoto3. - Работает навигация браузера: переходы между fragment identifiers связаны с историей браузера, поэтому кнопки «Назад» и «Вперёд» могут менять состояние страницы и отображать разные действия пользователя.
Именно поэтому :target иногда оказывается гораздо полезнее, чем кажется на первый взгляд.
Простой пример создания навигации на основе :target
Создадим несколько разделов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <nav> <a href="#about">О нас</a> <a href="#services">Услуги</a> <a href="#contacts">Контакты</a> </nav> <section id="about"> <h2>О нас</h2> <p>Информация о компании.</p> </section> <section id="services"> <h2>Услуги</h2> <p>Описание услуг.</p> </section> <section id="contacts"> <h2>Контакты</h2> <p>Наши контактные данные.</p> </section> |
Теперь можно подсветить выбранный раздел:
1 2 3 4 | section:target { outline: 3px solid #4a90e2; } |
Давайте протестируем:
See the Pen :target for navigation by HTML-plus (@ambassador) on CodePen.
В этом примере при переходе на #services правило применится только к <section id="services"> и т.д.
Значительно более сложный пример от Chris Bolson:
See the Pen Scrollspy using :target-current & anchor-positioning by Chris Bolson (@cbolson) on CodePen.
Используем :target для показа и скрытия элементов
Еще одна интересная опция псевдокласса :target. Допустим, нужно скрыть дополнительную информацию и показать её после клика.
1 2 3 4 5 6 7 8 9 10 11 12 13 | HTML ----------------- <a href="#info">Показать информацию</a> <div id="info"> <p>Это дополнительная информация, которая изначально скрыта.</p> <a href="#close-info">Закрыть</a> </div> CSS: ------------- #info { display: none; } #info:target { display: block; } |
Это дополнительная информация, которая изначально скрыта.
ЗакрытьПочему вторая ссылка в примере работает на закрытие информации? Потому что она убирает конкретный fragment identifier из URL. Элемент #info перестаёт быть :target, поэтому снова применяется: #info { display: none;}
href="#" приводит к тому, что страницы "прыгает" вверх, т.к. такая ссылка всегда ведет именно на ту же страницу, на которой вы находитесь. Поэтому для закрытия можно использовать href="#close-info" на элемент, которого реально нет в разметке страницы.Получается простейший переключатель состояния без JavaScript, но слегка дерганный 🙂
Вкладки/табы на :target
На той же идее можно построить простейшие вкладки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <style> .tabs__content { display: none; background: #cde; padding: 20px; margin-bottom: 10px; } .tabs__content:target {display: block;} .tabs__nav a { display: inline-block; background: #cde; padding: 10px; border-radius: 4px 4px 0 0; } </style> <div class="tabs"> <nav class="tabs__nav"> <a href="#tab1">HTML</a> <a href="#tab2">CSS</a> <a href="#tab3">JavaScript</a> </nav> <div id="tab1" class="tabs__content"> <h3>HTML</h3> <p>HTML отвечает за структуру страницы.</p> </div> <div id="tab2" class="tabs__content"> <h3>CSS</h3> <p>CSS отвечает за оформление страницы.</p> </div> <div id="tab3" class="tabs__content"> <h3>JavaScript</h3> <p>JavaScript добавляет интерактивность.</p> </div> </div> |
В этом примере мы сначала спрятали все вкладки: .tabs__content { display: none; }, а потом стали по ним переключаться.
HTML
HTML отвечает за структуру страницы.
CSS
CSS отвечает за оформление страницы.
JavaScript
JavaScript добавляет интерактивность.
Не обошлось здесь и без проблем: при клике элемент прыгает вверх, подтягиваясь к верхней границе браузера.
Но есть один нюанс: при первом открытии страницы ни один элемент может не быть :target. Поэтому можно добавить вкладку по умолчанию или использовать небольшое количество дополнительного CSS/HTML /JS в зависимости от задачи.
Одно из решений - JS-код для открытия первой вкладки.
See the Pen Pure CSS3 Tabs using :target by Marek Zeman (@MarekZeman91) on CodePen.
У этого способа есть и другое интересное свойство: состояние вкладки находится в URL. Например: example.com/#tab3 можно скопировать и отправить другому человеку. Он откроет страницу сразу с третьей вкладкой.
Продвинутый пример переключения табов от Chris Bolson:
See the Pen inverted corner tabs using corner-shape + anchor-positioning + :target #cssonly by Chris Bolson (@cbolson) on CodePen.
Аккордеон на основе :target
Нам нужна простая разметка из ссылок с вопросами/заголовками и div с ответами. Например, такая простая:
1 2 3 4 5 6 7 8 9 | <a href="#answer1">Что такое HTML?</a> <div id="answer1" class="answer"> HTML описывает структуру документа. </div> <a href="#answer2">Что такое CSS?</a> <div id="answer2" class="answer"> CSS отвечает за внешний вид документа. </div> |
CSS тоже прост:
1 2 3 4 5 6 | .answer { display: none; } .answer:target { display: block; } |
Результат будет работать в таком виде:
See the Pen Simple accordion with :target by HTML-plus (@ambassador) on CodePen.
Однако такой аккордеон имеет важное отличие от компонентов, реализованных через JavaScript или <details>: состояние каждого открытого элемента зависит от URL.Плюс клик на новом элементе приводит к сворачиванию предыдущего. Иногда это очень здорово, иногда необходимо видеть несколько развернутых блоков. Поэтому :target не всегда является лучшим инструментом.
Еще одна реализация аккордеона от pollardld.
See the Pen CSS Accordion by pollardld (@pollardld) on CodePen.
Галерея изображений без JavaScript
А теперь используем ту же механику для создания настоящей галереи. Предположим, есть три изображения со ссылками на большие картинки.
Разметка примерно такова:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <div class="gallery__thumbs"> <a href="#photo1"> <img src="images/thumb-1.jpg" alt="Фото 1"> </a> <a href="#photo2"> <img src="images/thumb-2.jpg" alt="Фото 2"> </a> <a href="#photo3"> <img src="images/thumb-3.jpg" alt="Фото 3"> </a> </div> <div class="gallery__photos"> <div id="photo1" class="gallery__photo"> <img src="images/photo-1.jpg" alt="Фото 1"> </div> <div id="photo2" class="gallery__photo"> <img src="images/photo-2.jpg" alt="Фото 2"> </div> <div id="photo3" class="gallery__photo"> <img src="images/photo-3.jpg" alt="Фото 3"> </div> </div> |
CSS:
1 2 3 4 5 6 7 | .gallery__photo { display: none; } .gallery__photo:target { display: block; } |
Теперь клик по миниатюре <a href="#photo2"> делает <div id="photo2" class="gallery__photo"> текущим :target, после чего он становится видимым. Для того чтобы первое изображение отображалось, используем немного JavaScript для имитации клика по первой ссылке.
Тестируем:
See the Pen CSS simple image gallery :target by HTML-plus (@ambassador) on CodePen.
Превращаем галерею в lightbox
Можно пойти ещё дальше. Представим, что при клике на миниатюру большое изображение должно открываться поверх страницы с затемнением фона.
HTML можно изменить следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <div class="gallery__thumbs"> <a href="#photo1"> <img src="images/thumb-1.jpg" alt="Фото 1"> </a> <a href="#photo2"> <img src="images/thumb-2.jpg" alt="Фото 2"> </a> <a href="#photo3"> <img src="images/thumb-3.jpg" alt="Фото 3"> </a> </div> <div class="lightbox"> <div id="photo1" class="lightbox__item"> <a href="#" class="lightbox__close">×</a> <img src="images/photo-1.jpg" alt="Фото 1"> </div> <div id="photo2" class="lightbox__item"> <a href="#" class="lightbox__close">×</a> <img src="images/photo-2.jpg" alt="Фото 2"> </div> <div id="photo3" class="lightbox__item"> <a href="#" class="lightbox__close">×</a> <img src="images/photo-3.jpg" alt="Фото 3"> </div> </div> |
CSS-код будет таким, что позволит нам спрятать .lightbox__item за счет display: none, заданном изначально, а потом показать его при клике в виде .lightbox__item:target с display: flex. Само фото мы поместим в div-контейнер с id и еще одной ссылкой для скрытия лайтбокса по примеру выше.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | .gallery { display: flex; flex-direction: column; text-align: center; } .lightbox__item { position: fixed; inset: 0; align-items: center; justify-content: center; padding: 30px; background: rgb(0 0 0 / 50%); display: none; } .lightbox__item img { max-width: 90vw; max-height: 90vh; } .lightbox__close { position: absolute; top: 20px; right: 30px; font-size: 40px; color: white; text-decoration: none; } .lightbox__item:target { display: flex; } |
Теперь последовательность действий выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Клик по миниатюре ↓ href="#photo2" ↓ URL получает #photo2 ↓ #photo2 становится :target ↓ .lightbox__item:target ↓ display: flex ↓ изображение появляется поверх страницы |
Получился lightbox без JavaScript. Давайте на него посмотрим:
See the Pen CSS simple image gallery-lightbox with :target by HTML-plus (@ambassador) on CodePen.
Более сложные примеры фотогалерей:
Пример 1
See the Pen #hash nav gallery using :target-before, :target-current, :target-after & anchor-positioning by Chris Bolson (@cbolson) on CodePen.
Пример 2
See the Pen Image swap using anchor-positioning by Chris Bolson (@cbolson) on CodePen.
Пример 3
See the Pen animation-timeline gallery + anchor-positioning on the thumbnails by Chris Bolson (@cbolson) on CodePen.
Popup-окно на :target
Ту же технику можно использовать не только для изображений, но и для простого информационного popup-окна.
HTML-разметка:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <a href="#popup" class="button"> Открыть окно </a> <div id="popup" class="popup"> <div class="popup__content"> <a href="#" class="popup__close"> × </a> <h2>Привет!</h2> <p> Это модальное окно работает без JavaScript. </p> </div> </div> |
CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | .popup { position: fixed; inset: 0; align-items: center; justify-content: center; padding: 20px; background: rgb(0 0 0 / 70%); display: none; } .popup:target { display: flex; } .popup__content { position: relative; width: min(500px, 100%); padding: 30px; background: white; border-radius: 10px; } .popup__close { position: absolute; top: 10px; right: 15px; font-size: 30px; text-decoration: none; } |
Результат:
See the Pen CSS Modal using :target Selector by HTML-plus (@ambassador) on CodePen.
Причём URL при открытом окне будет выглядеть примерно так: example.com/page/#popup, а после нажатия на × ссылка <a href="#"> возвращает страницу в исходное состояние.
Добавляем анимацию
Сам по себе :target не умеет анимировать элементы. Он всего лишь меняет состояние, а анимацию можно поручить обычному CSS. Например, с помощью свойств animation и @keyframes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | .popup:target .popup__content { animation: popup-show 0.3s ease; } @keyframes popup-show { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } |
Теперь окно не просто появляется, а плавно увеличивается.
Можно сделать и более простой эффект:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .popup:target .popup__content { animation: popup-show 0.3s ease; } @keyframes popup-show { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } |
Еще один пример всплывающего окна от Homer Gaines.
See the Pen CSS Modal using :target Selector by Homer Gaines (@xirclebox) on CodePen.
Больше примеров всплывающих окон вы найдете в отдельной статье.
Примеры использования псевдокласса :target
Презентация от Envato Tuts+
See the Pen #9: Pure CSS3 “PowerPoint” Like Presentation (complete) by Envato Tuts+ (@tutsplus) on CodePen.
Открытие меню
See the Pen CSS ":target" Navigation by Elen (@ambassador) on CodePen.
Использование :target для запуска анимации от Guil H
See the Pen Using :target to trigger a simple animation. by Guil H (@Guilh) on CodePen.