JavaScripte für die Textauswahl: SelectionMenu und CopyLink

Mit dem Arbeiten mit Textauswahl habe ich mich schon in zwei vorigen Artikeln beschäftigt: Text automatisch markieren sowie Text an Cursorposition einfügen, an dem ich als Herausgeber mitgewirkt habe.

Textauswahl-APIs

Mit JavaScript ist es möglich, auf den vom Anwender selektierten Text zuzugreifen. Zudem lässt sich Text programmatisch markieren und die Markierung verändern. Das funktioniert über drei Schnittstellen:

  • window.getSelection() ist eine einst von Netscape/Mozilla erfundene Methode. Über das zurückgegebene Objekt können wir auf den ausgewählten Text sowie die dahinterliegenden DOM-Knoten zugreifen. Das Mozilla Developer Center hat zum Selection-Objekttyp eine gute Referenz. Mittlerweile wird die Selection-API in HTML5 standardisiert. Die Browser-Engines Gecko, Webkit und Presto unterstützen sie bereits.
  • Die Manipulation der Auswahl erfolgt über den W3C-Standard DOM 2 Range aus dem Jahr 2000. Dieser lässt zwar viele Wünsche offen, ist an einigen Stellen umständlich und bedarf einer Überarbeitung, doch er wird ebenfalls von den besagten Browser-Engines unterstützt. Das zentrale Range-Interface ist ebenfalls übersichtlich bei MozDev dokumentiert. Über das besagte Selection-Objekt kommt man an das zugehörige Range-Objekt.
  • Der Internet Explorer 8 (Trident-Engine) unterstützt weder window.getSelection() noch DOM 2 Range. Er hat jedoch äquivalente Objekte. Das sind document.selection sowie TextRange. Diese stellen ähnliche Funktionalität wie die oben genannten APIs zur Verfügung, arbeiten jedoch etwas anders. Manche Aufgaben lassen sich damit einfacher umsetzen als mit DOM 2 Range, manche schwieriger.

Um ein browserübergreifendes Script zu schreiben, müssen wir also eine Fähigkeitenabfrage machen, die zwischen window.getSelection und document.selection unterscheidet. Dann wird jeweils noch geprüft, ob die benötigten DOM-Range- bzw. TextRange-Fähigkeiten unterstützt werden. Die Funktionalität muss dann leider für beide Modelle separat implementiert werden.

Ich habe zwei kleine Scripte geschrieben, die das Arbeiten mit der Textauswahl demonstrieren.

SelectionMenu – Kontextmenü zur Markierung einblenden

newyorktimes.com hat ein praktisches Feature: Wenn man ein Wort markiert, erscheint darüber eine Sprechblase mit einem Fragezeichen. Bei einem Klick darauf öffnet sich ein Fenster und das markierte Wort wird im Wörterbuch nachgeschlagen:

Diese Funktion lässt sich recht einfach mit Ranges/TextRanges umsetzen. Man fragt die Range der aktuellen Auswahl ab und erstellt eine zweite, die am Ende der ersten beginnt. In diese leere Range wird nun ein Element eingefügt. Dieses Element beinhaltet das Kontextmenü, welches mit CSS über dem Wort positioniert wird. Bei einem Klick auf einen Menüeintrag wird der ausgewählte Text z.B. in eine Adresse eingefügt, welche aufgerufen wird.

Die Anwendung des Scriptes sieht so aus: Der Konstruktor SelectionMenu wird mit new aufgerufen. Der Funktion wird ein Object mit der Konfiguration übergeben. Die Eigenschaften dieses Objects sind:

container (Elementobjekt) Das DOM-Element, bei dessen Inhalt das Menü angezeigt wird
menuHTML (String) Der HTML-Inhalt des Menüs
handler (Funktion) Handlerfunktion, die beim Klick auf das Menü ausgeführt wird (gleich welcher Eintrag)
minimalSelection (Number, optional) Minimalanzahl der ausgewählten Zeichen, damit das Menü angezeigt wird. Der Standardwert ist 5.

Das folgende Anwendungsbeispiel erzeugt ein Kontextmenü mit Such-Links zu Google und Bing. Bei einem Klick auf die Menüeinträge wird der markierte Text bei der jeweiligen Suchmaschine gesucht.

