WordPress Inhaltsverzeichnis erstellen (ohne Plugin, mein kostenloser Code)
Ein Inhaltsverzeichnis für WordPress Beiträge und Artikel ist eine geschickte Sache. Besucher haben einen schnellen Überblick, SEO-Vorteile und schickes Design werten deinen Beitrag so richtig auf!
Es ist einfach zu umständlich ein Inhaltsverzeichnis in jedem Beitrag neu zu erstellen oder bei Änderungen zu aktualisieren. Für so eine „Kleinigkeit“ ein Plugin zu installieren ist auch keine gute Lösung, da es die Seite nur langsamer macht und man es – wie Du gleich sehen wirst – auch ganz einfach selber coden kann.
Falls Du keine oder wenig Programmiererfahrung hast, kannst Du dieses Inhaltsverzeichnis trotzdem verwenden. Das ist kein Problem! Falls noch Fragen offen bleiben, kann ich auch über die Kommentare versuchen Hilfestellung zu geben.
So wird unser Endergebnis aussehen:
Den kompletten Code habe ich selbst programmiert und darf kostenlos für jegliche Zwecke (auch kommerziell) verwendet und auch nach belieben verändert werden!
Schritt 1: Inhaltsverzeichnis generieren (PHP Skript)
Für Programmierunerfahrene: Im Backend-Menü (linker Rand) gibt es einen Punkt „Design“. Navigiere dort zum Unterpunkt „Theme-Editor“ und wähle dann am rechten Rand unter „Theme-Dateien“ die „Theme-Funktionen“ (functions.php) aus. Dort kannst Du bis ganz ans Ende scrollen und nach der letzten Zeile Code 2-3 Leerzeilen einfügen und dann den untenstehenden PHP-Code 1:1 einfügen und abspeichern.
Für Programmiererfahrene: Dieser Code muss in die functions.php des verwendeten Themes (falls verwendet ins Child-Theme) eingefügt werden.
function get_toc($content) { // get headlines $headings = get_headings($content, 1, 6); // parse toc ob_start(); echo "<div class='table-of-contents'>"; echo "<span class='toc-headline'>Table Of Contents</span>"; echo "<!-- Table of contents by webdeasy.de -->"; echo "<span class='toggle-toc custom-setting' title='collapse'>−</span>"; parse_toc($headings, 0, 0); echo "</div>"; return ob_get_clean(); } function parse_toc($headings, $index, $recursive_counter) { // prevent errors if($recursive_counter > 60 || !count($headings)) return; // get all needed elements $last_element = $index > 0 ? $headings[$index - 1] : NULL; $current_element = $headings[$index]; $next_element = NULL; if($index < count($headings) && isset($headings[$index + 1])) { $next_element = $headings[$index + 1]; } // end recursive calls if($current_element == NULL) return; // get all needed variables $tag = intval($headings[$index]["tag"]); $id = $headings[$index]["id"]; $classes = isset($headings[$index]["classes"]) ? $headings[$index]["classes"] : array(); $name = $headings[$index]["name"]; // element not in toc if(isset($current_element["classes"]) && $current_element["classes"] && in_array("nitoc", $current_element["classes"])) { parse_toc($headings, $index + 1, $recursive_counter + 1); return; } // parse toc begin or toc subpart begin if($last_element == NULL) echo "<ul>"; if($last_element != NULL && $last_element["tag"] < $tag) { for($i = 0; $i < $tag - $last_element["tag"]; $i++) { echo "<ul>"; } } // build li class $li_classes = ""; if(isset($current_element["classes"]) && $current_element["classes"] && in_array("toc-bold", $current_element["classes"])) $li_classes = " class='bold'"; // parse line begin echo "<li" . $li_classes .">"; // only parse name, when li is not bold if(isset($current_element["classes"]) && $current_element["classes"] && in_array("toc-bold", $current_element["classes"])) { echo $name; } else { echo "<a href='#" . $id . "'>" . $name . "</a>"; } if($next_element && intval($next_element["tag"]) > $tag) { parse_toc($headings, $index + 1, $recursive_counter + 1); } // parse line end echo "</li>"; // parse next line if($next_element && intval($next_element["tag"]) == $tag) { parse_toc($headings, $index + 1, $recursive_counter + 1); } // parse toc end or toc subpart end if ($next_element == NULL || ($next_element && $next_element["tag"] < $tag)) { echo "</ul>"; if ($next_element && $tag - intval($next_element["tag"]) >= 2) { echo "</li>"; for($i = 1; $i < $tag - intval($next_element["tag"]); $i++) { echo "</ul>"; } } } // parse top subpart if($next_element != NULL && $next_element["tag"] < $tag) { parse_toc($headings, $index + 1, $recursive_counter + 1); } } function get_headings($content, $from_tag = 1, $to_tag = 6) { $headings = array(); preg_match_all("/<h([" . $from_tag . "-" . $to_tag . "])([^<]*)>(.*)<\/h[" . $from_tag . "-" . $to_tag . "]>/", $content, $matches); for($i = 0; $i < count($matches[1]); $i++) { $headings[$i]["tag"] = $matches[1][$i]; // get id $att_string = $matches[2][$i]; preg_match("/id=\"([^\"]*)\"/", $att_string , $id_matches); $headings[$i]["id"] = $id_matches[1]; // get classes $att_string = $matches[2][$i]; preg_match_all("/class=\"([^\"]*)\"/", $att_string , $class_matches); for($j = 0; $j < count($class_matches[1]); $j++) { $headings[$i]["classes"] = explode(" ", $class_matches[1][$j]); } $headings[$i]["name"] = strip_tags($matches[3][$i]); } return $headings; }
Mit diesem Code werden alle Überschriften ausgelesen, zusammengefasst und als Inhaltsverzeichnis wieder ausgegeben.
Standardmäßig werden alle Überschriften (H1-H6) ausgelesen und ausgegeben. Wenn du das anpassen möchtest, kannst du die Parameter in Zeile 3 anpassen.
Inhaltsverzeichnis in Seiten einfügen
Du kannst das Inhaltsverzeichnis in zwei verschiedenen Wegen in Deine Posts einbinden.
Möglichkeit 1: Wenn Du die Position in jedem Beitrag über den Shortcode TOC
selber festlegen möchtest, dann fügen noch zusätzlich diesen Code ein:
// TOC (from webdeasy.de) function toc_shortcode() { return get_toc(get_the_content()); } add_shortcode('TOC', 'toc_shortcode');
Möglichkeit 2: Wenn das Inhaltsverzeichnis automatisch nach dem ersten Absatz in jedem Beitrag eingefügt werden soll, dann füge noch zusätzlich diesen Code hinzu:
function add_table_of_content($content) { if (!is_single()) return $content; $paragraphs = explode("</p>", $content); $paragraphs_count = count($paragraphs); $middle_index= absint(floor($paragraphs_count / 2)); $new_content = ''; for ($i = 0; $i < $paragraphs_count; $i++) { if ($i === 1) { $new_content .= get_toc($content); } $new_content .= $paragraphs[$i] . "</p>"; } return $new_content; } // add our table of contents filter (from webdeasy.de) add_filter('the_content', 'add_table_of_content');
Solltest Du das Inhaltsverzeichnis erst nach dem zweiten Absatz ausgeben lassen wollen, kannst Du einfach die Zahl in Zeile 10 anpassen.
Möchtest Du mit deiner Website oder deinem Blog Geld verdienen? Dann schau dir mein Erfahrungsbericht über Ezoic an, die beste Google AdSense Alternative!
Schritt 2: Inhaltsverzeichnis stylen (CSS)
Mit dem bisherigen Code wird Dein Inhaltsverzeichnis bereits generiert. Allerdings würde es als normale Liste <ul>
ausgegeben werden. Damit das Ganze etwas schöner aussieht, spendieren wir dazu auch noch ein paar Zeilen CSS.
Für Programmiererfahrene: Dieser Code muss in eine CSS Datei des verwendeten Themes (falls verwendet ins Child-Theme) eingefügt werden. Oft ist das Standardmäßig die style.css auf oberster Ebene im Theme-Ordner.
Für Programmierunerfahrene: Im gleichen Menüpunkt wie schon beim PHP-Code kannst Du einfach rechts „Stylesheet“ (style.css) auswählen. Dort gehst Du auch wieder ans Ende und fügst nach der letzten Zeile den folgenden Code ein und speicherst die Datei dann ab.
Ich habe zwei verschiedene Styles erstellt. Nutze einfach den Code vom Inhaltsverzeichnis, welches dir am besten gefällt.
Variante 1 – Minimalistic
CSS-Code dazu:
/* Adjust these variables for your project */ :root { --dark-grey: #333333; --main-color: #036773; --font-size: 16px; --line-height: 1.2; } .table-of-contents { margin: 4rem 0; position: relative; } .table-of-contents .toc-headline { font-size: 22px; color: var(--dark-grey); font-weight: 600; display: block; cursor: pointer; } .table-of-contents .toggle-toc { position: absolute; top: 0; right: 1rem; font-size: 30px; cursor: pointer; font-weight: 800; color: var(--main-color); } .table-of-contents ul { padding: 0; padding-left: 1rem; } .table-of-contents li { position: relative; padding-left: 1rem; list-style: none; line-height: var(--line-height); font-weight: 400; margin: .3rem 0; transition: .2s ease all; } .table-of-contents li:hover { padding-left: 1.1rem; } .table-of-contents li a { font-size: var(--font-size); line-height: var(--line-height); text-decoration: none; color: var(--main-color); } .table-of-contents li:before { content: ''; -webkit-transform: translateX(-0.75em) rotate(45deg); transform: translateX(-0.75em) rotate(45deg); height: 0.5em; width: 0.5em; border-top: solid 2px var(--dark-grey); border-right: solid 2px var(--dark-grey); border-radius: 0; background: 0 0; position: absolute; display: block; top: 7px; left: 0; } .table-of-contents li>ul { padding-left: 0.7rem; padding-bottom: 1rem; }
Variante 2 – Basic
CSS-Code dazu:
/* Adjust these variables for your project */ :root { --dark-grey: #333333; --background-color: #eee; --main-color: #036773; --font-size: 16px; --line-height: 1.2; } .table-of-contents { margin: 4rem 0; position: relative; background-color: var(--background-color); padding: .5rem 1rem; border-left: 5px solid var(--main-color); border-radius: 5px; } .table-of-contents .toc-headline { font-size: 22px; color: var(--dark-grey); font-weight: 600; display: block; cursor: pointer; margin-top: .3rem; } .table-of-contents .toggle-toc { position: absolute; top: .8rem; right: .8rem; font-size: 20px; cursor: pointer; font-weight: 800; color: #FFF; width: 1.5rem; height: 1.5rem; display: flex; justify-content: center; align-items: center; border-radius: 50%; line-height: 10px; background-color: var(--main-color); } .table-of-contents ul { padding: 0; } .table-of-contents li { position: relative; list-style: none; line-height: var(--line-height); font-weight: 400; margin: .3rem 0; transition: .2s ease all; } .table-of-contents li a { font-size: var(--font-size); line-height: var(--line-height); color: var(--main-color); } .table-of-contents li>ul { padding-left: 1rem; padding-bottom: .5rem; }
Damit sieht das Inhaltsverzeichnis nun besser aus und ist auch als solches erkennbar. Das kannst Du natürlich alles an Dein Theme und Deinen Wünschen entsprechend anpassen (lassen).
Schritt 3: Inhaltsverzeichnis auf- und zuklappen (JavaScript)
Mit diesem Code können Besucher das Inhaltsverzeichnis auf- und zuklappen.
document.querySelectorAll('.table-of-contents .toggle-toc, .table-of-contents .toc-headline').forEach(toggler => { toggler.addEventListener('click', function() { let tocList = document.querySelectorAll('.table-of-contents ul')[0]; let toggler = document.querySelectorAll('.table-of-contents .toggle-toc')[0]; if(tocList.style.display == 'none') { tocList.style.display = 'block'; toggler.innerHTML = '-'; } else { tocList.style.display = 'none'; toggler.innerHTML = '+'; } }); });
Für Programmiererfahrene: Das kannst Du einfach in eine bestehende JS-Datei einfügen.
Für Programmierunerfahrene: Im Theme-Editor gibt es meist einen js Ordner mit .js Dateien oder direkt eine Datei mit der Endung .js. Wähle einfach eine Datei aus und füge den Code ganz am Ende ein.
Inhaltsverzeichnis standardmäßig schließen
Falls das Inhaltsverzeichnis standardmäßig geschlossen sein soll, kannst du noch die folgende Zeile nach dem vorherigen JavaScript Code ergänzen.
document.querySelectorAll('.table-of-contents .toggle-toc, .table-of-contents .toc-headline')[0].click();
Schritt 4: Verzeichnis in einzelne Beiträge einbinden
Wenn Du Dich für Möglichkeit 1 entschieden hast, musst Du, um das Inhaltsverzeichnis nun auf einer Seite auszugeben, einfach im Backendeditor des jeweiligen Beitrags den Shortcode TOC
einfügen.
Damit die einzelnen Überschriften auch verlinkt sind, benötigen diese einen sogenannten HTML-Anker. Auch hier gibt es wieder zwei Möglichkeiten. Entweder ein Skript fügt sie für Dich automatisch ein (Möglichkeit 1) oder Du fügst sie manuell ein (Möglichkeit 2).
Möglichkeit 1:
Das Skript hat mir ein Leser empfohlen (vielen Dank!) und er scheint es von dieser Seite zu haben.
Beachte, dass Du diesen Code vor den PHP Code aus Punkt 1 setzt.
/** * Automatically add IDs to headings such as <h2></h2> */ function auto_id_headings( $content ) { $content = preg_replace_callback('/(\<h[1-6](.*?))\>(.*)(<\/h[1-6]>)/i', function( $matches ) { if(!stripos($matches[0], 'id=')) { $matches[0] = $matches[1] . $matches[2] . ' id="' . sanitize_title( $matches[3] ) . '">' . $matches[3] . $matches[4]; } return $matches[0]; }, $content); return $content; } add_filter('the_content', 'auto_id_headings');
Wenn du das Einfügen per Shortcode gewählt hast, musst du die Funktion toc_shortcode()
aus Schritt 1 nochmal anpassen:
// TOC (from webdeasy.de) function toc_shortcode() { return get_toc(auto_id_headings(get_the_content())); } add_shortcode('TOC', 'toc_shortcode');
Möglichkeit 2:
Um die Anker manuell einzufügen, klickst Du die Überschrift an und fügst unter „Erweitert“ einen Anker ein. Den Namen kannst Du Dir frei überlegen, sollte aber zur Überschrift passen.
Das ist alles! Wenn Du Dir die Seite jetzt anschaust, sollte das Inhaltsverzeichnis angezeigt werden.
Menüpunkte ausschließen
Falls Du ein oder mehrere Menüpunkte ausschließen möchtest (wie diese Überschrift :)), so dass sie nicht im Inhaltsverzeichnis angezeigt werden, kannst Du der jeweiligen Überschrift zusätzlich die CSS Klasse nitoc
geben. Das Skript ignoriert diese dann.
Fazit
Du hast es geschafft! Durch ein paar Zeilen Code kannst Du Dir jetzt immer Dein eigenes Inhaltsverzeichnis generieren lassen und musst nicht mehr auf fertige Plugins zurückgreifen. Oder noch schlimmer: die Inhaltsverzeichnisse immer selber schreiben und aktualisieren. Viel Spaß damit! 🙂
Wie fandest du diesen Beitrag?
Hey danke für die super Anleitung.Bei mir funktioniert das Auf- und Zuklappen des Inhaltsverzeichnisses nicht.Woran könnte das liegen?
Kann viele Gründe haben:
Hallo, vielen Dank für die Super Anleitung!Ich würde gerne bei allen Beiträgen (Post) das Inhaltsverzeichnis automatisch einfügen und bei allen Seiten (Page) an der Stelle, die ich vorgesehen habe. Ist das auch möglich?Liebe Grüße, Patrick
Hi, freut mich! 🙂 Das geht natürlich auch:
Füge dazu den Code für den Shortcode (
toc_shortcode()
) ein, damit kannst du dann den Shortcode bei allen Pages manuell einfügen.Für das automatische Einfügen auf Post Seiten nimmst du die
add_table_of_content()
Funktion und fügst sie auch mit ein. Die zweite Zeile der Funktion (if (!is_single()) return $content;
) ist schon dafür zuständig, dass der TOC nur auf Post-Seiten angezeigt wird.Hoffe das hilft, falls weitere Fragen sind meld dich gern! 🙂
Vielen Dank für deine Hilfe!
Hallo Zusammen !
Das geht aber noch eleganter mit einer rekursiven Lösung (bis h5):
// filter function to generate the table of content
function add_table_of_content_sub($tags, $ids, $names, $ii, $level) {
$heads = array(‚h2‘, ‚h3‘, ‚h4‘, ‚h5‘);
$ul = 0;
$i = $ii;
if( $level > 3) return $ii – 1;
for(; $i 0 && $ul == 1 ) echo „\n“;
return add_table_of_content_sub($tags, $ids, $names, $i, $level+1);
}
if( $level > 0 && $ul == 0 ) {
echo „\n“;
$ul = 1;
}
if ( empty($names[$i]) ) continue;
echo „\n“;
if(!empty($ids[$i])) {
echo „„. $names[$i] . „\n“;
} else {
echo $names[$i];
}
$i = add_table_of_content_sub($tags, $ids, $names, $i+1, $level+1);
echo „\n“;
}
if( $level > 0 && $ul == 1 ) echo „\n“;
return $i;
}
function add_table_of_content($content) {
ob_start();
preg_match_all(„/(.*)/“, $content, $matches);
$tags = $matches[0];
$ids = $matches[1];
$names = $matches[2];
?>
Inhalt
<?PHP
return str_replace("{{TABLE_OF_CONTENTS}}“, ob_get_clean(), $content);
}
Viele Grüße
Michael
Hi, super Lösung! Ich hatte bereits an einer rekursiven Version gearbeitet, ist allerdings noch nicht fertig geworden, daher noch die iterative Lösung. Sobald meine Version vollendet ist, werde ich sie hier auch veröffentlichen. Trotzdem vielen Dank, dass Du Deine Lösung hier teilst! 🙂
Viele Grüße
LH
Hi, mich würde deine rekursive Lösung interessieren, sofern sie von jener abweicht, die Michael netterweise gepostet hat.Da ich nicht weiß, ob ich über eine Antwort informiert werde, würde ich mich über eine kurze Email sehr freuen, wär das möglich?Vielen Dank im Voraus 🙏Sascha
Ich habe es mir aufgeschrieben, sobald der Artikel geupdatet wirst, werde ich dich informieren 🙂
Viele Grüße
Lorenz
Der Beitrag ist jetzt überarbeitet – mit einer rekursiven Lösung! 🙂