VGSpy

Анимированный скроллинг шпион

Прокрутка и якоря

Data API, контейнер прокрутки и ручная инициализация.

VGSpy добавляет класс active ссылке текущей секции. Прокрутите область примера или выберите пункт навигации. Каждый пример независим; оформление активных ссылок задаёт приложение.

Data API: плавный переход

Навигация и область прокрутки — разные элементы. data-target указывает контейнер, data-root-margin задаёт область наблюдения. Нажмите «Проверка» и прокрутите обратно.

01 · Подготовка

Добавьте уникальные id секциям и ссылки на них в навигацию.

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

02 · Настройка

data-target указывает прокручиваемую область. Без него VGSpy ищет scroll-root у секций автоматически.

03 · Проверка

Нажмите «Подготовка» и проверьте плавный возврат к началу.

HTML
<div class="spy-demo">
    <nav class="spy-demo__nav" id="spy-data-nav" aria-label="Этапы запуска"
         data-vg-toggle="spy" data-target="#spy-data-scroll"
         data-root-margin="0px 0px -25%" data-threshold="0.1, 0.5, 1">
        <a href="#spy-data-start">Подготовка</a>
        <a href="#spy-data-build">Настройка</a>
        <a href="#spy-data-check">Проверка</a>
    </nav>
    <div class="spy-demo__scroll" id="spy-data-scroll" tabindex="0" role="region" aria-label="Этапы запуска: содержимое">
        <section class="spy-demo__section" id="spy-data-start"><h4>01 · Подготовка</h4><p>Добавьте уникальные id секциям и ссылки на них в навигацию.</p><p>Прокрутите эту область вниз: выделение следует за видимыми разделами.</p></section>
        <section class="spy-demo__section" id="spy-data-build"><h4>02 · Настройка</h4><p>data-target указывает прокручиваемую область. Без него VGSpy ищет scroll-root у секций автоматически.</p></section>
        <section class="spy-demo__section" id="spy-data-check"><h4>03 · Проверка</h4><p>Нажмите «Подготовка» и проверьте плавный возврат к началу.</p></section>
    </div>
</div>
JavaScript
/**
 * Описание: декларативный пример VGSpy.
 * Возможности: автозапуск и настройка наблюдения через data-атрибуты.
 */
// Дополнительная инициализация не требуется: используется Data API.
// Общий JavaScript-entrypoint vgapp должен быть подключён на странице.
CSS
/* Композиция демо; семантические токены OKAUX на сайте используют префикс vg. */
.spy-demo {
    display: grid;
    gap: 1rem;
    min-width: 0;
}
.spy-demo__nav, .spy-demo__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: .5rem;
}
.spy-demo__nav a, .spy-demo__nav button {
    display: inline-flex;
    padding: .5rem .75rem;
    border: 1px solid var(--vg-border-default);
    border-radius: .5rem;
    background: var(--vg-background-primary);
    color: var(--vg-text-primary);
    text-decoration: none;
    font: inherit;
}
.spy-demo__nav .active {
    background: var(--vg-background-tertiary);
    border-color: var(--vg-border-active);
    box-shadow: inset 0 -3px var(--vg-border-active);
    font-weight: 700;
}
.spy-demo__nav :is(a, button):focus-visible {
    outline: 2px solid var(--vg-border-active);
    outline-offset: 2px;
}
.spy-demo__nav .nav {
    display: grid;
    gap: .25rem;
    padding: .5rem 0 0 1rem;
}
.spy-demo__nav .vg-dropdown-container {
    display: grid;
    gap: .25rem;
    padding: .5rem;
}
.spy-demo__scroll {
    height: 20rem;
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--vg-border-default);
    border-radius: .75rem;
    background: var(--vg-background-primary);
    color: var(--vg-text-primary);
}
.spy-demo__section {
    min-height: 18rem;
    padding: 1.25rem;
    border-bottom: 1px solid var(--vg-border-default);
    overflow-wrap: anywhere;
}
.spy-demo__section:nth-child(even) { background: var(--vg-background-secondary); }
.spy-demo__section h4 { margin: 0 0 .75rem; }
.spy-demo__log {
    max-height: 10rem;
    overflow: auto;
    margin: 0;
    padding-left: 1.5rem;
    color: var(--vg-text-secondary);
    overflow-wrap: anywhere;
}
.spy-demo__empty { padding: 1.25rem; color: var(--vg-text-secondary); }
@media (max-width: 575px) {
    .spy-demo__scroll { height: 17rem; }
    .spy-demo__section { min-height: 16rem; padding: 1rem; }
}

Ручная инициализация: только наблюдение

target передан DOM-элементом, threshold — массивом. smoothScroll: false оставляет обычные якорные переходы браузера. VGSpy продолжает отслеживать прокрутку.

Требования

Здесь нет data-vg-toggle="spy": экземпляр создаётся вручную после появления разметки.

Реализация

Клики обрабатывает браузер: переход без анимации VGSpy, адрес получает якорь. Прокрутка колесом также меняет активный пункт.

Результат

Пороги [0, 0.5, 1] заменили настройки по умолчанию. Наблюдение работает независимо от первого примера.

