Навигация Scrollspy – это распространенный шаблон пользовательского интерфейса, который автоматически выделяет ссылку навигации, соответствующую разделу страницы, находящемуся в данный момент в области просмотра. Это позволяет пользователям лучше ориентироваться в длинных документах.
Такая модель часто используется в двух случаях:
- Одностраничные сайты. На таких сайтах ссылки в основном меню ведут на разные разделы одной страницы, а не на отдельные URL.
- Объемный контент. Оглавление или боковая панель навигации, которые позволяют пользователю отслеживать, какую часть статьи, руководства или документации он уже прочитал.
Рекомендации по внедрению
Раньше для создания scrollspy требовался JavaScript и IntersectionObserver API. Современный CSS предоставляет нативный способ добиться этого с помощью свойства scroll-target-group и псевдокласса :target-current .
- Убедитесь, что в ссылках навигации используются идентификаторы фрагментов. Навигация должна состоять из анкорных ссылок (
<a>), которые указывают на разделы на той же странице с помощью идентификаторов фрагментов (например,<a href="#section-1">), соответствующихidразделов контента. - Включите группировку целей прокрутки. Примените свойство
scroll-target-group: autoк контейнеру ссылок навигации. Это указывает браузеру, что ссылки нужно обрабатывать как маркеры, отслеживающие соответствующие цели. - Оформление активной ссылки. Используйте псевдокласс
:target-currentдля навигационных ссылок, чтобы применять стили, когда целевой раздел виден в контейнере прокрутки. - Семантическое состояние. Используйте скрипт, чтобы обновлять
aria-current="true"для активной ссылки, когда прокрутка останавливается. Это позволит правильно передавать состояние технологиям специальных возможностей.
Пример кода
В этом примере активная ссылка в фиксированном меню навигации выделяется, когда пользователь прокручивает разделы.
/* MANDATORY: The container of markers must have scroll-target-group: auto */
nav {
/* Enables automatic tracking of fragment links */
scroll-target-group: auto;
}
/* MANDATORY: Use :target-current to style the link whose target is in view. */
nav a:target-current {
color: var(--accent, #007bff);
border-bottom-color: var(--accent, #007bff);
}
<nav>
<a href="#intro">Introduction</a>
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
</nav>
<main>
<!-- MANDATORY: Each section must have a unique ID matching a link fragment in the <nav>. -->
<section id="intro">
<h2>Introduction</h2>
<!-- content -->
</section>
<section id="features">
<h2>Features</h2>
<!-- content -->
</section>
<section id="pricing">
<h2>Pricing</h2>
<!-- content -->
</section>
</main>
<script type="module">
if (CSS.supports('scroll-target-group: auto')) {
const syncAriaCurrent = () => {
const currentLink = document.querySelector('nav a:target-current');
document.querySelectorAll('nav a').forEach(link => {
link.setAttribute('aria-current', link === currentLink ? 'true' : 'false');
});
};
// Update the aria on initial page load. Ensure the DOM content is loaded, either by using `<script type="module">` or an event listener for `DOMContentLoaded`.
syncAriaCurrent();
// Update the aria after scroll ends
document.addEventListener('scrollend', syncAriaCurrent);
}
</script>
Рекомендации
- РЕКОМЕНДУЕТСЯ использовать
scroll-target-group: autoв специальном контейнере для ссылок навигации. Это позволит браузеру определить, какие ссылки нужно отслеживать. - РЕКОМЕНДУЕТСЯ убедиться, что у целей есть уникальные атрибуты
id, соответствующие атрибутамhrefссылок. - РЕКОМЕНДУЕТСЯ оставлять достаточно места по вертикали для разделов.
scroll-target-groupнаиболее четко определяет видимую цель, когда разделы достаточно велики, чтобы одновременно были видны только один или два. - ОБЯЗАТЕЛЬНО. Для обеспечения специальных возможностей визуальное состояние должно быть отражено с помощью
aria-current. Хотя:target-currentобрабатывает визуальный аспект, это не отображается в программах чтения с экрана. - AVOID использовать только цвет для обозначения активного состояния. Добавьте другие визуальные подсказки, например насыщенность шрифта, подчеркивание или точку-индикатор, или обеспечьте достаточный контраст между цветами, чтобы разница была заметна пользователям с нарушениями цветового зрения.
Резервные стратегии
Для браузеров, которые не поддерживают scroll-target-group, следует использовать резервный вариант на основе IntersectionObserver API.
<script>
// Feature detection for scroll-target-group
const supportsScrollTargetGroup = CSS.supports('scroll-target-group: auto');
if (!supportsScrollTargetGroup) {
const navLinks = document.querySelectorAll('nav a');
const sections = document.querySelectorAll('main section');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.getAttribute('id');
navLinks.forEach(link => {
const isActive = link.getAttribute('href') === `#${id}`;
// Use the escaped colon class for consistent styling
link.classList.toggle('\:target-current', isActive);
link.setAttribute('aria-current', isActive);
});
}
});
}, { rootMargin: '-50% 0px -50% 0px', threshold: 0 });
sections.forEach(section => observer.observe(section));
}
</script>
<style>
/* Fallback style if :target-current is not supported.
Use :is() to combine the native pseudo-class with a fallback class for older browsers. DO NOT use `nav a:target-current, nav a.\:target-current, as the unrecognized pseudoselector invalidates the entire rule in older browsers. */
nav a:is(:target-current, .\:target-current) {
color: var(--accent, #007bff);
border-bottom-color: var(--accent, #007bff);
font-weight: bold;
}
</style>