An open API service indexing awesome lists of open source software.

https://github.com/fsegurai/test-web-vanilla

Scrollspy is a lightweight JS library that highlights active navigation links based on scroll position. Supports nested menus and custom events for seamless single-page navigation.
https://github.com/fsegurai/test-web-vanilla

menu navigation scroll scrollspy test

Last synced: 26 days ago
JSON representation

Scrollspy is a lightweight JS library that highlights active navigation links based on scroll position. Supports nested menus and custom events for seamless single-page navigation.

Awesome Lists containing this project

README

          


ScrollSpy Logo



Build Main Status


Latest Release



GitHub contributors
Dependency status for repo

GitHub License



Stars
Forks

**A library for scrollspy functionality**

`@fsegurai/test-package-1` is a dependency-free, lightweight scrollspy library that highlights navigation links based on
scroll position. Perfect for documentation sites, blogs, and landing pages with sticky tables of contents.

---

## ๐Ÿ“‹ Table of Contents

- [๐Ÿš€ Features](#-features)
- [๐Ÿ“ฆ Installation](#-installation)
- [NPM](#npm)
- [CDN / HTML](#cdn--html)
- [๐Ÿง  Usage](#-usage)
- [HTML Example](#html-example)
- [JavaScript Example](#javascript-example)
- [TypeScript Example](#typescript-example)
- [๐Ÿงช Demo Integration](#-demo-integration)
- [โš™๏ธ Options](#๏ธ-options)
- [๐Ÿ“ก Events](#-events)
- [`gumshoeactivate`](#gumshoeactivate)
- [`gumshoedeactivate`](#gumshoedeactivate)
- [Type-Safe Event Listeners](#type-safe-event-listeners)
- [๐Ÿ” Dynamic Content Support](#-dynamic-content-support)
- [๐Ÿ“˜ API](#-api)
- [๐ŸŽฏ TypeScript Support](#-typescript-support)
- [โœ… Browser Support](#-browser-support)
- [๐Ÿงผ License](#-license)

---

## ๐Ÿš€ Features

- โšก๏ธ Lightweight (no dependencies)
- ๐Ÿ“˜ **100% TypeScript** with full type definitions
- ๐Ÿ” Intelligent scroll-based section detection
- ๐Ÿงฉ Nested navigation support
- ๐Ÿงญ Works with dynamic or static content
- ๐ŸŽฏ Scroll offset for fixed headers
- ๐Ÿ”„ Automatic DOM mutation observer (optional)
- ๐ŸŽ‰ Type-safe custom activation events
- ๐Ÿงผ Clean API with init/refresh/destroy

---

## ๐Ÿ“ฆ Installation

### NPM

```bash
npm install @fsegurai/test-package-1
```

### CDN / HTML

```html

import ScrollSpy from '@fsegurai/test-package-1';

const spy = new ScrollSpy('#toc');

```

---

## ๐Ÿง  Usage

### HTML Example

```html

Intro


...


Install


...


Usage


Basic


...


Advanced


...

```

### JavaScript Example

```js
import ScrollSpy from '@fsegurai/test-package-1';

const spy = new ScrollSpy('#toc', {
offset: 80,
nested: true,
nestedClass: 'parent-active',
reflow: true,
events: true,
observe: true
});

// Listen for activation events
document.addEventListener('gumshoeactivate', (event) => {
console.log('Activated:', event.detail.target.id);
});
```

### TypeScript Example

```ts
import ScrollSpy, {type ScrollSpyEvent, type ScrollSpyOptions} from '@fsegurai/test-package-1';

const options: ScrollSpyOptions = {
offset: 80,
nested: true,
nestedClass: 'parent-active',
reflow: true,
events: true,
observe: true
};

const spy = new ScrollSpy('#toc', options);

// Fully typed event listener
document.addEventListener('gumshoeactivate', (event: Event) => {
const customEvent = event as CustomEvent;
console.log('Activated:', customEvent.detail.target.id);
console.log('Nav item:', customEvent.detail.nav);
});
```

---

## ๐Ÿงช Demo Integration

The demo in `demo/scripts/utils/toc.ts` builds a nested table of contents from headings, marks each heading with
`data-gumshoe`, and then initializes ScrollSpy against `#tableOfContents`.

```ts
import {
generateTOC,
initScrollspy,
setupMobileToggle,
setupSmoothScroll,
} from './utils/toc';

const content = document.querySelector('#content') as HTMLElement;

generateTOC(content);
setupMobileToggle();
setupSmoothScroll();
initScrollspy();
```

In that demo flow, the generated headings look like this:

```html

Intro


```

`initScrollspy()` configures the instance with `content: '[data-gumshoe]'`, `offset: 120`, `bottomThreshold: 10`,
`reflow: true`, and `events: true`.

---

## โš™๏ธ Options

All available options for customizing behavior:

| Option | Type | Default | Description |
|---------------------|-----------------------------------------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `nav` | `string` | โ€” | **(Required)** Selector for the navigation container. This is the element ScrollSpy scans for links. |
| `content` | `string` | `[data-gumshoe]` | Default selector used by the demo and dynamic-content workflows. The core lookup still resolves targets from nav fragments and `fragmentAttribute`. |
| `nested` | `boolean` | `false` | Adds a class to parent `

  • ` items in nested TOC structures. |
    | `nestedClass` | `string` | `'active-parent'` | Class name for parent `
  • ` elements when `nested` is `true`. |
    | `offset` | `number \| () => number` | `0` | Scroll offset in pixels or a function returning an offset, useful for fixed headers. |
    | `bottomThreshold` | `number` | `100` | Distance in pixels from the bottom of the page where the last section is auto-activated. |
    | `reflow` | `boolean` | `false` | If `true`, ScrollSpy also re-detects on window resize. |
    | `events` | `boolean` | `true` | Emits `gumshoeactivate` when the active section changes. `gumshoedeactivate` is part of the typings, but the current runtime does not dispatch it. |
    | `observe` | `boolean` | `false` | Enables a `MutationObserver` that calls `refresh()` when observed DOM nodes change. |
    | `fragmentAttribute` | `string \| (item: Element) => string \| null` | `null` | Attribute or function used to map nav items to content sections instead of relying on `href`. Supports full URLs like `/route#fragment` through the default hash parsing path. |
    | `navItemSelector` | `string` | `'a[href*="#"]'` | Selector for nav items (anchors or other elements) that should be considered by ScrollSpy. |

    > If you're using `observe: true`, make sure your headings or section wrappers have a consistent structure. The
    > `data-gumshoe` attribute is used by the demo and matches the default `content` selector, but section matching still
    > starts from the nav fragments themselves.

    ---

    ### Advanced Fragment Mapping (SPA/Angular)

    If you need to support full URLs in `href` (e.g. `/route#fragment`) or use a custom attribute (e.g.
    `data-scrollspy-fragment`), use the `fragmentAttribute` option:

    ```js
    // Use a custom attribute
    const spy = new ScrollSpy('#toc', {
    fragmentAttribute: 'data-scrollspy-fragment',
    });

    // Or use a function for advanced mapping
    const spy = new ScrollSpy('#toc', {
    fragmentAttribute: (item) => item.getAttribute('data-scrollspy-fragment') || null,
    });
    ```

    - The library will now match anchors using the custom attribute or function, not just `href`.
    - This is useful for Angular/SPA scenarios where you want the user to see the full URL in the browser, but scrollspy to
    map by fragment only.

    ---

    ## ๐Ÿ“ก Events

    These custom events are available on `document` when ScrollSpy updates the active section.

    ### `gumshoeactivate`

    Triggered when a new section becomes active.

    ```js
    document.addEventListener('gumshoeactivate', (e) => {
    console.log('Activated:', e.detail.target.id);
    console.log('Content:', e.detail.content);
    console.log('Nav item:', e.detail.nav);
    });
    ```

    ### About `gumshoedeactivate`

    `gumshoedeactivate` is included in the type definitions, but the current implementation does not dispatch it. If you
    need deactivation hooks, listen for `gumshoeactivate` and compare the previous active section yourself.

    Event `detail` includes:

    - `target`: The content section element
    - `content`: Alias of `target`
    - `nav`: Corresponding anchor tag from the TOC

    ### Type-Safe Event Listeners

    The library includes full TypeScript type definitions for the custom events that ship with the package. The
    `DocumentEventMap` is augmented to include both `gumshoeactivate` and `gumshoedeactivate`, even though only
    `gumshoeactivate` is emitted by the current runtime:

    ```ts
    import type {ScrollSpyEvent} from '@fsegurai/test-package-1';

    // TypeScript knows about these custom events automatically
    document.addEventListener('gumshoeactivate', (event: Event) => {
    const customEvent = event as CustomEvent;
    // Full intellisense support for event.detail.target, content, nav
    console.log(customEvent.detail.target.id);
    });
    ```

    - `target`: The content section element
    - `content`: Alias of `target`
    - `nav`: Corresponding anchor tag from the TOC

    ---

    ## ๐Ÿ” Dynamic Content Support

    If you update the TOC or headings dynamically, call:

    ```js
    spy.refresh();
    ```

    Or initialize with `observe: true` to let ScrollSpy refresh itself using a `MutationObserver`.

    ---

    ## ๐Ÿ“˜ API

    | Method | Description |
    |-----------------|-----------------------------------------------------------------------------|
    | `init()` | Performs the initial DOM lookup, content mapping, detection, and listeners. |
    | `getContents()` | Rebuilds the internal nav-to-target map from the current DOM. |
    | `getNavItem()` | Resolves the nav element associated with a content section. |
    | `detect()` | Re-runs detection logic based on current scroll position. |
    | `setup()` | Rebuilds contents and runs detection again. |
    | `refresh()` | Same rebuild/detect cycle as `setup()`; use this after dynamic updates. |
    | `destroy()` | Removes listeners, clears active classes, and disconnects the observer. |

    > `setup()` and `refresh()` currently perform the same rebuild/detect pass.

    ---

    ## ๐ŸŽฏ TypeScript Support

    The library is built entirely in **TypeScript** and exports complete type definitions:

    ```ts
    import ScrollSpy, {
    type ScrollSpyOptions,
    type ScrollSpyEvent,
    type ContentPosition
    } from '@fsegurai/test-package-1';

    // Full type safety for all options
    const options: ScrollSpyOptions = {
    offset: 80,
    nested: true,
    };

    // Constructor is fully typed
    const spy = new ScrollSpy('#toc', options);

    // Event detail is typed
    document.addEventListener('gumshoeactivate', (event: Event) => {
    const e = event as CustomEvent;
    const target: Element = e.detail.target;
    const nav: Element = e.detail.nav;
    });
    ```

    `ScrollSpyOptions` includes the navigation selector, offset controls, nested-navigation classes, fragment mapping, and
    the optional MutationObserver toggle. `ScrollSpyEvent` is the shared detail payload for the activation event.

    ---

    ## โœ… Browser Support

    | Browser | Support |
    |---------|---------|
    | Chrome | โœ… |
    | Firefox | โœ… |
    | Safari | โœ… |
    | Edge | โœ… |
    | IE11 | โŒ |

    โš ๏ธ Requires `CustomEvent` support. You may need polyfills for legacy environments.

    ---

    ## ๐Ÿงผ License

    Licensed under [MIT](https://opensource.org/licenses/MIT).