Bookmarklets

jQuery: Eigene browser-übergreifende Bookmarklets erstellen

21. April 2010
von

Tommy Iamnotagoodartist

Bookmarklets sind kleine, JavaScript-codierte Anwendungen in Link-Form – auch Favelets genannt. Dank der client-seitigen Programmiersprache JavaScript, werden Bookmarklets von allen gängigen Browsern auf sämtlichen Plattformen unterstützt. Das Installieren eines zusätzlichen Plugins oder das Herunterladen von Software entfällt. In den meisten Fällen müssen Anwender lediglich das Favelet auf die Symbolleiste ihres Browsers zu ziehen – und das war’s. Dieses Tutorial erklärt Schritt für Schritt, wie Sie eigene Bookmarklets mithilfe von jQuery erstellen.

Meistens sind Booklets so genannte “One-Click”-Funktionen oder -Werkzeuge, die typischerweise dazu eingesetzt werden, um die Funktionen Ihres Browsers zu erweitern oder mit Online-Diensten zu interagieren. Mit Booklets können Sie zum Beispiel markierte Wörter bei Wikipedia oder in Google suchen oder Sie können damit bequem vom Frontend auf das Backend von WordPress zugreifen und vieles mehr (siehe Wikipedia).

Make Your Own Bookmarklets with jQuery

So fangen Sie am besten an

Sie können eine fiktive URI mit JavaScript erzeugen, indem Sie den Ausdruck javascript: voranstellen – etwa so:

<a href="javascript: alert('Arbitrary JS code!');">Alert!</a>

Achtung: Der Wert für das href-Attribut wird statt der doppelten (“) mit einfachen (‘) Anführungszeichen gesetzt. Sinn der Sache: Der Wert des href-Attributs und der JavaScript-Funktion sollen nicht mittendrin abgeschnitten werden. Einfache Anführungszeichen sind nicht die einzige Methode, mit der Sie den gewünschten Zweck erreichen, aber in diesem Fall funktioniert es prima.

Wir können nach diesem Schema nun weiter fortfahren und mehrere Zeilen JavaScript innerhalb der Anführungszeichen eintragen, jeweils per Semikolon voneinander getrennt, jedoch ohne Zeilenumbrüche. Falls Ihre Bookmarklets später nicht aktualisiert werden müssen, ist diese “all-inclusive”-Methode womöglich ausreichend. Für diesen Workshop werden wir den Code allerdings in eine JS-Datei auslagern, die wir irgendwo anders hosten.

Ein Link zu einem externen Bookmarklet:

<a href="javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://foo.bar/baz.js';})();">Externalized Bookmarklet</a>

Mit diesem Code suchen Sie nach dem Body des Dokuments und hängen ein  <script>-Element über das folgendermaßen definierte src-Attribut an: “http://foo.bar/baz.js”. Denken Sie jedoch daran, dass rein gar nichts passiert, wenn ein leeres Tab aktiv ist oder wenn ein Bereich aktiv ist, der keinen Body hat. Denn in dem Fall kann nichts angehängt werden.

Die .JS-Datei können Sie an einem beliebigen Ort hosten. Behalten Sie dabei aber die Bandbreite im Hinterkopf, falls Sie jede Menge Traffic erwarten.

jQuery einbinden

Da viele von Ihnen vermutlich mit dem jQuery-Framework vertraut sind, werden wir es zum Erstellen dieses Booklets verwenden.

Wie kommt der Code nun ins JS-Dokument? Am einfachsten geht das über das CDN-Netzwerk von Google (Link) – aber mit ein bisschen mehr Logik:

if (typeof jQuery == 'undefined') {
	var jQ = document.createElement('script');
	jQ.type = 'text/javascript';
	jQ.onload=runthis;
	jQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
	document.body.appendChild(jQ);
} else {
	runthis();
}

function runthis() {
	// Tragen Sie hier Ihren JavaScript-Code ein!
}

Sauber und simpel! Zunächst wird geprüft, ob jQuery schon geladen ist, da viele Internetseiten jQuery ohnehin verwenden – ein erneutes Aufrufen lässt sich so vermeiden. Falls jQuery noch nicht geladen ist, rufen wir es über Google auf und stellen die Funktion runthis() so ein, dass sie nach dem erfolgreichen Laden von jQuery startet. Falls jQuery schon geladen ist, wird der Schritt übersprungen und die Funktion runthis()direkt ausgeführt.

