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:
- 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>
Basic Usage
The simplest way to initialize the plugin:
new TOC({
contentSelector: "#content",
tocSelector: "#toc"
});
"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...',
});
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"
"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:
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:
Performance and Optimization
Recommendations for optimal plugin performance.
Performance Optimization
The plugin is optimized for minimal performance impact:
- Uses debouncing for scroll and resize event handling
- Caches element positions for quick access
- Minimal DOM manipulations
- Efficient CSS selectors
Usage Recommendations
For best performance:
- 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:
- Up to 1000 headings β excellent performance
- Initialization time < 50ms on average device
- Minimal impact on FCP and LCP metrics
Accessibility and UX
The plugin is developed with accessibility principles in mind.
Accessibility Support
Built-in accessibility features:
- Semantic HTML structure
- ARIA attributes for screen readers
- Keyboard navigation support
- High contrast mode compatibility
- Reduced motion settings
User Experience
Thoughtful UX for different scenarios:
Desktop Experience
- Smooth animations and transitions
- Intuitive hover effects
- Position preservation during scrolling
Mobile Experience
- Responsive design
- Touch-friendly buttons
- Compact view for mobile
- Collapsed by default
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:
- The content container exists
- The content has headings of specified levels
- The TOC element exists in the DOM
- There are no JavaScript errors in the console
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:
- Incorrect offset β try increasing the value
- Conflict with other sticky elements
- Dynamic content changes heights β call refresh()
Browser Support
The plugin supports all modern browsers:
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
Known Limitations
- Does not work with Internet Explorer
- Requires JavaScript to function
- CSS Flex support is mandatory
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.
- β 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!