Ein perfektes Inhaltsverzeichnis mit HTML + CSS PlatoBlockchain Data Intelligence. Vertikale Suche. Ai.

Ein perfektes Inhaltsverzeichnis mit HTML + CSS

Anfang dieses Jahres habe ich ein E-Book mit dem Titel selbst veröffentlicht JavaScript-Versprechen verstehen (kostenlos zum Download). Obwohl ich nicht die Absicht hatte, es in ein gedrucktes Buch zu verwandeln, fragten genügend Leute nach einer gedruckten Version, sodass ich beschloss, sie ebenfalls selbst zu veröffentlichen. Ich dachte, es wäre eine einfache Übung, HTML und CSS zu verwenden ein PDF erzeugen und dann an den Drucker schicken. Was mir nicht klar war, war, dass ich auf einen wichtigen Teil eines gedruckten Buches keine Antwort hatte: das Inhaltsverzeichnis.

Der Aufbau eines Inhaltsverzeichnisses

Im Kern ist ein Inhaltsverzeichnis ziemlich einfach. Jede Zeile stellt einen Teil eines Buchs oder einer Webseite dar und gibt an, wo Sie diesen Inhalt finden können. Typischerweise enthalten die Zeilen drei Teile:

  1. Der Titel des Kapitels oder Abschnitts
  2. Führungslinien (dh Punkte, Striche oder Linien), die den Titel visuell mit der Seitenzahl verbinden
  3. Die Seitenzahl

Ein Inhaltsverzeichnis ist in Textverarbeitungstools wie Microsoft Word oder Google Docs einfach zu erstellen, aber da mein Inhalt in Markdown war und dann in HTML umgewandelt wurde, war das keine gute Option für mich. Ich wollte etwas Automatisiertes, das mit HTML funktioniert, um das Inhaltsverzeichnis in einem für den Druck geeigneten Format zu generieren. Ich wollte auch, dass jede Zeile ein Link ist, damit sie in Webseiten und PDFs verwendet werden kann, um im Dokument zu navigieren. Ich wollte auch Punktführer zwischen dem Titel und der Seitenzahl.

Und so begann ich zu recherchieren.

Ich bin auf zwei hervorragende Blog-Beiträge zum Erstellen eines Inhaltsverzeichnisses mit HTML und CSS gestoßen. Das erste war „Erstellen Sie ein Inhaltsverzeichnis aus Ihrem HTML“ von Julia Blanc. Julie arbeitete weiter PagedJS, ein Polyfill für fehlende ausgelagerte Medienfunktionen in Webbrowsern, das Dokumente für den Druck richtig formatiert. Ich habe mit Julies Beispiel angefangen, aber festgestellt, dass es bei mir nicht ganz funktioniert hat. Als nächstes fand ich Christoph Grabos „Responsive Inhaltsverzeichnis-Leaderlines mit CSS“ post, in dem das Konzept der Verwendung von CSS Grid (im Gegensatz zu Julies Float-basiertem Ansatz) eingeführt wurde, um die Ausrichtung zu vereinfachen. Aber wieder einmal war sein Ansatz für meine Zwecke nicht ganz richtig.

Nachdem ich diese beiden Beiträge gelesen hatte, hatte ich jedoch das Gefühl, dass ich die Layoutprobleme gut genug verstanden hatte, um mich selbstständig zu machen. Ich habe Teile aus beiden Blogbeiträgen verwendet und dem Ansatz einige neue HTML- und CSS-Konzepte hinzugefügt, um ein Ergebnis zu erzielen, mit dem ich zufrieden bin.

Auswahl des richtigen Markups

Bei der Entscheidung über das richtige Markup für ein Inhaltsverzeichnis habe ich vor allem an die richtige Semantik gedacht. Grundsätzlich handelt es sich bei einem Inhaltsverzeichnis um einen Titel (Kapitel oder Unterabschnitt), der an eine Seitenzahl gebunden ist, fast wie ein Schlüssel-Wert-Paar. Das führte mich zu zwei Optionen:

  • Eine Möglichkeit ist die Verwendung einer Tabelle (<table>) mit einer Spalte für den Titel und einer Spalte für die Seite.
  • Dann gibt es noch die oft ungenutzte und vergessene Definitionsliste (<dl>) Element. Es fungiert auch als Key-Value-Map. Die Beziehung zwischen dem Titel und der Seitenzahl wäre also wieder einmal offensichtlich.