Informationen auslesen

Je nach Aufgabe Ihres Bookmarklets, möchten Sie vielleicht auf Informationen aus der jeweils aktiven Internetseite zugreifen. Die wichtigsten Informationen sind  document.location, was die URL der Seite liefert, oder document.title, was den Titel der Internetseite wiedergibt.

Sie können natürlich auch den vom Anwender ausgewählten Text auslesen; das ist allerdings ein bisschen kompliziert:

function getSelText() {
	var SelText = '';
	if (window.getSelection) {
		SelText = window.getSelection();
	} else if (document.getSelection) {
		SelText = document.getSelection();
	} else if (document.selection) {
		SelText = document.selection.createRange().text;
	}
	return SelText;
}

(Das ist eine leicht abgewandelte Form von dieser Variante: http://www.codetoad.com/javascript_get_selected_text.asp)

Ohne allzu sehr in die Tiefe zu gehen, definiert der Code zuerst eine leere SelText-Variable. Mit einigen weiteren Funktionen, wird versucht, die Variable mit dem ausgewählten Text oder Inhalt zu füllen, wobei jede Methode spezifisch auf verschiedene Browser abgestimmt ist. Es ist nicht ganz so simpel oder sauber, wie wir es vielleicht gerne hätten – aber es funktioniert.

Eine andere Möglichkeit besteht darin, über die input-Funktion von JavaScript die Anwender mittels Pop-Up zu befragen:

var yourname = prompt("What's your name?","my name...");

Zeichen-Codierung

Falls Sie das komplette JavaScript lieber im Link selbst unterbringen als in einer externen Datei, suchen Sie vielleicht nach einer besseren Methode, Einträge in Anführungszeichen ineinander zu verschachteln als die Abstufung zwischen einfachen und doppelten Anführungszeichen es erlaubt (zum Beispiel so, “ein Zitat ‘in einem Zitat’”). Verwenden Sie stattdessen &quot; (also so: “Ein Zitat &quot;in einem Zitat&quot;“):

<a
href="javascript:var%20yourname=prompt(&quot;What%20is%20your%20name?&quot;);alert%20(&quot;Hello,%20"+yourname+"!&quot;)">What is your name?</a>

Im vorigen Beispiel haben wir auch die Leerzeichen mit der Angabe %20 codiert, was bei älteren Browsern ratsam ist. Außerdem verhindern Sie auf diese Art, dass Links beim Übertragen zerstückelt werden und falsche Ergebnisse liefern.

Innerhalb von JavaScript ist es manchmal sinnvoll, Zitate zu vermeiden. Sie können das tun, indem Sie ihnen einen Backslash (\) voranstellen:

alert("Dies ist ein \"Zitat\" in einem Zitat.");

Die einzelnen Funktionen im Zusammenhang

Just for fun, let’s make a little bookmarklet that checks to see if there’s a selected word on the page, and, if there is, searches Wikipedia and shows the results in a jQuery-animated iFrame.

Lassen Sie uns spaßeshalber und zur Übung ein Bookmarklet erstellen, das prüft, ob auf einer besuchten Internetseite ein Wort markiert ist. Falls ja, soll das Booklet den Begriff in Wikipedia suchen und die Ergebnisse in einem jQuery-animierten iFrame ausgeben.

WikiFrame

So in etwa soll das fertige Ergebnis aussehen.

Wir fangen damit an, die Funktionen zum Einbinden von jQuery und dem Auslesen von Informationen zu kombinieren:

if (typeof jQuery == 'undefined') {
	var jQ = document.createElement('script');
	jQ.type = 'text/javascript';
	jQ.onload=runthis;
	jQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
	document.body.appendChild(jQ);
} else {
	runthis();
}

function runthis() {
	// Tragen Sie Ihren JavaScript-Code hier ein!
}

function getSelText() {
	var s = '';
	if (window.getSelection) {
		s = window.getSelection();
	} else if (document.getSelection) {
		s = document.getSelection();
	} else if (document.selection) {
		s = document.selection.createRange().text;
	}
	return s;
}

Als Nächstes ermitteln wir, ob Text ausgewählt wurde und speichern diesen in einer Variablen namens “s”. Falls nichts ausgewählt wurde, fordern wir den Anwender dazu auf:

var s = "";
s = getSelText();
if (s == "") {
	var s = prompt("Haben Sie schon etwas markiert?");
}

Die einzelnen Komponenten
Nachdem wir uns vergewissert haben, dass wir tatsächlich einen Wert für “s” bekommen haben, hängen wir diesen neuen Inhalt an den Body des Dokuments an. Darin enthalten sind:

  • ein DIV-Container (“wikiframe),
  • eine Ebene zum Abdecken des Hintergrunds (“wikiframe_veil”)
  • ein <p>-Element (“wird geladen ….”)
  • der iFrame selbst
  • etwas CSS.
Das CSS benötigen wir, um das Ganze etwas hübscher zu machen und Hintergrund-Abdeckung nebst iFrame über der aktuellen Seite zu fixieren.
if ((s != "") && (s != null)) {
	$("body").append("\
	<div id='wikiframe'>\
		<div id='wikiframe_veil' style=''>\
			<p>Loading...</p>\
		</div>\
		<iframe src='http://en.wikipedia.org/w/index.php?&search="+s+"' onload=\"$('#wikiframe iframe').slideDown(500);\">Enable iFrames.</iframe>\
		<style type='text/css'>\
			#wikiframe_veil { display: none; position: fixed; width: 100%; height: 100%; top: 0; left: 0; background-color: rgba(255,255,255,.25); cursor: pointer; z-index: 900; }\
			#wikiframe_veil p { color: black; font: normal normal bold 20px/20px Helvetica, sans-serif; position: absolute; top: 50%; left: 50%; width: 10em; margin: -10px auto 0 -5em; text-align: center; }\
			#wikiframe iframe { display: none; position: fixed; top: 10%; left: 10%; width: 80%; height: 80%; z-index: 999; border: 10px solid rgba(0,0,0,.5); margin: -5px 0 0 -5px; }\
		</style>\
	</div>");
	$("#wikiframe_veil").fadeIn(750);
}

Das src-Attribut des iFrame füllen wir mit der URL der Wikipedia-Suche plus “s”. Per CSS setzen wir die Anzeige des iFrame standardmäßig auf display: none;, so dass wir ihm über den Event-Handler onload und eine jQuery-Animation einen beeindruckenderen Auftritt verschaffen können, wenn die Seite geladen wird. Wenn all das zur ursprünglichen Seite hinzugefügt wurde, blenden wir die Ebene zum Abdecken des Hintergrunds ein.

Haben Sie die Backslashes am Ende jeder Zeile des angehängten HTML-Codes bemerkt? Sie erlauben mehrzeiligen Code, was ihn übersichtlicher und besser editierbar macht.

Nun sind wir fast fertig. Allerdings müssen wir jetzt nach sicherstellen, dass die Elemente nicht schon vorhanden sind, bevor wir sie anhängen. Das erreichen wir, indem wir den oben abgebildeten Code in eine bedingte Anweisung einbinden. Falls die Bedingung nicht erfüllt wird, sorgt der Else-Zweig dafür, dass alles zurückgesetzt wird.

Die daraus resultierende .JS-Datei sieht so aus:

if (typeof jQuery == 'undefined') {
	// http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS
	var jQ = document.createElement('script');
	jQ.type = 'text/javascript';
	jQ.onload=runthis;
	jQ.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js';
	document.body.appendChild(jQ);
} else {
	runthis();
}

function runthis() {
	if ($("#wikiframe").length == 0) {
		var s = "";
		s = getSelText();
		if (s == "") {
			var s = prompt("Haben Sie schon etwas markiert?");
		}
		if ((s != "") && (s != null)) {
			$("body").append("\
			<div id='wikiframe'>\
				<div id='wikiframe_veil' style=''>\
					<p>wird geladen...</p>\
				</div>\
				<iframe src='http://en.wikipedia.org/w/index.php?&search="+s+"' onload=\"$('#wikiframe iframe').slideDown(500);\">Enable iFrames.</iframe>\
				<style type='text/css'>\
					#wikiframe_veil { display: none; position: fixed; width: 100%; height: 100%; top: 0; left: 0; background-color: rgba(255,255,255,.25); cursor: pointer; z-index: 900; }\
					#wikiframe_veil p { color: black; font: normal normal bold 20px/20px Helvetica, sans-serif; position: absolute; top: 50%; left: 50%; width: 10em; margin: -10px auto 0 -5em; text-align: center; }\
					#wikiframe iframe { display: none; position: fixed; top: 10%; left: 10%; width: 80%; height: 80%; z-index: 999; border: 10px solid rgba(0,0,0,.5); margin: -5px 0 0 -5px; }\
				</style>\
			</div>");
			$("#wikiframe_veil").fadeIn(750);
		}
	} else {
		$("#wikiframe_veil").fadeOut(750);
		$("#wikiframe iframe").slideUp(500);
		setTimeout("$('#wikiframe').remove()", 750);
	}
	$("#wikiframe_veil").click(function(event){
		$("#wikiframe_veil").fadeOut(750);
		$("#wikiframe iframe").slideUp(500);
		setTimeout("$('#wikiframe').remove()", 750);
	});
}

function getSelText() {
	var s = '';
	if (window.getSelection) {
		s = window.getSelection();
	} else if (document.getSelection) {
		s = document.getSelection();
	} else if (document.selection) {
		s = document.selection.createRange().text;
	}
	return s;
}

Note that we fade out and remove the “wikiframe” content both if the user re-clicks the bookmarklet after it’s loaded and if the user clicks on its background veil.

Beachten Sie, dass wir den “wikiframe”-Inhalt sowohl dann ausblenden und entfernen, wenn der User das Bookmarklet erneut anklickt als auch beim Klick auf die Hintergrund-Abdeckung.

Dies ist das HTML-Bookmarklet zum Laden des JavaScripts:

<a href="javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://iamnotagoodartist.com/stuff/wikiframe.js';})();">WikiFrame</a>

Demo: WikiFrame

Voilà – da haben Sie ein funktionierendes Beispiel, was Sie mit einem Bookmarklet und etwas jQuery fabrizieren können!

Es geht noch besser

Das Beispiel war nett, aber es geht definitiv noch besser:

It wasn’t compressed. If your script will be accessed a lot, it may be a good idea to keep two versions of your code: a normal, working version and a compressed, minimized version. The one you’ll serve to your users will be compressed; this will save on loading time for them and bandwidth for you. Check the resource links below for some good JS compressors.

Der Code war nicht komprimiert. Wenn Ihr Skript häufig abgerufen wird, wäre es durchaus schlau, zwei Versionen Ihres Codes bereitzuhalten:

  • eine reguläre Arbeits-Version
  • eine komprimierte Mini-Version.

Ihren Anwendern geben Sie die komprimierte Version – das verkürzt die Ladezeiten bei Ihren Anwendern und Sie sparen Bandbreite. Im Abschnitt Nützliche Resourcen finden Sie auch einige Links zu web-basierten Java- und CSS-Kompressoren.

Obwohl das Bookmarklet rein technisch im alten IE6 funktioniert, bedeutet der Einsatz der festen Positionierung jedoch, dass der iFrame irgendwie ans Ende der Seite angehängt wird. Das ist nicht sehr anwenderfreundlich. Mit ein wenig mehr Zeitaufwand und Augenmerk auf die unterschiedliche Darstellung in den verschiedenen Versionen des Internet Explorer (siehe Smashing Magazine), können Sie das Bookmarklet so codieren, dass es nicht nur in verschiedenen Browsern gleich funktioniert, sondern auch (relativ) gleich aussieht.

In our example we used jQuery, which is an excellent tool when developing more advanced JavaScript applications. Chances are good, however, that if your bookmarklet is simple and doesn’t require a lot of CSS manipulation or animation, you may not need anything that advanced. Plain old JavaScript may suffice. Remember: the less you force the user to load, the faster their experience and the happier they’ll be.

In unserem Beispiel haben wir jQuery benutzt, das sich hervorragend eignet, wenn Sie etwas kompliziertere JavaScript-Anwendungen erstellen wollen. Für ein ein eher einfach gehaltenes Bookmarklet, das mit relativ wenig oder ohne CSS und Animationen auskommt, ist diese fortgeschrittene Technik schon ein bisschen überdimensioniert. Guter alter JavaScript-Code würde es in einem solchen Fall auch tun.

Merksatz: Je weniger Ladezeit Sie dem Anwender aufs Auge drücken, desto schneller ist das Anwender-Erlebnis und umso glücklicher machen Sie die Nutzer Ihrer Tools.

Bookmarklets

Methoden der Wahl – Dinge, an die Sie denken sollten

Ungetester Code ist nicht-valider Code, wie Programmierer der alten Schule gerne sagen. Auch wenn Bookmarklets auf jedem Browser laufen, der JavaScript unterstützt, kann es doch nicht schaden, die Tools auf möglichst vielen verschiedenen Browsern zu testen. Vor allem dann, wenn Sie CSS verwenden, kommt eine ganze Menge an Faktoren dazu, die sich auf Ihren JavaScript-Code auswirken. Spannen Sie zumindest Freundeskreis oder Familie zum Testen Ihrer Bookmarklets auf deren Computern und Browsern ein.

Tipp: Wesentlich effizienter – und stets auf den gängstigen Browsern/Betriebssystemen – testen Sie mit Online-Tools wie Adobe BrowserLab (siehe Dr.Web).

Apropos CSS, denken Sie auch daran, dass die Darstellung jeglichen Contents, den Sie einer Seite zufügen, von deren CSS-Stylesheet beeinflusst wird. In weiser Voraussicht sollten Sie daher eventuell ein Browser-Reset einbauen, um vererbte Margins, Padding oder Font-Eigenschaften zurückzusetzen und mit Ihren Werten zu überschreiben.

Since bookmarklets are, by definition, extraneous, many of the guidelines for JavaScript like unobtrusiveness and graceful degradation aren’t as sacred as they may normally be. For the most part, though, a healthy understanding of the best practices for traditional JavaScript and its frameworks will only help you.

Da Bookmarklets per Definition belanglos sind, sind einige der Richtlinien für JavaScript wie Unaufdringlichkeit oder Teilausfall nicht so “heilig” wie sonst üblich. Dennoch ist ein solides Verständnis für die ideale Anwendung, neudeutsch “Best Practises”, traditionellen JavaScripts und der diversen Frameworks sicherlich von hilfreich und von Vorteil.

  • Entwickeln Sie einen Programmier-/Codierungs-Stil und wenden Sie diesen konsequent an. Halten Sie Ihren Code sauber
  • Schonen Sie den Browser. Verzichten Sie auf unnötige Prozeduren und vermeiden Sie unnötige globale Variablen.
  • Nutzen Sie überall da, wo es sinnvoll ist, Kommentare. So können Sie sich später besser erinnern, was es mit dem Code auf sich hat und nahtlos weiterarbeiten.
  • Vermeiden Sie “Steno”-JavaScript. Machen Sie üppigen Gebrauch von Semikola, auch wenn Ihr Browser Ihnen mögliche Fehler verzeiht.

Nützliche Resourcen

Hilfreiche JavaScript-Tools:

JavaScript- und CSS-Kompressoren:

Sammlungen:

2 Kommentare zu „jQuery: Eigene browser-übergreifende Bookmarklets erstellen
  1. Rauter am 21. April 2010 um 23:37

    Wow, wirklich klasse artikel!

  2. Sven Lennartz am 23. April 2010 um 12:33

    man beachte auch die darstellung der quellcodes…

Ein Kommentar? Schön!

Wir freuen uns immer über Leser, die durch nützliche und konstruktive Beiträge zum Thema eine Diskussion anstoßen oder den Artikel mit weiteren Informationen anreichern. Alle Kommentare werden in diesem Sinne moderiert. Zum Kommentar-Fairplay gehört für uns auch der Einsatz von rel="nofollow". Bitte verwenden Sie zudem als Namen weder eine Domain noch ein spamverdächtiges Wort. Vielen Dank!