WordPress Inhaltsverzeichnis automatisch erstellen – ohne Plugin!

WordPress Inhaltsverzeichnis automatisch erstellen – ohne Plugin! Thumbnail
Veröffentlicht am 19. Mai 2020Zuletzt aktualisiert am 25. April 2021

Bei vielen WordPress Blog-Einträgen oder Seiten kann ein Inhaltsverzeichnis wahre Wunder wirken und dem Besucher einen schnellen Überblick über die Gliederung der Inhalte geben.

Anzeige

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 (z.B. Easy Table Of Contents) 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:

Das Styling ist ein kleines bisschen abweichend, da ich es für mich an mein Blog-Design angepasst habe.

Den kompletten Code habe ich selbst programmiert und darf kostenlos für jegliche Zwecke (auch kommerziell) verwendet und auch nach belieben verändert werden!

1. Inhaltsverzeichnis generieren (PHP Skript)

Der folgende Code generiert aus den Überschriften <h2> und <h3> das Inhaltsverzeichnis.

Anzeige

Für Programmiererfahrene: Dieser Code muss in die functions.php des verwendeten Themes (falls verwendet ins Child-Theme) eingefügt werden.

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.

// filter function to generate the table of content (from webdeasy.de)
function get_table_of_content($content) {
    ob_start();
    preg_match_all("/<h[2,3](?:\sid=\"(.*)\")?(?:.*)?>(.*)<\/h[2,3]>/", $content, $matches);
    $tags = $matches[0];
    $ids = $matches[1];
    $names = $matches[2];
    ?>
    <div class="table-of-contents">
    	<p class="toc-headline"><strong>Inhaltsverzeichnis</strong></p>
    	<span class="toggle-toc">+</span>
	    <ul style="display: none;">
	        <!-- Table of contents by webdeasy.de -->
	        <?php for($i = 0; $i < count($names); $i++) { ?>
	            <?php if(strpos($tags[$i], "h2") === false || strpos($tags[$i], "class=\"nitoc\"") !== false) continue; ?>
	            
	                <li>
	                    <?php if(!empty($ids[$i])) { ?>
	                        <a href="#<?php echo $ids[$i]; ?>"><?php echo $names[$i]; ?></a>
	                    <?php } else { ?>
	                        <?php echo $names[$i]; ?>  
	                    <?php } ?>
	        
	                    <?php if($i !== count($names) && strpos($tags[$i+1], "h3") !== false) { ?>
	                        <ul>
	                            <?php for($j = 0; $j < count($names) - 1; $j++) { ?>
	                                <?php $sub_index = $i + $j; ?>
	                                <?php if($j != 0 && strpos($tags[$sub_index], "h2") !== false) break; ?>
	                                <?php if(strpos($tags[$sub_index], "h3") === false || strpos($tags[$sub_index], "class=\"nitoc\"") !== false) continue; ?>
	                                <li>
	                                    <?php if(!empty($ids[$sub_index])) { ?>
	                                        <a href="#<?php echo $ids[$sub_index]; ?>"><?php echo $names[$sub_index]; ?></a>
	                                    <?php } else { ?>
	                                        <?php echo $names[$sub_index]; ?>  
	                                    <?php } ?>
	                                </li>
	                            <?php } ?>
	                        </ul>
	                    <?php } ?>
	                </li>
	        <?php } ?>
	    </ul>
	</div>
    <?php
    return ob_get_clean();
}

Das Inhaltsverzeichnis ist mit diesem Code standardmäßig zugeklappt. Wenn Du es standardmäßig geöffnet haben möchtest entferne einfach style="display: none;" in Zeile 12. Die Funktionalität zum auf- und zuklappen programmieren wir in Abschnitt 3.

Du kannst das Inhaltsverzeichnis in zwei verschiedenen Wegen in Deine Posts einbinden.

Möglichkeit 1: Wenn Du die Position in jedem Beitrag über den Platzhalter {{TABLE_OF_CONTENTS}} selber festlegen möchtest, dann fügen noch zusätzlich diesen Code ein:

function add_table_of_content($content) {
    return str_replace("<p>{{TABLE_OF_CONTENTS}}</p>", get_table_of_content($content), $content);
}
// add our table of contents filter (from webdeasy.de)
add_filter('the_content', 'add_table_of_content');

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_table_of_content($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.

Anzeige

Und noch etwas zum technischen Hintergrund:

Der reguläre Ausdruck zum Auslesen der Überschriften ist wie folgt aufgebaut:

<h[2,3](?:\sid=\"(.*)\")?(?:.*)?>(.*)<\/h[2,3]>

Damit suchen wir alle HTML-Tags die eine h2 oder h3 sind. Der Teil (?:\sid=\"(.)\")?(?:.)? gruppiert uns die ID (falls vorhanden) und gruppiert außerdem alle weiteren Attribute (falls vorhanden), die der HTML-Tag noch hat.

Kleiner Tipp: Wenn Du mit Deiner Website oder Deinem Blog nebenbei etwas Geld verdienen möchtest, erfährst Du in diesem Beitrag alles wichtige über Google Adsense, einer der größten Werbedienste im Internet.

2. Inhaltsverzeichnis aufhübschen (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.

Anzeige
.table-of-contents {
  padding: 1rem;
  border-left: 3px solid #cecece;
  background-color: #e6e6e6;
  position: relative;
}
.table-of-contents .toc-headline {
    margin-bottom: 0;
    font-size: 110%;
    cursor: pointer;
}
.table-of-contents .toggle-toc {
    position: absolute;
    top: .4rem;
    right: 1rem;
    font-size: 30px;
    cursor: pointer;
    font-weight: 800;
    cursor: pointer;
}
.table-of-contents ul {
    padding-left: 1rem;
}
.table-of-contents li {
  list-style: none;
}

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).

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.

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 als normalen Absatz {{TABLE_OF_CONTENTS}} einfügen.

Platzhalter in WordPress-Beitrag einfügen
Platzhalter in WordPress-Beitrag 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:

Anzeige

Das Skript hat mir ein Leser empfohlen (vielen Dank!) und er scheint es von dieser Seite zu haben, danke.

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');

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.

WordPress HTML-Anker hinzufügen
WordPress HTML-Anker hinzufügen

Das ist alles! Wenn Du Dir die Seite jetzt anschaust, sollte das Inhaltsverzeichnis entsprechend den <h2> und <h3> Tags ausgegeben werden.

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! 🙂

Ähnliche Beiträge
💬 Beteilige dich an der Unterhaltung

5 Kommentare

  1. Michael Sporleder sagt:

    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

    1. LH sagt:

      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

      1. Sascha sagt:

        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

        1. LH sagt:

          Ich habe es mir aufgeschrieben, sobald der Artikel geupdatet wirst, werde ich dich informieren 🙂

          Viele Grüße
          Lorenz

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.