HTML
<div class="spy-demo">
    <nav class="spy-demo__nav" id="spy-manual-nav" aria-label="Нативные якоря">
        <a href="#spy-manual-one">Требования</a>
        <a href="#spy-manual-two">Реализация</a>
        <a href="#spy-manual-three">Результат</a>
    </nav>
    <div class="spy-demo__scroll" id="spy-manual-scroll" tabindex="0" role="region" aria-label="Нативные якоря: содержимое">
        <section class="spy-demo__section" id="spy-manual-one"><h4>Требования</h4><p>Здесь нет data-vg-toggle="spy": экземпляр создаётся вручную после появления разметки.</p></section>
        <section class="spy-demo__section" id="spy-manual-two"><h4>Реализация</h4><p>Клики обрабатывает браузер: переход без анимации VGSpy, адрес получает якорь. Прокрутка колесом также меняет активный пункт.</p></section>
        <section class="spy-demo__section" id="spy-manual-three"><h4>Результат</h4><p>Пороги [0, 0.5, 1] заменили настройки по умолчанию. Наблюдение работает независимо от первого примера.</p></section>
    </div>
</div>
JavaScript
/**
 * Описание: ручной запуск VGSpy в режиме наблюдения.
 * Возможности: DOM-элемент target, собственные пороги и нативные якорные переходы.
 */
import { VGSpy } from 'vgapp';

function initSpyDemo() {
    const nav = document.getElementById('spy-manual-nav');
    if (!nav) return;
    VGSpy.getOrCreateInstance(nav, {
        target: document.getElementById('spy-manual-scroll'),
        smoothScroll: false,
        rootMargin: '0px 0px -25%',
        threshold: [0, 0.5, 1],
    });
}

initSpyDemo();
CSS
/* Композиция демо; семантические токены OKAUX на сайте используют префикс vg. */
.spy-demo {
    display: grid;
    gap: 1rem;
    min-width: 0;
}
.spy-demo__nav, .spy-demo__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: .5rem;
}
.spy-demo__nav a, .spy-demo__nav button {
    display: inline-flex;
    padding: .5rem .75rem;
    border: 1px solid var(--vg-border-default);
    border-radius: .5rem;
    background: var(--vg-background-primary);
    color: var(--vg-text-primary);
    text-decoration: none;
    font: inherit;
}
.spy-demo__nav .active {
    background: var(--vg-background-tertiary);
    border-color: var(--vg-border-active);
    box-shadow: inset 0 -3px var(--vg-border-active);
    font-weight: 700;
}
.spy-demo__nav :is(a, button):focus-visible {
    outline: 2px solid var(--vg-border-active);
    outline-offset: 2px;
}
.spy-demo__nav .nav {
    display: grid;
    gap: .25rem;
    padding: .5rem 0 0 1rem;
}
.spy-demo__nav .vg-dropdown-container {
    display: grid;
    gap: .25rem;
    padding: .5rem;
}
.spy-demo__scroll {
    height: 20rem;
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid var(--vg-border-default);
    border-radius: .75rem;
    background: var(--vg-background-primary);
    color: var(--vg-text-primary);
}
.spy-demo__section {
    min-height: 18rem;
    padding: 1.25rem;
    border-bottom: 1px solid var(--vg-border-default);
    overflow-wrap: anywhere;
}
.spy-demo__section:nth-child(even) { background: var(--vg-background-secondary); }
.spy-demo__section h4 { margin: 0 0 .75rem; }
.spy-demo__log {
    max-height: 10rem;
    overflow: auto;
    margin: 0;
    padding-left: 1.5rem;
    color: var(--vg-text-secondary);
    overflow-wrap: anywhere;
}
.spy-demo__empty { padding: 1.25rem; color: var(--vg-text-secondary); }
@media (max-width: 575px) {
    .spy-demo__scroll { height: 17rem; }
    .spy-demo__section { min-height: 16rem; padding: 1rem; }
}

Подключение и параметры

import { VGSpy } from 'vgapp'
Подключите общий JS-entrypoint. data-vg-toggle="spy" ставится на навигацию, а не на секции. Элементы, вставленные после загрузки DOM, требуют getOrCreateInstance().
target / data-target
Селектор или DOM-элемент прокрутки. Если явно заданный элемент не прокручивается, VGSpy ищет overflow-контейнер у первой секции, затем использует окно. Без target сначала проверяется сама навигация. Все секции примера должны принадлежать одному scroll-root.
rootMargin / data-root-margin
По умолчанию '0px 0px -25%'. Меняет область IntersectionObserver; это не отступ прокрутки от фиксированной шапки.
threshold / data-threshold
По умолчанию [0.1, 0.5, 1]. Массив чисел от 0 до 1 или строка '0.1, 0.5, 1'; массив заменяет стандартные пороги.
smoothScroll / data-smooth-scroll
true по умолчанию. false отключает перехват клика, но не наблюдение. Кнопке с data-vg-target при false понадобится собственное действие; обычная ссылка продолжает работать нативно.
offset / data-offset
Устаревший параметр, по умолчанию null. Если задан, заменяет rootMargin на «offset px 0px -30%». Для новых интеграций используйте rootMargin.
Приоритет
Значения по умолчанию → параметры JavaScript → одноимённые data-атрибуты. Настройки читаются при создании экземпляра; refresh() не меняет конфигурацию.