JavaScript/Tutorials/Text automatisch markieren

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Information

Informationen zum Autor

Name:
Matthias Schäfer
E-Mail:
Dank geht an Roland Skop für seine Mitarbeit.


Einleitung[Bearbeiten]

Beim Arbeiten im Web spielt »Copy and Paste«, das Kopieren und Einfügen von Text, in vielen Fällen eine wichtige Rolle. Normalerweise nutzt der Anwender den Mauszeiger, um eine Textpassage im HTML-Dokument mit gedrückter Maustaste zu markieren, um diese dann in die Zwischenablage zu übertragen. In einigen Browsern ist aber auch eine Markierung mit der Tastatur möglich.

Solange es JavaScript gibt, wurden Scripte dazu gebraucht, das Auswählen und Kopieren zu vereinfachen. Klassisch ist das ein- oder mehrzeilige Eingabefeld (input- oder textarea-Element), dessen Text beim Fokus über die select-Methode markiert wird:

Beispiel
<input onfocus="this.select()" value="Kopiere mich!>

Diese Technik funktioniert bereits seit JavaScript 1.0. Auf das Eingabefeld konnte man damals nicht verzichten. Im Laufe der Zeit konnte man es mit CSS umformatieren, sodass es nicht mehr als solches erkennbar war – die JavaScript-Funktionalität blieb aber dieselbe.

Für JavaScript ist es nicht nur interessant, eine Markierung zu setzen, sondern auch den vom Leser markierten Text sowie dessen Position im Dokument auszulesen. Dies ist unter anderem für sogenannte Rich-Text-Editoren nötig (Stichwort contentEditable und designMode). Diese ermöglichen es, dass das Dokument editierbar wird und sich wie ein Textverarbeitungsprogramm verhält. Diese Möglichkeiten haben ganz neue Webanwendungen hervorgebracht, die von komfortablen E-Mail-Interfaces zu Online-Tabellenkalkulation reichen. Das einfache Einfügen von Formatierungscodes in eine Textarea beschreibt der Artikel Text an Cursorposition einfügen.

Bisher gibt es keinen Standard, der den Zugriff auf Markierungen regelt. (Lediglich die WHATWG bemüht sich derzeit um eine Vereinheitlichung im HTML 5 Working Draft.) Es wurden allerdings unterschiedliche proprietäre Schnittstellen entwickelt: eine von Microsoft und eine von Netscape bzw. Mozilla (Gecko-Engine).

Letztere baut zumindest teilweise auf DOM 2 Range des W3C auf. Dieser Standard beschreibt sogenannte Ranges (Bereiche, Ausschnitte, Spannen), die – vereinfacht gesagt – von einem Punkt im Dokument bis zu einem anderen reichen. Bekanntlich betrachtet das DOM ein Dokument als Baumstruktur bestehend aus Knoten, vor allem Element- und Textknoten. Eine Textmarkierung kann als eine Range dargestellt werden, die in einem Textknoten beginnt und in demselben oder einem anderen endet. Sie kann somit mehrere Knoten und deren (Teil-)Inhalt umfassen.

Das Microsoft-Konzept funktioniert im Grunde ähnlich, indem es Objekte vom Typ TextRange einführt. Der genaue Umgang mit Ranges soll hier nicht näher besprochen werden. Andere Browser als Internet Explorer und Firefox, namentlich Opera und Safari, haben sich zwar an den beiden proprietären Schnittstellen orientiert, sie aber noch nicht komplett fehlerfrei implementiert. Unter anderem aufgrund dieser heiklen Browsersituation existiert nur wenig Fachdokumentation zum Thema. Einen guten Einstieg bietet Peter-Paul Kochs Introduction to Range.

Funktion zum Markieren des Textinhaltes eines Elements[Bearbeiten]

Beispiel
function markieren (elem) {
  if (document.selection &amp;&amp; document.selection.createRange) {
    var textRange = document.selection.createRange();
    textRange.moveToElementText(elem);
    textRange.select();
  } else if (document.createRange &amp;&amp; window.getSelection) {
    var range = document.createRange();
    range.selectNode(elem);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
}

Sowohl das Microsoft- als auch das Gecko-Modell repräsentieren die gegenwärtige Auswahl in einem Selection-Objekt. Die Modelle lassen sich grundsätzlich über die Zugriffsweise auf das Selection-Objekt unterscheiden: Im Microsoft-Modell ist es in der Eigenschaft document.selection gespeichert, im Gecko-Modell liefert es die Methode window.getSelection() zurück.

Wollen wir also modellübergreifend programmieren, verzichten wir auf Browserabfragen wie »Internet Explorer oder Firefox?« und fragen nach, welchen Ansatz der jeweilige Browser unterstützt.

Beispiel
var currentSelection = null;
if (document.selection) {
  // Der Browser scheint das Microsoft-Modell zu kennen.
  currentSelection = document.selection;
} else if (window.getSelection) {
  // Der Browser scheint das Gecko-Modell zu kennen.
  currentSelection = window.getSelection();
}

Auf diese Weise können auf die aktuelle, durch den Anwender vorgenommene Auswahl in Form eines Objektes zugreifen. Allerdings interessiert uns dies nur zweitrangig, schließlich wollen wir eine neue Auswahl erzeugen bzw. die aktuelle Auswahl verschieben.

Um eine neue Auswahl im Microsoft-Modell zu erzeugen, benutzen wir die Methode document.selection.createRange. Wir prüfen also, ob auch diese Untermethode existiert. Wenn dies zutrifft, führen wir sie aus und empfangen von ihr das erzeugte TextRange-Objekt:

Beispiel
if (document.selection &amp;&amp; document.selection.createRange) {
  var textRange = document.selection.createRange();
  textRange.moveToElementText(elem);
  textRange.select();
} else ...

Die weitere Vorgehensweise im Microsoft-Modell ist ganz naheliegend: Wir rufen die Methode moveToElementText des TextRange-Objektes mit dem gewünschten Element als Parameter auf. Schließlich wenden wir die Auswahl an mit dem Aufruf der select-Methode.

Im Gecko-Modell können wir ebenfalls ein Range-Objekt mittels document.createRange() erzeugen. Anschließend wird die Auswahl mit selectNode() auf einen Elementknoten begrenzt. Schließlich wird sie mit addRange() dem aktuellen Selection-Objekt zugewiesen. Zuvor löschen wir mit removeAllRanges() gegebenenfalls vorhandenen Textmarkierungen.

Beispiel
  
} else if (document.createRange &amp;&amp; window.getSelection) {
  var range = document.createRange();
  range.selectNode(elem);
  var selection = window.getSelection();
  selection.removeAllRanges();
  selection.addRange(range);
}

