It is widely known that you can open links in a new tab using target="_blank"
.
For external links it often makes sense to add rel="noopener noreferrer"
to secure the content of the current page. More information and a demo on this topic can be found in Mathias Bynens' blog post on this topic.
You can easily add this functionality in Kirby's WYSIWYG markdown editor using the (link)
tag. However in longer texts with many links, there is a chance that one might forget adding the rel
attribute.
It would be much nicer if Kirby would add target="_blank"
and rel="noopener noreferrer"
to external links automatically when rendering a page.
For this functionality I'm using feature called hooks: It makes it possible to add custom logic in different steps of the page rendering process.
To get started I first added a new entry called kirbytext:after
in confg.php
under hooks
:
<?php
return [
'hooks': [
'kirbytext:after' => function ( $text ) {
return $text;
}
]
];
This function is executed for each field with the type kirbytext
. The HTML string generated from the markdown is passed as an argument. This function is expected to modify the HTML string and return it at the end.
To find and modify all anchor elements in this HTML string one could use a complicated regular expression.
However it is much more convienient to use the PHP DOM library instead:
<?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;
}
]
];
Automatic bookmarks
Similarly, you can add a link with an automatically generated ID to all headings to jump easily between sections:
<?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;
}
]
];
Comments
Anonymous
2022-10-15 22:29
Leave a comment
Replied on your own website? Send a Webmention!