Beides schien eine gute Option zu sein, bis mir klar wurde, dass sie wirklich nur für einstufige Inhaltsverzeichnisse funktionieren, nämlich nur, wenn ich ein Inhaltsverzeichnis nur mit Kapitelnamen haben wollte. Wenn ich jedoch Unterabschnitte im Inhaltsverzeichnis anzeigen wollte, hatte ich keine guten Optionen. Tabellenelemente eignen sich nicht gut für hierarchische Daten, und obwohl Definitionslisten technisch verschachtelt werden können, schien die Semantik nicht korrekt zu sein. Also ging ich zurück ans Reißbrett.

Ich beschloss, auf Julies Ansatz aufzubauen und eine Liste zu verwenden; Ich habe mich jedoch für eine geordnete Liste entschieden (<ol>) anstelle einer ungeordneten Liste (<ul>). Ich denke, eine geordnete Liste ist in diesem Fall angemessener. Ein Inhaltsverzeichnis stellt eine Liste von Kapiteln und Unterüberschriften in der Reihenfolge dar, in der sie im Inhalt erscheinen. Die Reihenfolge ist wichtig und sollte nicht im Markup verloren gehen.

Leider bedeutet die Verwendung einer geordneten Liste, dass die semantische Beziehung zwischen dem Titel und der Seitenzahl verloren geht, also bestand mein nächster Schritt darin, diese Beziehung innerhalb jedes Listenelements wiederherzustellen. Der einfachste Weg, dies zu lösen, besteht darin, einfach das Wort „Seite“ vor der Seitenzahl einzufügen. So ist auch ohne weitere optische Unterscheidung der Bezug der Zahl zum Text eindeutig.

Hier ist ein einfaches HTML-Skelett, das die Grundlage meines Markups bildete:

<ol class="toc-list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol> <!-- subsection items --> </ol> </li>
</ol>

Anwenden von Stilen auf das Inhaltsverzeichnis

Nachdem ich das Markup festgelegt hatte, das ich verwenden wollte, bestand der nächste Schritt darin, einige Stile anzuwenden.

Zuerst habe ich die automatisch generierten Nummern entfernt. Sie können die automatisch generierten Nummern in Ihrem eigenen Projekt behalten, wenn Sie möchten, aber es ist üblich, dass Bücher nicht nummerierte Vor- und Nachwörter in der Kapitelliste enthalten, wodurch die automatisch generierten Nummern falsch sind.

Für meinen Zweck würde ich die Kapitelnummern manuell eingeben und dann das Layout so anpassen, dass die Liste der obersten Ebene keine Auffüllung hat (und sie somit an Absätzen ausrichtet) und jede eingebettete Liste um zwei Leerzeichen eingerückt ist. Ich entschied mich für a 2ch padding value, weil ich noch nicht ganz sicher war, welche Schriftart ich verwenden würde. Das ch Die Längeneinheit ermöglicht es, dass die Auffüllung relativ zur Breite eines Zeichens ist – unabhängig davon, welche Schriftart verwendet wird – und nicht eine absolute Pixelgröße, die am Ende inkonsistent aussehen könnte.

Hier ist das CSS, mit dem ich gelandet bin:

.toc-list, .toc-list ol { list-style-type: none;
} .toc-list { padding: 0;
} .toc-list ol { padding-inline-start: 2ch;
}

Sara Soueidan wies mich darauf hin, dass WebKit-Browser Listensemantik entfernen, wenn list-style-type is none, also musste ich hinzufügen role="list" in den HTML-Code, um ihn beizubehalten:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page">Page 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>
CodePen Embed-Fallback

Gestaltung des Titels und der Seitenzahl

Nachdem die Liste nach meinem Geschmack gestaltet war, war es an der Zeit, mit der Gestaltung eines einzelnen Listenelements fortzufahren. Für jeden Eintrag im Inhaltsverzeichnis müssen der Titel und die Seitenzahl in derselben Zeile stehen, wobei der Titel links und die Seitenzahl rechts ausgerichtet sein müssen.

