TOC Controls βš™οΈ

Floating Table of Contents

Demonstration of automatic table of contents generation plugin

Introduction to the Plugin

This plugin automatically generates a floating table of contents (TOC) based on the headings in your webpage. It supports many useful features:

Key Features:
  • Automatic scanning of H1-H6 headings
  • ScrollSpy β€” highlighting active sections
  • Smooth scrolling to sections
  • Responsive design for mobile devices
  • Support for light and dark themes
  • Flexible configuration through JavaScript
  • ARIA roles for accessibility

Installation and Setup

To get started with the plugin, you need to include the CSS and JavaScript files to your HTML:

<link rel="stylesheet" href="./toc.css">

<body>
    <div id="toc"></div>
    
    <div id="content">
        Your content here.
    </div>
    
    <script type="module">
        import TOC from './toc.js';
    
        const toc = new TOC();
    </script>
</body>
Note: Specify the correct paths to resources on your site.

Basic Usage

The simplest way to initialize the plugin:

new TOC({
    contentSelector: "#content",
    tocSelector: "#toc"
});             
Note: Define your own CSS selectors for TOC. If omitted, "tocSelector" will default to "#toc", "contentSelector" will default to "body".

Advanced Configuration

The plugin supports many options for customization:

new TOC({
    contentSelector: "#content",
    tocSelector: "#toc",
    headingLevels: ["h2", "h3", "h4"],
    scrollSmooth: true,
    scrollSpy: true,
    autoHide: false,
    sticky: true,
    collapsible: false,
    searchable: true,
    theme: "light",
    position: "top-right",
    animation: "bounce",
    size: "compact",
    toggleIcon: "β–Ό",
    offset: 80,
    minHeadings: 2,
    mobileBreakpoint: 768,
    title: 'Table of Contents',
    toggleText: 'Toggle',
    searchPlaceholder: 'Search headings...',
});              
Note: No-code settings are planned to be added in future versions.

How TOC UI Customization Works

To make styling your Table of Contents easy and flexible, the plugin uses CSS variables. Default values are defined in the :root selector and can be overridden later in styles specific to each TOC theme.

How to Override Styles

The plugin supports about 40 CSS variables, giving you full control over appearance. For example, if you don't want rounded corners, simply set:

--toc-border-radius: 0px;

Similarly, you can customize colors, fonts, spacing, shadows, and more β€” everything is fully adjustable.

To save you time, 18 pre-built themes are included, covering a variety of design needs β€” from minimal and clean to bold or decorative.

Configuration and Parameters

Detailed description of all available plugin parameters.

Basic Parameters

These parameters allow you to control the basic behavior of the plugin:

contentSelector

Selector for the container where the plugin will search for headings. Default: "body"

tocSelector

Selector for the element where the generated table of contents will be inserted. Default: "#toc"

headingLevels

Array of heading levels to include in the table of contents. Default: ["h1", "h2", "h3", "h4", "h5", "h6"]

Behavioral Parameters

These options control the interactivity and behavior of the plugin:

scrollSmooth

Enable smooth scrolling when clicking on table of contents items. Default: true

scrollSpy

Enable tracking of active sections during scrolling. Default: true

autoHide

This option hides the TOC if the number of headings on the page is less than the value set in minHeadings. Default: true

sticky

Make the table of contents sticky (always visible during scrolling). Default: true

collapsible

Enables the ability to collapse or expand the TOC. Set to true to allow users to toggle visibility.

searchable

Adds a search input that lets users filter TOC entries by text.

toggleIcon

Specifies the icon used for the collapse/expand toggle button.

