jQuery / Plugin / jquery.toc — Crear un índice desde títulos con desplazamiento suave
jquery.toc recoge títulos h1–h6 dentro de una zona y genera un índice con enlaces. Llame a .toc({ container: "#contents" }) sobre el artículo para insertar la lista en otro lugar; un clic desplaza al título. Separe artículo e índice y defina niveles y reconstrucción.
Preparar fuente y documento
Se trata de jquery.toc de jakobbossek, sin mezclar opciones de otros plugins parecidos. La fuente v1.1 revisada está fijada a un commit. Guarde el archivo original jquery.toc.js con ese nombre junto al HTML, no la página de visualización de GitHub.
Los archivos locales son toc-demo.html y jquery.toc.js. jQuery completo 4.0.0 viene del CDN. El orden es jQuery, plugin e inicialización. Slim carece de los efectos necesarios. La misma fuente se probó con 4.0.0 y 3.7.1 en Windows Edge.
Se recogen todos los :header del área elegida. #article deja fuera índice, título del documento y aside. Dos h3 siguen a un h2 como subtemas reales. No use los niveles solo para decorar. Esta fuente no implementa una opción de profundidad máxima.
Ejemplo completo con foco y reconstrucción
Guarde y abra toc-demo.html en UTF-8 sin servidor ni compilación. Internet carga jQuery. Los títulos son texto sencillo controlado. El plugin reescribe su HTML y coloca caracteres en cadenas HTML; no es un ejemplo general para títulos arbitrarios recibidos ni botones y enlaces interactivos dentro de ellos.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Generate a scoped table of contents</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script src="jquery.toc.js"></script>
<style>
html { scroll-behavior: auto; }
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 20px; }
#contents { border: 1px solid teal; padding: 16px; }
#contents ul { padding-left: 24px; }
#contents a { overflow-wrap: anywhere; }
#article p { min-height: 440px; }
h2:focus, h3:focus { outline: 3px solid teal; }
button { font: inherit; }
footer { min-height: 100vh; }
</style>
</head>
<body>
<h1>A focused table of contents</h1>
<nav id="contents" aria-label="Article contents"><p>Contents will be generated when scripts load.</p></nav>
<button type="button" id="rebuild">Rebuild original example</button>
<p id="status" role="status">Article text is available below.</p>
<main id="article">
<h2 id="existing-overview">Overview</h2><p>First main topic with an existing stable ID.</p>
<h3>Preparation</h3><p>First subtopic.</p>
<h3>Verification</h3><p>Second subtopic.</p>
<h2>Overview</h2><p>A different section with the same displayed title.</p>
</main>
<aside><h2>Outside the article</h2><p>This heading must not enter the contents.</p></aside>
<footer>Space for the final destination.</footer>
<script>
jQuery(function ($) {
if (typeof $.fn.toc !== "function") return;
const $article = $("#article"), $toc = $("#contents"), original = $article.html();
const $scroller = $(document.scrollingElement);
let generation = 0;
function build() {
generation++; $scroller.stop(true, false);
$article.html(original); $toc.empty().off("click.demo");
$article.toc({ container: "#contents", strip: false, stripAfter: 30, scrollSpeed: 400, scrollOffset: 20 });
// Replace this example's generated links' click handlers only.
$toc.find("a").off("click");
$toc.on("click.demo", "a[href^='#hl_']", function (event) {
const anchor = document.getElementById(this.hash.slice(1));
if (!anchor) return;
event.preventDefault();
const run = ++generation, heading = anchor.parentElement;
$scroller.stop(true, false);
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
$scroller.animate({ scrollTop: Math.max(0, $(heading).offset().top - 20) }, reduce ? 0 : 400, function () {
if (run !== generation) return;
heading.tabIndex = -1; heading.focus({ preventScroll: true });
$("#status").text("Arrived: " + heading.textContent);
});
});
$("#status").text("Entries: " + $toc.find("a").length);
}
$("#rebuild").on("click", function () { build(); window.scrollTo(0, 0); });
build();
});
</script>
</body>
</html>
Resultado inicial; los dos Overview representan secciones diferentes.
Entries: 4
- Compruebe cuatro entradas con las dos centrales anidadas. Outside the article queda fuera.
- El último Overview llega en 400ms, deja 20px arriba y recibe contorno de foco.
- Enfoque una entrada con el teclado y pulse Enter.
- Con movimiento reducido en reduce la llegada es inmediata.
- Rebuild original example vuelve arriba y mantiene Entries: 4 al repetir, sin listas o anclas acumuladas.
- Reconstruya durante el movimiento: una finalización antigua no debe sobrescribir el nuevo estado.
Después de generar, el ejemplo reemplaza solo los manejadores originales de sus enlaces. El complemento anima un único elemento de desplazamiento real y añade limpieza de cola, distinción de cancelaciones, foco y movimiento reducido. No modifica el archivo del plugin. No cambia fragmento URL ni historial.
Llamada básica y opciones reales
Para observar el original, conserve las etiquetas script de inicialización y reemplace todo su código interior por esto. Mantenga carga, artículo y #contents, pero quite botón y estado de reconstrucción. La llamada usa el desplazamiento original sin foco, movimiento reducido o control de reconstrucción adicionales.
$("#article").toc({
container: "#contents",
strip: false,
stripAfter: 30,
scrollSpeed: 400,
scrollOffset: 20
});
#article elige la fuente y container el lugar de inserción. wrapWith define el HTML envolvente, con clase predeterminada toc_container. Limite CSS a #contents.
scrollSpeed es duración en milisegundos. Compare 400 y 800 en la llamada básica y vuelva al original. scrollOffset se resta a la coordenada del ancla interna; positivo 20 deja espacio superior. El código completo alinea la caja del título a 20px, que puede diferir unos píxeles del ancla en línea.
En el ejemplo completo, cambiar solo esas opciones .toc() no altera el manejador sustituto. Cambie allí animate 400 o la resta 20 y mantenga el cero para movimiento reducido.
La condición de truncado consulta strip. Aunque los valores iniciales incluyen shorten, shorten:true no activa la rama. strip:true aplica substring hasta stripAfter y agrega ... a todas las entradas, incluso cortas. Cuenta unidades de cadena, no palabras; puede partir caracteres combinados como emoji. Prefiera strip:false para títulos completos legibles.
Anclas, ID existentes y límites de integración
La fuente conserva el ID del título e inserta dentro un a identificado como hl_112. existing-overview permanece. Los ID generados dependen de área, orden y nivel, no del texto; las etiquetas repetidas de este ejemplo tienen destinos distintos. También recoge Unicode, pero reordenar puede cambiar los ID. No los tome como una política de enlaces permanentes.
El original agrega listas y vuelve a envolver el HTML al repetir. El código completo restaura su copia HTML inicial y vacía el índice. No conserva contenido añadido después, estados de formulario ni eventos directos. Un editor necesita otra estrategia para contenido actual e ID estables.
No lo inicialice junto a un tema o shortcode WordPress que ya genere el índice del mismo artículo. La práctica es distinta del sistema de este sitio. La selección original de clics alcanza globalmente listas .toc; no dé por supuesta la independencia de varias instancias.
Si no aparece, revise ruta y contenido JavaScript real del archivo, orden, títulos y contenedor. Si se desalinea, compruebe menú fijo, imágenes/fuentes tardías y espacio final. Envuelva etiquetas largas con CSS y mantenga legible el artículo sin índice.




