In Kirby externe Links automatisch in neuem Tab öffnen

Veröffentlicht von am

Es ist ja weitgehend bekannt, dass man per target="_blank" Links in einem neuen Fenster öffnen kann.

Für externe Links macht es oft Sinn, zusätzlich noch rel="noopener noreferrer" hinzuzufügen, um den Inhalt der aktuellen Seite abzusichern. Weitere Informationen und eine Demo zu dem Thema gibt es in dem Blog-Eintrag von Mathias Bynens zu diesem Thema.

Diese Funktionalität kann man zwar einfach in Kirby's WYSIWYG-Markdown Editor über den (link)-Tag hinzufügen, es besteht aber gerade bei längeren Texten mit vielen Links die Möglichkeit, dass man das einmal vergisst.

Um dem vorzubeugen, habe ich das Hinzufügen von target="_blank" und rel="noopener noreferrer" automatisiert: Die entsprechenden Attribute werden beim Rendern einer Seite von Kirby selbst hinzugefügt.

Für diese Funktionalität benutze ich Kirby Hooks. Dazu lege ich in der confg.php unter hooks zunächst einen neuen Eintrag namens 'kirbytext:after' an:

<?php

return [
	'hooks': [
		'kirbytext:after' => function ( $text ) {
			return $text;
		}
	]
];	

Diese Funktion wird für jedes Feld mit dem Typ kirbytext ausgeführt. Als Argument wird der aus dem Markdown generierte HTML-String übergeben. Es wird erwartet, dass diese Funktion den HTML-String verändert, und ihn dann am Ende auch wieder zurückgibt.

Um alle Anker-Elemente in diesem HTML-String zu finden und modifizieren könnte man einen komplizierten regulären Ausdruck verwenden.

Deutlich einfacher funktioniert es aber mit der in PHP mitgelieferten DOM-Bibliothek, mit der sich DOM-Knoten leicht erstellen und bearbeiten lassen:

<?php

return [
	'hooks' => [
		'kirbytext:after' => function ( $text ) {            
			if ( strlen( $text ) > 0 ) {
				// Den aktuellen Seitenhost herausfinden
				// (die Attribute sollen ja nur bei externen Links gesetzt werden)
				$site_host = parse_url( site()->url() )['host'];

				// Aus $text ein neues DOM Objekt erstellen
				$dom = new DomDocument();
				$dom->loadHTML($text);

				// Geht alle Anker-Elemente durch
				foreach ( $dom->getElementsByTagName( 'a' ) as $link_el ) {
					// Die Link-Adresse parsen
					$link_href = $link_el->getAttribute( 'href' );
					$link_parts = parse_url( $link_href );

					if (
						$link_parts &&
						isset( $link_parts['host'] ) &&
						isset( $link_parts['scheme'] )
					) {
						$link_host = $link_parts['host'];
						$link_scheme = $link_parts['scheme'];

						// Nur weitermachen, wenn es sich um einen
						// externen Link handelt
						if (
							in_array( $link_scheme, [ 'http', 'https' ] ) &&
							$link_host !== $site_host
						) {
							// alten Link-String erzeugen (wird später ersetzt)
							$link_str = $dom->saveHTML( $link_el );

							// Link-Attribute hinzufügen
							$link_el->setAttribute( 'rel', 'noopener noreferrer' );
							$link_el->setAttribute( 'target', '_blank' );

							// neuen Link-String erzeugen
							$new_link_str = $dom->saveHTML( $link_el );

							// Im $text den alten link durch den neuen ersetzen
							$text = str_replace( $link_str, $new_link_str, $text );
						}
					}
				}
			}       
			
			return $text;
		}
	]
];	

Automatische Lesemarken

Auf ähnliche Weise kann man zu allen Überschriften einen Link mit automatisch generierter ID hinzufügen, um einfach zwischen Abschnitten springen zu können:

<?php

return [
	'hooks' => [
		'kirbytext:after' => function ( $text ) {
		if ( strlen( $text ) > 0 ) {
			$dom = new DomDocument();
			$dom->loadHTML( $text );
			
			// Elemente auswählen, die Verlinkt werden sollen
			$tags_with_id = [ 'h1', 'h2', 'h3' ];

			foreach ( $tags_with_id as $tag_name ) {
				foreach ( $dom->getElementsByTagName( $tag_name ) as $item ) {
					$item_content = $item->nodeValue;

					if ( $item_content ) {
						// den alten HTML-String erzeugen
						$item_str = $dom->saveHTML( $item );

						// die ID aus dem Inhalt des Elements generieren
						$id = Str::slug( $item_content . '' );

						// die ID des Elements als Attribut setzen
						$item->setAttribute( 'id', $id );

						// den Inhalt des Elements in einen Link verschieben
						$link_el = $dom->createElement('a');
						$link_el->setAttribute( 'href', '#' . $id );
						$link_el->appendChild($item->firstChild);
						$item->insertBefore($link_el, $item->firstChild);

						// den neuen HTML string erzeugen
						$new_item_str = $dom->saveHTML( $item );

						// Im $text das alte Markup durch das neue ersetzen
						$text = str_replace( $item_str, $new_item_str, $text );
					}
				}
			}

			return $text;
		}
	]
];	

Hinterlasse einen Kommentar

Verfügbare Formatierungen

Benutze Markdown-Befehle oder ihre HTML-Äquivalente, um deinen Kommentar zu formatieren:

Textauszeichnungen
*kursiv*, **fett**, ~~durchgestrichen~~, `Code` und <mark>markierter Text</mark>.
Listen
- Listenpunkt 1
- Listenpunkt 1
1. Nummerierte Liste 1
2. Nummerierte Liste 2
Zitate
> Zitierter Text
Code-Blöcke
```
// Ein einfacher Code-Block
```
```php
// Etwas PHP-Code
phpinfo();
```
Verlinkungen
[Link-Text](https://example.com)
Vollständige URLs werden automatisch in Links umgewandelt.

Auf der eigenen Website geantwortet? Sende eine Webmention!