Possible icon options include: text characters (e.g., Β», ⟩, //, ☰ etc.), emojis (e.g., , ↕️ 🐾, βš™οΈ), or inline SVG.

const tocInstance = new TOC({
    // Blue triangle pointing downward:
    toggleIcon: `<svg viewBox="0 0 100 100" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
        <polygon points="0,0 100,0 50,0" fill="#2196F3" />
    </svg>`,
    // or
    // Example using emoji:
    toggleIcon: 'πŸ“Ž'
});

When the TOC is collapsed, the toggle icon rotates 90Β° counterclockwise. To change this behavior, override the relevant styles in toc.css as needed.

/* in toc.css */
.toc-toggle[aria-expanded="true"] .toc-toggle-icon {
    transform: rotate(90deg); // here
}

.toc-toggle[aria-expanded="false"] .toc-toggle-icon {
    transform: rotate(-90deg); // and here
}

offset

Vertical offset in pixels used to adjust the scroll position when highlighting active TOC items β€” useful if your page has a fixed header.

title

The title displayed above the Table of Contents list.

toggleText

The text label for the collapse/expand button. A single string.

searchPlaceholder

The placeholder text shown inside the TOC search input field.

Styling

Parameters for customizing appearance:

theme

Theme style: "light", "dark", "aurora", "forest", "neon", "ocean", "sunset", "lavender", "mono", "mint", "royal", "rose", "electric", "autumn", "crystal", "cosmic", "emerald" or "vintage". Default: "light"

position

Position of the table of contents on the page: "top-left", "top-right", "bottom-left" or "bottom-right". Default: "top-right"

Note: By default, only the "bottom" position is available on mobile. These styles are easy to override for convenience. There are also plans to expand the set of available positions.

size

Size (width) of the table of contents on the page: "narrow", "compact", "normal", and "wide". Default: "normal".

Size changed by "toc.setSize(newSize)" method.

animation

Animation of the table of contents on the page: "slide", "fade", "bounce", or "none". Default: "slide".

Usage Examples

Different scenarios for using the plugin in real projects.

Blog or Documentation

Perfect solution for long articles and documentation:

// For blog with large articles
new TOC({
    contentSelector: ".article-content",
    tocSelector: "#article-toc",
    headingLevels: ["h2", "h3"],
    theme: "light",
    position: "top-right"
});             

Technical Documentation

For technical documentation with deep nesting:

// For technical documentation
new TOC({
    contentSelector: ".docs-content",
    tocSelector: "#docs-toc",
    headingLevels: ["h1", "h2", "h3", "h4"],
    minHeadings: 3,
    collapsible: true
});             

Mobile Optimization

The plugin automatically adapts to mobile devices, but you can customize the behavior:

new TOC({
    mobileBreakpoint: 480,
    // On mobile devices TOC will be at the bottom of the screen
});             

Custom Theme

Creating custom themes through CSS variables:

.toc-custom {
   --toc-bg: #2f2620;
   --toc-bg-secondary: #3d3328;
   --toc-border: #8b7355;
   --toc-border-secondary: #a0855b;
   --toc-text: #f5e6d3;
   --toc-text-secondary: #e6d7c3;
   --toc-text-tertiary: #d4c4a8;
   --toc-hover: #4a3f32;
   --toc-active: #cd853f;
   --toc-active-bg: #3d3328;
   --toc-active-border: #cd853f;
   --toc-shadow: 0 8px 24px rgba(139, 115, 85, 0.3);
   --toc-shadow-hover: 0 12px 32px rgba(139, 115, 85, 0.5);
   --toc-backdrop: rgba(47, 38, 32, 0.85);
   --toc-accent: #cd853f;
   --toc-accent-light: #3d3328;
   --toc-gradient: linear-gradient(135deg, #2f2620 0%, #3d3328 50%, #4a3f32 100%);
   --toc-font-family: cursive;
   --toc-font-size: 16px;
   --toc-font-size-small: 15px;
   --toc-font-size-large: 17px;
   --toc-font-size-title: 18px;
   --toc-line-height: 1.2;
   --toc-font-weight: 700;
   --toc-font-weight-semibold: 900;
}               

Specify the theme name in one word. Use this name when initializing the plugin. That is: the ".toc-custom" class is responsible for styling the "custom" theme. You can override all properties or just a part of them.

"custom" is just an example. It could be "mystic" or "lemonade", depending on your taste.

API and Methods

Programming interface for controlling the plugin after initialization.

Main Methods

After creating a TOC instance, the following methods are available:

refresh()

Re-scans the page and updates the table of contents. Useful when dynamically adding content:

const toc = new TOC(options);
// After adding new content
toc.refresh();  

destroy()

Removes the plugin, event listeners and cleans up the DOM:

toc.destroy();  

setTheme(theme)

Dynamically changes the theme style (e.g., "light", "dark", "aurora", "forest", "neon", "ocean", "sunset", "lavender", "mono", "mint", "royal", "rose", "electric", "autumn", "crystal", "cosmic", "emerald", "vintage", or "custom"):

toc.setTheme("dark");

setPosition(position)

Changes the position of the TOC (e.g., "top-left", "top-right", "bottom-left", "bottom-right", "center-left", "center-right"):

toc.setPosition("bottom-right");

setSize(size)

Changes the size of the TOC (e.g., "compact", "normal", "wide" or "narrow"):

toc.setSize("wide");

setToggleIcon(icon)

Changes the icon used for the toggle button:

toc.setToggleIcon("☰");

show()

Makes the TOC visible if it was hidden:

toc.show();     

hide()

Hides the TOC from the page:

toc.hide();     

search(query)

Filters the TOC items by the given search query (only available if searchable is enabled):

toc.search("Introduction");

clearSearch()

Clears any applied search filter and restores the full TOC:

toc.clearSearch();

getHeadings()

Returns an array of all headings detected on the page:

const headings = toc.getHeadings();

getFilteredItems()

Returns an array of TOC items that match the current search filter:

const visibleItems = toc.getFilteredItems();

getConfig()

Returns a copy of the current configuration:

const config = toc.getConfig();

isVisible()

Returns true if the TOC is currently visible, otherwise false:

if (toc.isVisible()) {
    // TOC is shown
}               

isSearchable()

Returns true if the TOC is configured to be searchable:

if (toc.isSearchable()) {
    // Search input is enabled
}               

Events

Plugin-generated events that you can subscribe to:

Note: Events will be added in the next version of the plugin for even greater integration flexibility.

Framework Integration

How to use the plugin with popular JavaScript frameworks.

React

Example integration with React component (in root folder for this example):

Create useTOC.js:

import { useEffect, useRef } from 'react';
import TOC from './toc.js';

export const useTOC = (options = {}) => {
    const tocInstanceRef = useRef(null);

    useEffect(() => {
        // Initialization of TOC
        const initTOC = () => {
            if (tocInstanceRef.current) {
                tocInstanceRef.current.destroy();
            }
        
            tocInstanceRef.current = new TOC(options);
        };
    
        // Delay to ensure that the DOM is ready
        const timeoutId = setTimeout(initTOC, 100);
    
        return () => {
            clearTimeout(timeoutId);
            if (tocInstanceRef.current) {
                tocInstanceRef.current.destroy();
            }
        };
    }, [options]);
    
    // Returning methods for TOC management
    return {
        refresh: () => tocInstanceRef.current?.refresh(),
        destroy: () => tocInstanceRef.current?.destroy(),
        setTheme: (theme) => tocInstanceRef.current?.setTheme(theme),
        setPosition: (position) => tocInstanceRef.current?.setPosition(position),
        setSize: (size) => tocInstanceRef.current?.setSize(size),
        show: () => tocInstanceRef.current?.show(),
        hide: () => tocInstanceRef.current?.hide(),
        search: (query) => tocInstanceRef.current?.search(query),
        clearSearch: () => tocInstanceRef.current?.clearSearch(),
        getHeadings: () => tocInstanceRef.current?.getHeadings() || [],
        getFilteredItems: () => tocInstanceRef.current?.getFilteredItems() || [],
        getConfig: () => tocInstanceRef.current?.getConfig() || {},
        isVisible: () => tocInstanceRef.current?.isVisible() || false,
        isSearchable: () => tocInstanceRef.current?.isSearchable() || false
    };
};              

Create TocComponent.js:

import React, { useEffect, useRef } from 'react';
import { useTOC } from './useTOC';

const TocComponent = ({
    contentSelector = 'body',
    headingLevels = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
    theme = 'light',
    position = 'top-right',
    size = 'normal',
    title = 'Table of Contents',
    toggleIcon = '⟩',
    scrollSmooth = true,
    scrollSpy = true,
    sticky = true,
    collapsible = true,
    searchable = false,
    autoHide = false,
    animation = 'slide',
    offset = 80,
    minHeadings = 2,
    mobileBreakpoint = 768
}) => {
    const tocRef = useRef(null);

    const tocOptions = {
        contentSelector,
        tocSelector: '#toc',
        headingLevels,
        theme,
        position,
        size,
        title,
        toggleIcon,
        scrollSmooth,
        scrollSpy,
        sticky,
        collapsible,
        searchable,
        autoHide,
        animation,
        offset,
        minHeadings,
        mobileBreakpoint
    };

    const tocControls = useTOC(tocOptions);

    return (
        <div
            id="toc"
            ref={tocRef}
            className="toc"
        >
            {/* TOC will be generated automatically */}
        </div>
    );
};

export default TocComponent;

In App.js:

import './App.css';
import './toc.css';
import TocComponent from './TocComponent';

function App() {
    return (
        <div className="App">
            // Your main code
        </div>
        
        <div className="app">
            <TocComponent
                contentSelector=".content"
                theme="dark"
                position="top-right"
                searchable={true}
                collapsible={true}
            />

            <div className="content">
                // Your article here
            </div>
        </div>
    </div>
  );
}               

Vue.js

Example usage in Vue component:

Create useToc.js (in root for this example):

import TOC from "./utils/toc.js"; // or minified version
import { onMounted, onBeforeUnmount } from "vue";

export function useToc(options = { contentSelector: "#content", tocSelector: "#toc" }) {
let toc = null;

onMounted(() => {
    console.log("contentSelector:", document.querySelector(
    console.log("tocSelector:", document.querySelec>
    toc = new TOC(options);
});

onBeforeUnmount(() => {
    if (toc) {
        toc.destroy();
    }
});

   return { toc };
}               

In ./components/TOC.vue:

<script>
    import { useToc } from "../useToc";
    
    export default {
        props: {
            contentSelector: {
                type: String,
                default: "#content",
            },
            tocSelector: {
                type: String,
                default: "#toc",
            },
        },
        setup(props) {
            const { toc } = useToc({
                contentSelector: props.contentSelector,
                tocSelector: props.tocSelector,
            });
            
            return { toc };
        },
    };
</script>

<template>
    <div :id="tocSelector.replace('#', '')"></div>
</template>    

In ./components/Content.vue:

<template>
    <div id="content">
        Your article here.
    </div>
</template>  

In App.vue:

<script setup>
    import TheWelcome from "./components/TheWelcome.vue";
    import TOC from "./components/TOC.vue";
    import Content from "./components/Content.vue";
</script>

<template>
    <main>
        <div>
            <TheWelcome />
            <Content />
        </div>
    </main>
    
    <aside>
        <TOC content-selector="#content" toc-select>
    </aside>
</template>

In main.js:

import "./assets/main.css";
import "./assets/toc.css";

import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);
app.mount("#app");
            

WordPress

Integration with WordPress themes:

Note: will be implemented in the next updates.

Performance and Optimization

Recommendations for optimal plugin performance.

Performance Optimization

The plugin is optimized for minimal performance impact:

Usage Recommendations

For best performance:

Tips:
  • Use specific selectors instead of 'body'
  • Limit the number of heading levels
  • Call refresh() only when necessary
  • Remove plugin through destroy() when leaving the page

Performance Testing

The plugin has been tested on pages with large amounts of content:

Accessibility and UX

The plugin is developed with accessibility principles in mind.

Accessibility Support

Built-in accessibility features:

User Experience

Thoughtful UX for different scenarios:

Desktop Experience

Mobile Experience

Extensions and Customization

How to extend plugin functionality for your needs.

Custom Theme

How to add a custom theme: go to Custom Theme.

Additional Styles

Examples of custom styles:

/* Minimalist style */
.toc-minimal {
    background: transparent;
    border: none;
    box-shadow: none;
}

.toc-minimal .toc-link {
    border-left: 2px solid transparent;
    transition: border-color 0.2s;
}

.toc-minimal .toc-item.active .toc-link {
    border-left-color: #007bff;
    background: transparent;
}               

For example, you may add class toc-minimal to the ul.

JavaScript Extensions

Example of extending functionality:

// Adding progress bar in toc.js
class ExtendedTOC extends TOC {
    constructor(options) {
        super(options);
        this.addProgressBar();
    }

    addProgressBar() {
        const progressBar = document.createElement('div');
        progressBar.className = 'toc-progress';
        this.tocElement.appendChild(progressBar);
        
        window.addEventListener('scroll', () => {
            const progress = this.calculateProgress();
            progressBar.style.width = `${progress}%`;
        });
    }

    calculateProgress() {
        const scrollTop = window.pageYOffset;
        const docHeight = document.documentElement.scrollHeight - window.innerHeight;
        return (scrollTop / docHeight) * 100;
    }
}

// Adding styles in toc.css
.toc-progress {
    position: absolute;
    top: 0.5rem;
    height: 4px;
    z-index: 1000;
    background-color: teal;
}               

Support and FAQ

Answers to frequently asked questions and troubleshooting tips.

Frequently Asked Questions

Why isn't the TOC generating?

Make sure that:

How to change TOC styles?

Use CSS variables or override styles:

/* Overriding styles */
.toc {
    --toc-bg: #f8f9fa;
    --toc-border-radius: 20px;
}

.toc-link {
    font-weight: 500;
}               

ScrollSpy not working correctly

Possible causes:

Browser Support

The plugin supports all modern browsers:

Known Limitations

Conclusion

Floating TOC Plugin is a powerful tool for improving navigation through long content. It integrates easily, has many settings, and adapts to any design.

Summary of Benefits:
  • βœ… Zero dependencies β€” works with vanilla JavaScript
  • βœ… Easy integration β€” 2 files to include
  • βœ… Responsive design β€” works on all devices
  • βœ… High performance β€” optimized code
  • βœ… Accessibility β€” screen reader support
  • βœ… Customization β€” flexible style settings

Try different settings using the control panel on the top. The plugin is ready for production use!