Sie denken vielleicht: „Kein Problem, dafür ist Flexbox da!“ Du liegst nicht falsch! Flexbox kann tatsächlich die richtige Ausrichtung der Titelseite erreichen. Aber es gibt einige knifflige Ausrichtungsprobleme, wenn die Anführer hinzugefügt werden, also habe ich mich stattdessen für Christophs Ansatz mit einem Raster entschieden, was als Bonus auch bei mehrzeiligen Titeln hilfreich ist. Hier ist das CSS für ein einzelnes Element:

.toc-list li > a { text-decoration: none; display: grid; grid-template-columns: auto max-content; align-items: end;
} .toc-list li > a > .page { text-align: right;
}

Das Raster hat zwei Spalten, von denen die erste ist auto-dimensioniert, um die gesamte Breite des Containers auszufüllen, abzüglich der zweiten Spalte, die so bemessen ist max-content. Die Seitenzahl wird rechtsbündig ausgerichtet, wie es in einem Inhaltsverzeichnis üblich ist.

Die einzige andere Änderung, die ich an dieser Stelle vorgenommen habe, war, den Text „Seite“ auszublenden. Dies ist hilfreich für Screenreader, aber visuell unnötig, daher habe ich a verwendet traditionell visually-hidden Klasse um es aus der Sicht zu verstecken:

.visually-hidden { clip: rect(0 0 0 0); clip-path: inset(100%); height: 1px; overflow: hidden; position: absolute; width: 1px; white-space: nowrap;
}

Und natürlich muss der HTML-Code aktualisiert werden, um diese Klasse zu verwenden:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title</span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>

Mit dieser Grundlage ging ich weiter, um die Führer zwischen dem Titel und der Seite anzusprechen.

CodePen Embed-Fallback

Erstellen von Punktführern

Leaders sind in Printmedien so verbreitet, dass Sie sich vielleicht fragen, warum CSS das nicht bereits unterstützt? Die Antwort ist: es tut. So in etwa.

Es gibt tatsächlich eine leader() Funktion definiert in der CSS Generated Content for Paged Media-Spezifikation. Wie bei vielen der Seitenmedienspezifikationen ist diese Funktion jedoch in keinem Browser implementiert, weshalb sie als Option ausgeschlossen ist (zumindest zu dem Zeitpunkt, als ich dies schreibe). Es ist nicht einmal aufgeführt caniuse.com, vermutlich weil es noch niemand umgesetzt hat und es auch keine Pläne oder Signale dafür gibt.

Glücklicherweise haben sowohl Julie als auch Christoph dieses Problem bereits in ihren jeweiligen Beiträgen angesprochen. Um die Punktführer einzufügen, verwendeten beide a ::after Pseudo-Element mit seinen content Eigenschaft auf eine sehr lange Reihe von Punkten gesetzt, wie folgt:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .title::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Das ::after Pseudo-Element wird auf eine absolute Position gesetzt, um es aus dem Fluss der Seite herauszunehmen und einen Zeilenumbruch zu vermeiden. Der Text wird rechtsbündig ausgerichtet, weil die letzten Punkte jeder Zeile bündig mit der Zahl am Ende der Zeile sein sollen. (Mehr zu den Komplexitäten davon später.) Die .title Das Element ist so eingestellt, dass es eine relative Position hat ::after Pseudo-Element bricht nicht aus seiner Box aus. Inzwischen ist die overflow ist ausgeblendet, sodass all diese zusätzlichen Punkte unsichtbar sind. Das Ergebnis ist ein hübsches Inhaltsverzeichnis mit Punktführern.

Es gibt jedoch etwas anderes, das berücksichtigt werden muss.

Sara wies mich auch darauf hin, dass all diese Punkte als Text für Screenreader gelten. Was hörst du also? „Einleitung Punkt Punkt Punkt Punkt…“, bis alle Punkte angesagt sind. Das ist eine schreckliche Erfahrung für Screenreader-Benutzer.

Die Lösung besteht darin, ein zusätzliches Element mit einzufügen aria-hidden einstellen true und verwenden Sie dann dieses Element, um die Punkte einzufügen. Das HTML wird also:

