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 ) {
				// Get current page host
				// (he attributes will only be set for external links)
				$site_host = parse_url( site()->url() )['host'];

				// Convert $text to DOM tree
				$dom = new DomDocument();
				$dom->loadHTML( $text );

				// Loop over all anchor elements
				foreach ( $dom->getElementsByTagName( 'a' ) as $link_el ) {
					// Parse link address
					$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'];

						// Only continue if the link is external
						if (
							in_array( $link_scheme, [ 'http', 'https' ] ) &&
							$link_host !== $site_host
						) {
							// Create string of old link (to find and replace it later)
							$link_str = $dom->saveHTML( $link_el );

							// Add link attributes
							$link_el->setAttribute( 'rel', 'noopener noreferrer' );
							$link_el->setAttribute( 'target', '_blank' );

							// Create new link string
							$new_link_str = $dom->saveHTML( $link_el );

							// replace old link with new link in $text
							$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 );
			
			// Tags to be linked
			$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 ) {
						// Create string for old element (to find and replace later)
						$item_str = $dom->saveHTML( $item );

						// Generate element ID from element text content
						$id = Str::slug( $item_content . '' );

						// Set element ID
						$item->setAttribute( 'id', $id );

						// Create a new anchor element and
						// move all element childNodes to the new anchor.
						// Attach insert anchor into element
						$link_el = $dom->createElement( 'a' );
						$link_el->setAttribute( 'href', '#' . $id );
						$link_el->appendChild( $item->firstChild );
						$item->insertBefore( $link_el, $item->firstChild );

						// Generate new HTML string of element
						$new_item_str = $dom->saveHTML( $item );

						// In replace old element with new element in $text
						$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!