HTML/Tutorials/Links/Bookmarklets

Aus SELFHTML-Wiki
< HTML‎ | Tutorials‎ | Links
Wechseln zu: Navigation, Suche

Dieser Artikel greift einen Selfhtml-Aktuell Artikel von Robert Bienert aus dem Jahr 2006 auf.[1] Der Bedarf für Bookmarklets mag zurückgegangen sein, für manche Zwecke sind sie aber nach wie vor nützlich - und sie bergen auch Gefahren.

Was ist ein Bookmarklet oder Favelet

Ein Bookmarklet ist nichts weiter als eine URL, die das Schema javascript: verwendet. Ruft man mit einem Browser eine solche URL ab, wird das darin befindliche JavaScript ausgeführt.

Solche URLs können vom Benutzer als Lesezeichen (Favourite, daher Favelet) im Browser abgespeichert werden und dann auf jeder beliebigen Webseite ausgeführt werden. Solche Bookmarklets können nur durch bewusstes Tun des Benutzers entstehen und sind deshalb üblicherweise vertrauenswürdig.

Sie können aber auch im href-Attribut eines <a> oder <area>-Elements stecken und beim Klick auf den entsprechenden Link ausgeführt werden. Webseiten, die Inhalte ihrer Besucher präsentieren (z.B. Foren oder Blogs mit Kommentarfunktion) müssen deshalb aufpassen, solche URLs nicht zuzulassen

Was können Bookmarklets

Das Script in einem Bookmarklet kann alles tun, was auch JavaScript auf der Webseite tun kann: das DOM manipulieren, globale Variablen verändern, und auch Ressourcen abrufen. Dieser Artikel kann und soll jedoch keine JavaScript-Einführung sein, er soll nur die grundsätzlichen Mechanismen und Restriktionen darstellen. Die Kenntnis von JavaScript und der Möglichkeiten, die es im Browser hat, wird deshalb vorausgesetzt.

Popup-Nachricht
<a href="javascript:alert('You have been pwned!');">Impressum</a>

Einige der Restriktionen für http-URLs gelten hier nicht, beispielsweise müssen die Leerstellen nicht als %20 codiert werden (und dürfen auch nicht - wie es bei Webservern möglich ist - durch ein + ersetzt werden). Acht geben müssen Sie allerdings bei

Eins der Beispiele von Robert Bienert zeigte, wie man mit einem Bookmarklet Inhalte von Browservariablen anzeigen kann:

Anzeige der User-Agent Kennung
<a href="javascript:alert(navigator.useragent);">User-Agent anzeigen</a>

Das funktioniert auch heute noch, im Allgemeinen würde man hierfür aber die Entwicklerwerkzeuge des Browsers öffnen und dort die Konsole benutzen. In Browsern, die keine Entwicklerwerkzeuge haben (Mobilgeräte), ist diese Methode für eine schnelle Abfrage immer noch anwendbar.

Das einzige, was Sie in einem Bookmarklet nicht verwenden können, sind Zeilenumbrüche. Das Script muss sich auf einer einzigen Codezeile befinden. Technisch ist das kein Problem, JavaScript erlaubt einzeilige Programme beliebiger Komplexität. Für die Bearbeitung des Bookmarklets ist dies jedoch ein gravierendes Hindernis. Wenn Sie ein längeres Bookmarklet erstellen und pflegen möchten, erstellen Sie es wie ein normales Script und besorgen Sie sich ein Minifizierungstool, mit dem Sie das Script in eine Codezeile komprimieren können. Oder laden Sie das Bookmarklet als Script nach, wie im Abschnitt „Größere Bookmarklets“ dargestellt

Viele Beispiele für Bookmarklets möchten mit window.open Fenster öffnen. Die Popupblocker der heutigen Browser verhindern allerdings zumeist das Öffnen von Fenstern, deren URL einen anderen Origin als die dargestellte Seite hat. Deshalb soll auf solche Beispiele verzichtet werden.

Wovon dringend abgeraten werden soll, ist das Erstellen eines Login-Bookmarklets, das automatisch Login-Daten in ein Anmeldeformular einträgt. So etwas geht natürlich, man muss lediglich die entsprechenden Eingabefelder suchen und die Werte dort eintragen, aber damit hat man seine Login-Daten ungeschützt in einem Bookmarklet liegen. Gerät dieses in falsche Hände, sind die Login-Daten verloren. Verwenden Sie dafür einen Login-Mananger, den der Browser mitliefert oder den Sie separat installieren.

Vorsicht, Rückgabewert!

Die Ausführung von JavaScript-Anweisungen kann einen Wert ergeben. Das folgende Bookmarklet hat beispielsweise den Wert "Self":

javascript:alert("Selfhtml");window.zzz="Self";

Wenn ein Bookmarklet eine Zeichenkette zurückgibt, wird diese vom Browser als neues, anzuzeigendes Dokument aufgefasst. Das kann Absicht sein, oder auch nicht. Sie könnten ein Bookmarklet erstellen, das von einem Server ein HTML Dokument lädt und dieses zurückgibt. Es könnte aber auch nur ein Versehen sein.

Empfehlung: Um sicher zu sein, dass Ihr Bookmarklet keine Zeichenkette zurückgibt, definieren Sie es einfach als Anweisungsblock. Damit ist sein Wert immer undefined:

javascript:{alert("Selfhtml"); window.zzz="Self";}

