sinanisler logo

Dynamic Table of Contents with Nested Headings Shortcode, WordPress

This shortcode dynamically generates a table of contents with proper parent-child nesting based on heading levels (h2, h3, etc.) within a specified container, enhancing content navigation for your WordPress posts or pages.

shortcode: [custom_tableofcontents class_name=”.your-custom-container”]

// [custom_tableofcontents class_name=".your-custom-container"] shortcode

function custom_table_of_contents_shortcode($atts) {
    // Set default class as '.post-contents' if not provided in the shortcode
    $atts = shortcode_atts(
        array(
            'class_name' => '.post-contents', // default container class
        ), 
        $atts, 
        'custom_tableofcontents'
    );
    
    // Output the div for the TOC and the JavaScript
    ob_start();
    ?>
    <div id="table-of-contents"></div>
    <script>
    document.addEventListener("DOMContentLoaded", function() {
        // Get the container element based on the class_name attribute
        var contentContainer = document.querySelector("<?php echo esc_attr($atts['class_name']); ?>");
        if (!contentContainer) return; // Exit if the container isn't found

        var headings = contentContainer.querySelectorAll("h2, h3, h4, h5, h6");
        var tocContainer = document.getElementById("table-of-contents");
        var tocList = document.createElement("ul");

        var currentLevel = 2; // Start from h2
        var currentList = tocList; // Main list is the root level list

        headings.forEach(function(heading, index) {
            var level = parseInt(heading.tagName.substring(1)); // Get the heading level (2, 3, 4, etc.)
            var anchorId = "toc-heading-" + index;
            heading.setAttribute("id", anchorId); // Add unique ID to the heading

            var listItem = document.createElement("li");
            var link = document.createElement("a");
            link.setAttribute("href", "#" + anchorId);
            link.textContent = heading.textContent;
            listItem.appendChild(link);

            // Adjust list levels based on heading level
            if (level > currentLevel) {
                // Create a new nested list
                var nestedList = document.createElement("ul");
                currentList.lastElementChild.appendChild(nestedList); // Nest it in the previous item
                currentList = nestedList;
            } else if (level < currentLevel) {
                // Go back up to the correct level
                while (level < currentLevel) {
                    currentList = currentList.parentElement.parentElement;
                    currentLevel--;
                }
            }
            currentLevel = level; // Update the current level

            currentList.appendChild(listItem); // Add the item to the current list
        });

        tocContainer.appendChild(tocList); // Append the final TOC list to the container
    });
    </script>
    <?php
    return ob_get_clean(); // Output the HTML and JavaScript
}

// Register the shortcode
add_shortcode('custom_tableofcontents', 'custom_table_of_contents_shortcode');

Leave the first comment