Diese zweigleisige Vorgehensweise wird derzeit vom Internet Explorer ab Version 6, von Firefox ab Version 1.0, Opera ab Version 9.23 sowie Safari 3.0.4 unterstützt.

Die Reihenfolge der Fähigkeiten-Abfrage im Script hat einen Sinn. Denn der Opera-Browser will zwar beide Modelle unterstützen, die aktuelle Implementierung der Ranges nach dem Gecko-Modell ist allerdings noch lückenhaft. Deswegen wird zunächst die Unterstützung des Microsoft-Modells abgefragt, denn dieses unterstützt Opera hinreichend.

Alternative Umsetzung nach dem Gecko-Modell – Safari-Problematik[Bearbeiten]

Die für das Gecko-Modell vorgeschlagene Umsetzung ist vergleichsweise kompliziert und ist auch einfacher möglich. Es ist beim Gecko-Modell nicht unbedingt notwendig, ein Range-Objekt zu erzeugen und dieses an die Selection zu koppeln. Wir könnten ebenso vom aktuellen Selection-Objekt ausgehen und einfach dessen Methode selectAllChildren() aufrufen:

Beispiel
   
} else if (
  var selection = window.getSelection();
  selection.selectAllChildren(elem);
}

Wie man sieht, ist diese Umsetzung viel einfacher. Deren einziger Nachteil ist, dass Safari 3.0.4 sie noch nicht beherrscht (wohl aber die komplizierte). Glücklicherweise werden kommende Safari-Versionen selectAllChildren() beherrschen – im öffentlich einsehbaren Quellcode von Apple WebCore, der Safari-Rendering-Engine, ist die Methode nämlich bereits implementiert (Stand: 14.12.2007). In Zukunft können Sie also getrost die einfache Umsetzung verwenden, denn sie ist sicherlich weniger fehleranfällig.

Anwendungsbeispiel[Bearbeiten]

Beispiel ansehen …
In diesem Beispiel werden oft gebrauchte, aber auf der Tastatur nur schwer erreichbare Zeichen automatisch kopierfertig markiert.

Der Einsatz des Scriptes bietet sich vor allem in Dokumenten an, in denen Texte ausschließlich zum Nachschlagen und für Copy & Paste gelagert werden. Das Beispiel ist eine Tabelle für Webautoren und Webentwickler – es geht ihr nur darum, die Zeichen sowie deren Codes zum schnellen Copy & Paste bereitzustellen.

Nun ist es nicht unproblematisch, einfach die aktuelle, durch den Benutzer gesetzte Markierung zu löschen und sie ungefragt zu überschreiben – zumindest nicht als Reaktion auf ein bloßes Mouseover-Ereignis. Besser wäre ein Klick auf das Element selbst oder auf einen nebenstehenden Button, die die markieren-Funktion aufruft. Das Beispiel ist die Ausnahme, die die Regel bestätigt.

Das Beispiel zeigt, dass die automatische Markierung per JavaScript ein optionaler Zusatz ist. Nur wenn JavaScript aktiviert ist und der Browser hinreichend fähig ist, funktioniert das Script. Auf den entgegengesetzten Fall ist das Script aber auch vorbereitet - dann beendet es sich einfach und sollte keine Fehlermeldungen erzeugen. Das passt in das Konzept des Unobtrusive JavaScript.

Ausblick[Bearbeiten]

Die Möglichkeiten, die die beiden Schnittstellen bieten, gehen weit über die hier vorgestellte Nutzung hinaus. Nur wenigen JavaScript-Entwicklern sind diese Techniken bekannt, Tutorials mit browserübergreifenden Beispielen fehlen. Die Notwendigkeit der Berücksichtigung von zwei unterschiedlicher, zudem proprietärer Modelle erinnert uns an die dunklen Tage der späten 1990er-Jahre, als es nur Internet Explorer und Netscape jeweils mit eigenen DHTML-Modellen gab. Ein Vorstoß zur Standardisierung kommt derzeit nur von der WHATWG, steckt aber noch in den Kinderschuhen.

Im Zusammenhang mit Copy & Paste sei noch erwähnt, dass manche Browser eine (man ahnt es: proprietäre) Möglichkeit bieten, mittels JavaScript Text nicht nur auszuwählen, sondern ihn direkt auch in die Zwischenablage zu kopieren. Im Internet Explorer steht zu diesem Zweck das Objekt window.clipboardData mit der Methode setData() zur Verfügung (deutsche Referenz bei HTMLWorld). Dies ist hinsichtlich Benutzerfreundlichkeit und Sicherheit allerdings nicht unproblematisch und sollte nur in besonderen Webanwendungen Verwendung finden.