Linkvorschau und Embeds


Um kleine Linkvorschauen und eingebettete Inhalte anzeigen zu können, habe ich ein kleines Kirby-Plugin geschrieben.


Wenn Du Dir Artikel von mir ansiehst, wirst du feststellen, dass beim Hovern über Links, eine kleine Vorschau angezeigt wird. Im besten Fall mit Bild und Text. Um das zu bewerkstelligen, benutze ich zwei Tools:

  1. Ein Kirby-Plug-in zum Holen der Daten
  2. Tippy.js für die Tooltips.

Kirby Linkpreview-Plugin

Auch wenn ich den Tooltip mit JavaScript anzeige, wollte ich unbedingt vermeiden, dafür einen Request zu senden und erst im Falle eines Hovers, die Daten zu holen. Stattdessen hole ich mir die Daten beim Speichern der Seite und habe sie so jederzeit zur Verfügung.

Dazu habe ich mir ein kleines Plug-in geschrieben. Es lauscht auf den Kirby-Hook page.update:after 1, beginnt die Arbeit also, nachdem die Seite gespeichert wurde.

Es sucht nun im Text nach URLs, in welchen Feldern es schauen soll, kann ich meiner config festlegen. Jede gefundene URL wird zunächst in einem Array abgelegt.

Im nächsten Schritt schnappt sich das Plug-in die Liste der URLs und geht sie einzel durch. Zunächst schaut es, ob es sich auch wirklich um eine valide URL handelt, dazu bietet Kirby einen Validator: V::url($url).

Handelt es sich um eine valide URL kommt mein MetaParser ins Spiel, ein kleines Helferlein im Plug-in. Es kümmert sich darum, die Daten von der jeweiligen Webseite zu holen.

Es öffnet die URL und holt sich den Quellcode der Seite. Es schaut nach einem Seitentitel im <title></title> der Seite, dann schaut es nach OpenGraph Tags. Dabei schaut es nach den gängigen Tags:

twitter:title
twitter:description
twitter:image
og:title
og:description
og:image

Zusätzlich merke ich mir noch die aufgerufene URL und erzeuge eine Id. Alles zusammen wird zurückgegeben und schließlich habe ich am Ende des Hooks zu jeder URL mehr oder weniger detaillierte Daten.

Diese Daten speichere ich parallel zur Markdowndatei in einer JSON-Datei ab.

Auf der Seite

Zunächst habe ich die JSON-Daten dann mittels JavaScript GET-Call geholt und sie dann passend angezeigt, diesen Aufruf spare ich mir inzwischen auch. Ich schreibe die JSON-Daten einfach direkt in die Seite.

Schaust Du Dir also hier den Quellcode an und scrollst ganz nach unten, wirst Du etwas sehen wie: previewJson = …

Link Previews

Für die Hovers ist dann nur noch ein bisschen JavaScript-Magie angesagt: JSON durchgehen und URLs vergleichen, entsprechende Informationen anzeigen. Habe ich keine oder nicht genügend Informationen, zeige ich als Fallback die vollständige URL an.

Ich denke noch darüber nach, ob ich die Daten nicht direkt an den Link schreibe, dazu könnte ich data-Attribute benutzen. Da ich aber vermutlich nur selten so viele Links in einem Dokument haben werde, dass das Durchwandern des JSONs zeitliche Probleme verursachen würde, hat das keine Priorität.

Embeds

Ich benutze diese Daten auch, um Embedded Content anzuzeigen. Das sieht man im Blog und in den Notizen bei den Bookmark-Posts. Dort wird oben immer eine kleine Preview-Box des Bookmarks angezeigt:

Die Daten in der Box kommen ebenfalls aus den JSON-Daten.

Auch Tweets kann ich neuerdings so einbetten. Dazu habe ich mir einen Kirbytag geschrieben:

( embed: https://twitter.com/USER/status/TWEET )

Hier wollte ich nicht mit JavaScript arbeiten. Beim Rendern der Seite wird dieser Tag gegen ein HTML-Snippet ausgetauscht und die Daten entsprechend eingefügt.

So kann ich Tweets einbinden, ohne dass Daten an Twitter geschickt werden. Mit einer Ausnahme: Den Avatar speichere ich nicht mit ab, der kommt also weiterhin von Twitters Servern.

Fazit

Im Großen und Ganzen bin ich zufrieden mit der Lösung. Hier und da hakt es manchmal noch, aber in 98 % der Fälle klappt alles und die meisten Webseiten liefern inzwischen ausreichend gute Daten.

Vermutlich werde ich das Plug-in nach und nach erweitern, um andere Inhalte einbetten zu können. Ich halte diese Lösung für charmant, weil ich auf diese Weise Inhalte anzeigen kann, ohne dass die Daten meiner Seitenbesucher weitergereicht werden.