Scroll Spy
createScrollSpy(targets, options, onChange) — отслеживает, какой из списка целевых элементов сейчас находится в «активной» полосе рядом с вертикальным центром viewport, и сообщает его индекс — стандартный приём подсветки навигации (сайдбар документации, оглавление) без ручной арифметики скролла. Построен целиком поверх пулящегося движка видимости, поэтому все цели с одинаковыми rootMargin/threshold/root (обычный случай — все они приходят из одного вызова) делят один нативный IntersectionObserver.
function createScrollSpy(
targets: Array<Element | null | undefined>,
options: ScrollSpyOptions | undefined,
onChange: (activeIndex: number | null) => void,
): ScrollSpy
interface ScrollSpy {
destroy(): void
}import { createScrollSpy } from '@macrulez/inview-core'
const sections = document.querySelectorAll('section[id]')
const spy = createScrollSpy([...sections], undefined, (activeIndex) => {
const active = activeIndex === null ? null : sections[activeIndex]
navLinks.forEach((link, i) => link.classList.toggle('active', i === activeIndex))
})
// позже
spy.destroy()Опции
ScrollSpyOptions
| Поле | Тип | По умолчанию |
|---|---|---|
rootMargin | string | '-45% 0px -45% 0px' — тонкая полоса у вертикального центра; какая граница её пересекает, тот элемент и становится активным |
threshold | number | number[] | — |
root | Element | null | null |
pool | ObserverPool | общий пул по умолчанию |
Настраивайте rootMargin (и threshold) под конкретную вёрстку — странице с очень короткими секциями может понадобиться более узкая полоса, чем по умолчанию.
onChange(activeIndex)
Вызывается с индексом в targets того элемента, что сейчас находится в полосе, или null, если ни один не находится. Если сразу несколько целей одновременно оказываются в полосе (короткие секции, узкая полоса), побеждает самая ранняя по порядку в targets — детерминированно, и совпадает с порядком чтения.
Возвращаемое значение
destroy()
() => void
Снимает наблюдение со всех целей и уничтожает движок под капотом.
Реактивная версия: для React — см. React-хуки; Vue composable — ниже.
useScrollSpy (Vue)
function useScrollSpy(
targets: MaybeRefOrGetter<Array<HTMLElement | null | undefined>>,
options?: UseScrollSpyOptions,
): Ref<number | null>UseScrollSpyOptions имеет ту же форму, что ScrollSpyOptions выше. Переподписывается при каждом изменении самого targets (как ref/геттера); options захватываются в этот момент — та же одноразовая модель, что у useRevealController, а не реактивный ref/геттер сам по себе.
<script setup lang="ts">
import { useTemplateRefs } from 'vue' // или собирайте рефы вручную в v-for
import { useScrollSpy } from '@macrulez/inview-vue'
const sections = ref<HTMLElement[]>([])
const activeIndex = useScrollSpy(sections)
</script>
<template>
<nav>
<a v-for="(item, i) in items" :key="item.id" :class="{ active: i === activeIndex }">
{{ item.title }}
</a>
</nav>
</template>