Якорные ссылки и как их сделать потрясающими

Источник: «Anchor Links and How to Make Them Awesome»
Якорные ссылки — простой способ создать навигацию по странице. Например, в оглавлении можно использовать якорные ссылки для перехода к различным разделам страницы.

Они очень просты в использовании, но если добавить немного CSS, то их можно сделать по-настоящему яркими. Давайте начнём с основ и будем двигаться дальше.

Скромная якорная ссылка

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

<h1 id="my-anchor">Page anchor</h1>

Теперь можно ссылаться на якорный элемент страницы, используя # с id в качестве href.

<a href="#my-anchor">Jump to the page anchor</a>

Есть два специальных значения, которые можно использовать в качестве href якорной ссылки: #top или просто #. Они оба выполняют одну и ту же функцию — переводят на самый верх страницы. Для них не нужно создавать якорный элемент страницы.

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

Демонстрация: Якорная ссылка: Основы

Обратите внимание, что при клике на якорную ссылку к URL страницы в адресной строке добавляется хэш (например, #my-anchor), который можно скопировать и использовать для прямой ссылки на якорную страницу, как это сделано здесь.

Теперь давайте посмотрим, что можно сделать, добавив немного CSS.

Добавление плавной прокрутки

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

html {
scroll-behavior: smooth;
}

Теперь попробуйте ещё раз воспользоваться якорными ссылками.

Демонстрация: Якорная ссылка: Плавная прокрутка

Добавление отступа прокрутки

Якорные ссылки до сих пор прокручивали страницу до самого верхнего края области просмотра. Было бы неплохо, если бы там было немного свободного пространства. Это можно сделать, установив для якорного элемента страницы параметр scroll-margin-top.

h1 {
scroll-margin-top: 40px;
}

Это создаст 40px пространства между верхней частью области просмотра и якорным элементом страницы. Посмотрите.

Демонстрация: Якорная ссылка: Плавная прокрутка + Отступ

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

Добавление стилей целевых якорных элементов страницы

Можно использовать псевдокласс CSS :target, позволяющий добавить стилизацию к якорному элементу страницы при переходе на него.

h1:target {
color: #71a819; /* зелёный */
}

В этой демонстрации клике на "Jump to the page anchor" активирует CSS приведённый выше, делая текст якорного элемента страницы зелёным. При клике на "Jump back to top" <h1> перестаёт быть целевым, поэтому текст перестаёт быть зелёным.

Демонстрация: Якорная ссылка: Плавная прокрутка + Отступ+Стилизация

Более причудливый стиль целевого элемента

С помощью :target можно делать более сложные вещи, чем просто изменение цвета текста. Вот демонстрация, позволяющая выделять области контента и анимировать ссылку "Back to top".

Демонстрация: Якорная ссылка: Причудливая стилизация

Вот (сокращённый) HTML, показывающий, как всё устроено.

<nav>
<a href="#kiwis">Kiwis</a>
<a href="#limes">Limes</a>
<a href="#pears">Pears</a>
</nav>

<article>
<h1 id="kiwis">Kiwis</h1>
<p><!-- blurb about kiwis --></p>
</article>

<!-- другие элементы article здесь -->

<a class="back-to-top" href="#top">Back to top</a>

Выделение article осуществляется с помощью этого CSS.

/* border изначально прозрачный */
article {
border: 3px solid transparent;
}

/* border становится зелёным для article, когда целевой h1 находится внутри */
article:has(h1:target) {
border-color: #71a819;
}

А вот CSS для отображения/скрытия ссылки "Back to top".

.back-to-top {
/* позиционирование ссылки в правый верхний угол */
position: fixed;
top: 10px;
right: 10px;

/* изначально ссылка делается прозрачной и смещается за правый край области просмотра */
opacity: 0;
translate: calc(100% + 10px);

/* продолжительность перехода в полсекунды */
transition: all 0.5s;
}

/* когда в body есть целевой h1, ссылка проявляется и перемещается в область просмотра */
body:has(h1:target) .back-to-top {
opacity: 1;
translate: 0px;
}

Вложенная прокрутка

Все приведённые до сих пор примеры имели один контейнер прокрутки — саму страницу. Однако возможны и вложенные контейнеры прокрутки — например, прокручиваемый <div> на странице. Если поместить якорный элемент страницы во вложенный прокручиваемый контейнер, то ссылки, указывающие на него, прокрутят все необходимые контейнеры, чтобы вывести цель на экран. Великолепно!

Вот демонстрация. Обратите внимание, что позиционирование прокрутки кажется немного смещённым — не был добавлен scroll-margin. О причинах этого поговорим далее.

Демонстрация: Вложенная прокручивающаяся якорная ссылка: Без отступов

Отступ прокрутки vs. Поле прокрутки

Теперь давайте исправим положение прокрутки. Ранее мы использовали scroll-margin-top, но в Chrome и Edge он не работает. Отступ прокрутки не будет выходить за пределы вложенного прокручиваемого контейнера.

К счастью, есть другой способ решения этой задачи. Вместо scroll-margin можно использовать scroll-padding. Они оба дают схожий результат, но разница в следующем:

Давайте обновим эту демонстрацию, добавив в неё scroll-padding.

html {
scroll-padding-top: 130px;
}

.slide-container {
scroll-padding-inline: 20px;
}

Это добавляет щедрый отступ от верха страницы, чтобы кнопки оставались в области видимости. Кроме того, это добавляет полосы прокрутки к прокручиваемому <div>, чтобы каждый слайд был хорошо отцентрирован в контейнере прокрутки.

Демонстрация: Вложенная прокручивающаяся якорная ссылка: С отступом

Прокрутка в области просмотра с помощью JavaScript

Также можно использовать JavaScript для прокрутки к якорному элементу страницы, вызвав функцию scrollIntoView(). В самом простом виде это выглядит так.

const anchor = document.getElementById('my-anchor');
anchor.scrollIntoView();

На самом деле, даже не нужен якорный элемент страницы с id. Можно использовать функцию scrollIntoView() для прокрутки к любому элементу.

const element = document.querySelector('.whatever');
element.scrollIntoView();

В любом случае прокрутка будет такой же, как и ранее, но с некоторыми оговорками.

  1. URL-адрес страницы в адресной строке не будет обновляться с указанием хэша.
  2. Элемент, к которому осуществляется прокрутка, не считается целевым, поэтому псевдокласс CSS :target не будет работать.
  3. В Chrome, Firefox и Edge, когда функция scrollIntoView() используется внутри iframe, она может "вырваться" и повлиять на положение прокрутки родительской страницы.

Если это вас останавливает, и вам действительно нужно использовать JavaScript, есть обходной путь.

window.location.assign('#my-anchor');

Этот код будет вести себя так, как если бы была кликнута якорная ссылка #my-anchor.

Параметры scrollIntoView()

Давайте подробнее поговорим о scrollIntoView(). Можно точно настроить прокрутку, указав необязательный параметр объекта. Вот как это выглядит со значениями по умолчанию.

element.scrollIntoView({
behavior: 'auto',
block: 'start',
inline: 'nearest'
});

Свойство behavior определяет, будет ли прокрутка мгновенной или плавной.

Свойство block определяет вертикальное выравнивание прокрутки.

Свойство inline имеет аналогичную логику, но для горизонтального выравнивания прокрутки.

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

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

Демонстрация: Игровая площадка ScrollIntoView()

Заметки за кулисами

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

Обычно я встраиваю демки Codepen прямо в свои статьи, используя <iframe>. Как видите, в этот раз я этого не сделал по двум причинам.

  1. Клики по якорным ссылкам считаются перемещением, попадающим в историю браузера. Это верно, даже если ссылки находятся внутри <iframe>. Так что если вы кликнули пару якорных ссылок во встроенных демках, а затем попытались нажать кнопку "Назад" в браузере, то всё равно окажетесь на этой странице.
  2. Об этом уже упоминалось ранее, но использование функции scrollIntoView() в <iframe> оказывает влияние на позицию прокрутки родительской страницы. Нет никакой опции песочницы, чтобы исправить это. Особенно в последней демонстрации "игровой площадки" было очень странно играть со встроенной демкой, когда вся страница двигалась. В моих тестах Safari был единственным браузером, который этого не делал.

Ещё одно замечание: когда я изучал scroll-margin и scroll-padding, то встречал множество источников, в которых сбивчиво говорилось только об их связи с прилипанием прокрутки, и я подумал, не является ли использование их для якорных ссылок страницы непреднамеренным хаком. Это не так, поскольку я обнаружил, что в спецификацию CSS прилипания прокрутки были внесены изменения, призванные устранить эту путаницу.

Дополнительные материалы

Предыдущая Статья

Универсальность данных: API геттер и сеттер

Следующая Статья

Анимация элемента dialog