new SelectionMenu({
   // Überwache das Element mit der ID selectionmenu-demo
   container : document.getElementById('selectionmenu-demo'),
   // Menü-HTML
   menuHTML : 'Google-Suche' + 
              'Bing-Suche',
   // Handler-Funktion
   handler : function (e) {
      var target = e.target || e.srcElement,
         id = target.id,
         selectedText = this.selectedText,
         query = encodeURI(selectedText.replace(/\s/g, '+')),
         searchURI;
      
      if (id == 'selection-menu-google') {
         searchURI = 'http://www.google.de/search?ie=utf-8&q=';
      } else if (id == 'selection-menu-bing') {
         searchURI = 'http://www.bing.com/search?q=';
      }
      
      location.href = searchURI + query;
   }
});

In der Handlerfunktion wird auf das angeklickte Element zugegriffen (Event-Target) und dessen ID ausgelesen. Der markierte Text wird aus this.selectedText ausgelesen. this verweist auf die zugehörige SelectionMenu-Instanz.

Über die ID des angeklickten Elements findet die Auswahl zwischen Google und Bing statt. Der markierte Text wird in die Google- bzw. Bing-Adresse eingebunden, welche schließlich über eine Zuweisung an location.href angesprungen wird.

Ich habe die Lösung mit IDs und einer einzigen Handlerfunktion gewählt, da das Event-Handling sonst sehr kompliziert werden würde. Mit TextRanges im Internet Explorer wird mit HTML-Code, nicht auf DOM-Ebene gearbeitet. Das heißt, beim Einfügen des Menüs müssten die immer Event-Handler immer von neuem registriert werden. Daher habe ich mich erst einmal für eine einfache Lösung mit einem Handler entschieden, in welchem dann nach dem angeklickten Link unterschieden wird.

Probieren Sie das Script aus! Markieren Sie Text in diesem Abschnitt, um das Kontextmenü zu öffnen.

Das sieht dann in etwa so aus:

⇩ selectionmenu-1.0.zip herunterladen (6 KB)

Das ZIP enthält das kommentierte Script selectionmenu.js, eine komprimierte Version selectionmenu.min.js, zur Formatierung das Stylesheet selectionmenu.css sowie eine Beispielseite.

Das komprimierte Script ist nur 2KB groß. Es ist eigenständig und benötigt keine Bibliothek und kein Framework.

Getestete Browser: Firefox 3.5.8 und 3.6.3, Internet Explorer 6, 7 und 8, Safari 4.0.5, Chrome 4.1, Opera 10.00 und 10.51

Starten der Scripte bei DOM ready

Beide vorgestellten Scripte erwarten die Übergabe eines DOM-Elements im container-Parameter. Es empfiehlt sich, die Scripte z.B. nur auf die Inhaltsspalte oder den Artikeltext anzuwenden. Der Zugriff auf die zugehörigen Container-Elemente muss beim Aufruf von new SelectionMenu bzw. new CopyLink möglich sein. Daher sollten die Scripte beim DOM ready oder spätestens beim Eintreten des load-Ereignisses ausgeführt werden (siehe Onload-Techniken). Ein DOM-ready-Script ist nicht enthalten. Es ist jedoch Bestandteil aller großen Frameworks wie jQuery, Mootools, Prototype, YUI. Beispielsweise unter jQuery können sie schreiben:

jQuery(document).ready(function () {
	new SelectionMenu({ ... });
	new CopyLink({ ... });
});

Alternativ zu den genannten Bibliotheken können Sie eine separate Helferfunktion nutzen, z.B. ContentLoaded von Diego Perini. Das Initialisieren der Scripte beim DOM ready ist empfohlen, damit die Funktionalität schnellstmöglich zur Verfügung steht.

Die Scripte enthalten jeweils eine einfache addEvent-Funktion, mit welcher Sie Handler für das dokumentweite load-Ereignis registrieren können, falls Sie nicht mit DOM ready arbeiten können.

SelectionMenu.addEvent(window, 'load', function () {
	new SelectionMenu({ ... });
});
// bzw.
CopyLink.addEvent(window, 'load', function () {
	new CopyLink({ ... });
});

Scripte bei Github

Sie können an der Weiterentwicklung der Scripte über Github teilnehmen: github.com/molily/selectionmenu

« Artikel überarbeitet: Organisation von JavaScripten

Webentwicklerinnen zum Ada Lovelace Day 2010 »