<ol class="toc-list" role="list"> <li> <a href="#link_to_heading"> <span class="title">Chapter or subsection title<span class="leaders" aria-hidden="true"></span></span> <span class="page"><span class="visually-hidden">Page</span> 1</span> </a> <ol role="list"> <!-- subsection items --> </ol> </li>
</ol>

Und das CSS wird zu:

.toc-list li > a > .title { position: relative; overflow: hidden;
} .toc-list li > a .leaders::after { position: absolute; padding-left: .25ch; content: " . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . . . . "; text-align: right;
}

Jetzt ignorieren Screenreader die Punkte und ersparen den Benutzern die Frustration, mehrere angesagte Punkte zu hören.

CodePen Embed-Fallback

Abschluß Tippen

An diesem Punkt sieht die Inhaltsverzeichnis-Komponente ziemlich gut aus, aber sie könnte einige kleinere Detailarbeit gebrauchen. Zunächst einmal setzen die meisten Bücher die Kapiteltitel visuell von den Unterabschnittstiteln ab, also habe ich die Elemente der obersten Ebene fett gedruckt und einen Rand eingefügt, um die Unterabschnitte von den folgenden Kapiteln zu trennen:

.toc-list > li > a { font-weight: bold; margin-block-start: 1em;
}

Als nächstes wollte ich die Ausrichtung der Seitenzahlen bereinigen. Bei Verwendung einer Schriftart mit fester Breite sah alles in Ordnung aus, aber bei Schriftarten mit variabler Breite konnten die Führungspunkte ein Zickzackmuster bilden, wenn sie sich an die Breite einer Seitenzahl anpassen. Beispielsweise wäre jede Seitenzahl mit einer 1 schmaler als andere, was zu Führungspunkten führt, die nicht mit den Punkten in vorherigen oder folgenden Zeilen ausgerichtet sind.

Falsch ausgerichtete Zahlen und Punkte in einem Inhaltsverzeichnis.
Ein perfektes Inhaltsverzeichnis mit HTML + CSS

Um dieses Problem zu beheben, habe ich eingestellt font-variant-numeric zu tabular-nums so werden alle Zahlen mit der gleichen Breite behandelt. Indem Sie auch die Mindestbreite auf einstellen 2ch, habe ich darauf geachtet, dass alle Nummern mit ein oder zwei Ziffern perfekt ausgerichtet sind. (Möglicherweise möchten Sie dies einstellen 3ch wenn Ihr Projekt mehr als 100 Seiten hat.) Hier ist das endgültige CSS für die Seitenzahl:

.toc-list li > a > .page { min-width: 2ch; font-variant-numeric: tabular-nums; text-align: right;
}
Ausgerichtete Führungspunkte in einem Inhaltsverzeichnis.
Ein perfektes Inhaltsverzeichnis mit HTML + CSS

Und damit ist das Inhaltsverzeichnis komplett!

CodePen Embed-Fallback

Zusammenfassung

Das Erstellen eines Inhaltsverzeichnisses nur mit HTML und CSS war eine größere Herausforderung als ich erwartet hatte, aber ich bin mit dem Ergebnis sehr zufrieden. Dieser Ansatz ist nicht nur flexibel genug, um Kapitel und Unterabschnitte aufzunehmen, sondern handhabt auch Unter-Unterabschnitte gut, ohne das CSS zu aktualisieren. Der Gesamtansatz funktioniert auf Webseiten, auf denen Sie zu den verschiedenen Inhaltsorten verlinken möchten, sowie auf PDFs, auf denen das Inhaltsverzeichnis auf verschiedene Seiten verlinken soll. Und natürlich sieht es auch gedruckt gut aus, wenn Sie es jemals in einer Broschüre oder einem Buch verwenden möchten.

Ich möchte Julie Blanc und Christoph Grabo für ihre hervorragenden Blogbeiträge zum Erstellen eines Inhaltsverzeichnisses danken, da beide von unschätzbarem Wert waren, als ich anfing. Ich möchte auch Sara Soueidan für ihr Feedback zur Barrierefreiheit während der Arbeit an diesem Projekt danken.


Ein perfektes Inhaltsverzeichnis mit HTML + CSS ursprünglich veröffentlicht am CSS-Tricks.. Du solltest erhalten Sie den Newsletter.

Zeitstempel:

Mehr von CSS-Tricks