Crea una tabla de contenidos sin plugins

¿Quieres que tus lectores naveguen por tu contenido de manera sencilla? Las tablas de contenidos son tu solución. Son como un mapa que guía a tus visitantes a través de tus publicaciones, brindándoles una experiencia de lectura más agradable. Además, ¡puedes crearlas sin necesidad de plugins!

Código PHP para crear tu tabla de contenidos

Puedes agregar el siguiente código en el archivo functions.php de tu plantilla, agregarle la cabecera de un plugin para ejecutarlo o incluirlo en tu proyecto si ya estás programando.

Lo que hace este código es reemplazar el shortcode [tabla-contenido] por la tabla de contenidos según lo que encuentre en tu artículo. Te recomiendo leer los comentarios para orientarte en cada paso.

Asegúrate que la configuración de tu PHP tenga habilitada la clase DOMDocument.


<?php

add_filter('the_content', 'rnz_agregar_toc'], 9); // Puedes cambiar el 9 por otro número. 9 es para que solo lea los encabezados del artículo sin contar los generados por shortcodes u otros plugins.

function rnz_agregar_toc($content = ''){
    /* Colocamos la condicional que valide para continuar:
        * 1. Si es la página de una entrada, página o CPT
        * 2. Si tiene el shortcode [tabla-contenido] en alguna parte
        * 3. Si la clase DOMDocument está habilitada
    */

    if( !is_singular() || !strpos($content, '[tabla-contenido]') || !class_exists('\DOMDocument') ) return $content;

    $headings = [];
    $dom = new \DOMDocument;
    libxml_use_internal_errors(true);
    $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));

    $currentLevel = 0;

    // Busca etiquetas H1, H2, H3, H4, H5 y H6
    foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $node) {
        if ($node->nodeType === XML_ELEMENT_NODE && preg_match('/^h([1-6])$/i', $node->tagName, $matches)) {
            $level = intval($matches[1]);
    
            // Si el nivel actual es mayor que el nivel anterior, anidamos el encabezado
            while ($level > $currentLevel) {
                $currentLevel++;
            }
    
            // Si el nivel actual es menor que el nivel anterior, cerramos anidamientos
            while ($level < $currentLevel) {
                $currentLevel--;
            }
    
            // Agregar el encabezado al arreglo
            $text = $node->textContent;
            $slug = sanitize_title($text);
            $node->setAttribute('id', $slug); // Asignamos un ID al encabezado

            $headings[] = '<li class="autoc-level-'. $currentLevel . '"><a href="#' . $slug . '">' . $text . '</a></li>';
        }
    }

    $content = $dom->saveHTML(); // Guardamos los ID que agregamos a cada encabezado
    
    if(is_array($headings) && !empty($headings)){
        $toc = '<div class="rnz-toc">'
            . '<h2>Tabla de contenidos</h2>'
            . '<ul>' . implode('', $headings) . '</ul>' // Une todas las cabeceras encontradas
            . '</div>';

        $content = str_replace('[tabla-contenido]', $toc, $content);
    }

    return $content;
}

Código jQuery para crear una tabla de contenidos

Si deseas puedes optar por una versión jQuery que crea la tabla de contenidos «on-the-fly» o en el momento para tus artículos.


;(function($) {
    $(document).ready(function(){
        var $autoc = $(".autoc");
        var $content = $autoc.parent();
        var stopAt = $autoc.data("stopat");
        var hs = [];
        switch(stopAt){
            case "h6":
                hs.push("h6");
            case "h5":
                hs.push("h5");
            case "h4":
                hs.push("h4");
            case "h3":
                hs.push("h3");
            case "h2":
                hs.push("h2");
        }
        hs = hs.join();
        var $heads = $content.find(hs);
        if($heads.length === 0){
            return;
        }
        var toc = "";
        toc += "<div class="rnz-toc"><h2>Tabla de Contenidos</h2><ul>";
        $heads.each(function(){
            var $this = $(this);
            var tag = $this[0].tagName;
            var txt = $this.text();
            var slug = slugify(txt);
            $this.attr("data-linked",slug);
            toc += '<li class="autoc-level-'+tag+'">';
            toc += '<a href="#" data-linkto="'+slug+'">'+txt+"</a></li>";
        });
        toc += "</ul></div>";
        $autoc.append(toc);
        $(".autoc ul").on("click", "a", function(e){
            e.preventDefault();
            $([document.documentElement, document.body]).animate({
                scrollTop: $content.find('[data-linked="'+$(this)
                    .attr("data-linkto")+'"]').offset()
                    .top - parseInt($autoc.attr("data-offset"), 10)
            }, 2000);
        });
    });
    function slugify(text){
        return text.toString().toLowerCase()
            .replace(/\s+/g, "-")           // Replace spaces with -
            .replace(/[^\w\-]+/g, "")       // Remove all non-word chars
            .replace(/\-\-+/g, "-")         // Replace multiple - with single -
            .replace(/^-+/, "")             // Trim - from start of text
            .replace(/-+$/, "");            // Trim - from end of text
    }
})(jQuery);

Para que este código funcione debes agregar el siguiente código HTML en tu artículo:


<div class="autoc" data-stopat="h3" data-offset="30"></div>

Donde data-stopat es hasta qué nivel de encabezado debería considerar y data-offset es el margen que debe considerar para scrollear sin tapar la tabla de contenidos.

Deja una respuesta