Größere Bookmarklets

Um das Minifizieren des Bookmarklets auf eine Zeile zu vermeiden, oder um Bookmarklets nutzen zu können, die länger sind als eine URL sein sollte, können Sie das Script auch aus einer externen JS Datei nachladen. Dazu erstellen Sie ein <script>-Element mit einem src-Attribut und fügen es dem DOM hinzu. Der Browser lädt das daraufhin das Script und führt es aus.

Hierbei sind einige Aspekte zu beachten. Vor allem sollte man vermeiden, ein solches Script mehrfach zu laden. Das Script sollte deshalb ein Element im DOM oder eine Eigenschaft im Window-Objekt hinterlassen, über die das Script erkannt und bei wiederholtem Bookmarklet-Aufruf neu gestartet werden kann. Damit das eigentliche Bookmarklet keinen "Fußabdruck" im Browser hinterlässt, sollte es als IIFE gestaltet sein. Das Bookmarklet sähe als übersichtliches Script so aus:

Wiedererkennung
(function() {
   const url = "https://example.org/scripts/favelet3.js",
         myself = document.head.querySelector(`script[src=${url}]`);
   if (myself)
      myself.restart();
   else
      document.head.insertAdjacentHTML("beforeend", `<script type='module' src='${url}'>`);
})();

Die einzeilige Bookmarklet-Darstellung wäre hingegen diese (das Wiki bricht es um). Bis auf den Namen des Scripts ist dieses Loader-Bookmarklet generisch und kann als Vorlage verwendet werden.

Wiedererkennung im Bookmarklet-Stil
(function(){const url="https://example.org/scripts/favelet3.js",myself=document.head.querySelector(`script[src=${url}]`);myself? myself.restart():document.head.insertAdjacentHTML("beforeend",`<script type='module' src='${url}'>`);})();

Dieses Bookmarklet sucht im DOM das Script-Element, das es beim ersten Aufruf erstellt hat, und geht davon aus, dass es an diesem Script-Element eine Methode restart gibt. Das geladene Script hat die Aufgabe, sein eigenes <script>-Element zu finden und die restart-Methode dort hinzuzufügen. Auf diese Weise bleibt der Ablauf isoliert und benötigt keine globalen Variablen.

Ein weiterer Punkt ist, dass das externe Script einen minimalen „Fußabdruck“ im Browser hinterlassen muss, damit es die Seite, in der es ausgeführt wird, nicht stört. Schlimmer noch: Es gibt Seiten, die zu erkennen versuchen, ob der Anwender Bookmarklets ausführen will oder auf andere Weise Dinge ins DOM injiziert. Vor einer solchen Erkennung möchte man sich natürlich – soweit möglich – verbergen.

Deshalb wird das externe Script mit type="module" geladen, damit seine Variablen vom übrigen Dokument isoliert bleiben. Hinzu kommt, dass ein ES6-Modul über import.meta.url auf die URL zugreifen kann, von der es geladen wurde, so dass man das Script-Element, über das das Script geladen wurde, finden kann, ohne die URL im Script hinterlegen zu müssen.

Im Übrigen kann es sich am folgenden Muster orientieren:

Rahmen für ein externes Bookmarklet
const myself = findMyself();
if (myself != null) {
  initialize();
}

function initialize() {
  // Initialisierung durchführen
  ...
  // Restart-Funktion am Script-Element hinterlegen
  myScript.restart = moduleAction;
  // Eigentliche Bookmarklet-Aktion durchführen
  moduleAction();
}

function moduleAction() {
  console.log("Bookmarklet wird ausgeführt");
}

function findMyself() {
  for (const scriptElem of document.head.querySelectorAll("script")) {
    if ((new URL(scriptElem.src)).href == import.meta.url)
      return scriptElem;
  }
  return null;
}

Wie Sie das Script genau aufbauen, hängt natürlich von Ihren Bedürfnissen ab. Was als Initialisierung zu geschehen hat, ob die Restart-Aktion auch beim erstmaligen Aufruf durchzuführen ist oder etwas anderes passieren soll, das kann ein allgemeiner Rahmen nicht vorgeben. Sie können das DOM der Seite manipulieren, Stylesheets ergänzen oder manipulieren, Sie können auch "nach Hause telefonieren", also Webservices auf Ihrem eigenen Server aufrufen. Aber nur dort. Auf dem Server, von dem die eigentliche Seite geladen wurde, können Sie nur Services aufrufen, wenn das dort mittels CORS freigeschaltet wurde.

Das Muster zeigt vor allem, wie das externe Script das <script>-Element findet, mit dem es geladen wurde, und daran die restart-Aktion hinterlegt. Dazu muss das Script eine Möglichkeit haben, dieses Element zu identifizieren. Das kann über eine ID geschehen, die dann aber im Bookmarklet und im externen Script bekannt sein muss. Wir verwenden hier die import.meta-Pseudovariable, die seit 2018 in den Evergreen-Browsern zu finden ist und aus der man die URL erfahren kann, mit der das Script geladen wurde. Wollen Sie import.meta nicht nutzen, weil Sie auch Altbrowser supporten müssen, dann vergeben Sie eine ID oder tragen Sie den Namen des Scripts als Konstante im Script ein.


Referenzen

  1. Selfhtml-Aktuell 2006: Robert Bienert, Bookmarklets, archiviert von archive.org