jQuery / Plugin / jquery.toc — Criar sumário a partir de títulos com rolagem suave
jquery.toc reúne títulos h1–h6 de uma área e gera um sumário com links. Chame .toc({ container: "#contents" }) no artigo para inserir a lista em outro lugar; o clique rola até o título. Separe artigo e sumário e defina níveis e reconstrução.
Preparar fonte e documento
É o jquery.toc de jakobbossek, sem misturar opções de outros plugins semelhantes. A fonte v1.1 revisada está fixada em um commit. Salve o arquivo original jquery.toc.js com esse nome ao lado do HTML, não a página de visualização do GitHub.
Os arquivos locais são toc-demo.html e jquery.toc.js. jQuery completo 4.0.0 vem do CDN. A ordem é jQuery, plugin e inicialização. Slim não tem os efeitos necessários. A mesma fonte foi testada com 4.0.0 e 3.7.1 no Windows Edge.
Todos os :header da área escolhida são reunidos. #article exclui sumário, título do documento e aside. Dois h3 seguem um h2 como subtópicos reais. Não pule níveis para obter aparência. Esta fonte não implementa opção de profundidade máxima.
Exemplo completo com foco e reconstrução
Salve e abra toc-demo.html em UTF-8, sem servidor ou compilação. A internet carrega jQuery. Os títulos são texto simples controlado. O plugin reescreve seu HTML e insere caracteres em strings HTML; não é um exemplo genérico para títulos arbitrários recebidos ou botões e links interativos dentro deles.
<!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; os dois Overview são seções distintas.
Entries: 4
- Confira quatro entradas, com as duas centrais aninhadas. Outside the article fica de fora.
- O último Overview chega em 400ms, deixa 20px acima e recebe contorno de foco.
- Focalize uma entrada pelo teclado e pressione Enter.
- Com movimento reduzido em reduce, a chegada é imediata.
- Rebuild original example volta ao topo e mantém Entries: 4 mesmo repetindo, sem acumular listas ou âncoras.
- Reconstrua durante a rolagem: uma conclusão antiga não deve sobrescrever o estado novo.
Depois de gerar, o exemplo substitui somente os manipuladores originais dos seus links. O complemento anima um único elemento real de rolagem e acrescenta limpeza de fila, distinção de cancelamento, foco e movimento reduzido. O arquivo do plugin não é modificado. Fragmento URL e histórico permanecem iguais.
Chamada básica e opções reais
Para observar o original, mantenha as tags script da inicialização e substitua todo o código interno por isto. Preserve carregamento, artigo e #contents, mas retire botão de reconstrução e estado. A chamada usa a rolagem original sem os complementos de foco, movimento reduzido ou reconstrução.
$("#article").toc({
container: "#contents",
strip: false,
stripAfter: 30,
scrollSpeed: 400,
scrollOffset: 20
});
#article escolhe a fonte; container, o destino da lista. wrapWith define o HTML envolvente, com classe padrão toc_container. Restrinja CSS a #contents.
scrollSpeed é duração em milissegundos. Compare 400 e 800 na chamada básica e restaure. scrollOffset é subtraído da coordenada da âncora interna: positivo 20 deixa margem acima. O complemento completo alinha a caixa do próprio título a 20px, podendo diferir alguns pixels da âncora em linha.
No exemplo completo, alterar apenas essas opções .toc() não muda o manipulador substituto. Altere ali animate 400 ou a subtração 20 e preserve zero para movimento reduzido.
A condição real de abreviação consulta strip. Embora os padrões incluam shorten, shorten:true não ativa esse ramo. strip:true usa substring até stripAfter e acrescenta ... a todas as entradas, mesmo curtas. São unidades de string, não palavras; caracteres combinados como emoji podem ser partidos. Prefira strip:false para títulos completos legíveis.
Âncoras, IDs existentes e limites de integração
A fonte conserva o ID do título e insere dentro um a com ID como hl_112. existing-overview permanece. IDs gerados dependem de área, ordem e nível, não do texto; os rótulos repetidos deste exemplo têm destinos diferentes. Unicode também é reunido, mas reordenar pode mudar IDs. Não use isso como política de links permanentes.
O original acrescenta listas e envolve novamente o HTML a cada chamada. O código completo restaura sua cópia HTML inicial e esvazia o sumário. Assim, não preserva conteúdo adicionado depois, estado de formulário ou eventos diretos. Um editor precisa de outra estratégia para conteúdo atual e IDs estáveis.
Não inicialize junto com tema ou shortcode WordPress que já gere o sumário do mesmo artigo. A prática é separada do sistema deste site. A seleção original de cliques também alcança listas .toc globalmente; não presuma independência entre várias instâncias.
Se não aparecer, confira caminho e conteúdo JavaScript real do arquivo, ordem, títulos e contêiner. Para desalinhamento, veja menu fixo, imagens/fontes tardias e espaço no final. Quebre rótulos longos com CSS e mantenha o artigo legível sem sumário.




