Teil von SELFHTML aktuell Teil von Artikel Teil von JavaScript

Text automatisch markieren

nach unten Mathias Schäfer
nach unten Einleitung
nach unten Funktion zum Markieren des Textinhaltes eines Elements
nach unten Anwendungsbeispiel
nach unten Ausblick

Mathias Schäfer

E-Mail: E-Mail molily@selfhtml.org

Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!

Dank geht an Roland Skop für seine Mitarbeit.

nach obennach unten

Einleitung

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 bereichsübergreifende Seite select-Methode markiert wird:

<input type="text" 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 Seite Text an Cursorposition einfügen.

Bisher gibt es keinen Standard, der den Zugriff auf Markierungen regelt. (Lediglich die englischsprachige Seite WHATWG bemüht sich derzeit um eine Vereinheitlichung im englischsprachige Seite 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 englischsprachige Seite 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 englischsprachige Seite 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 englischsprachige Seite Introduction to Range.

nach obennach unten

Funktion zum Markieren des Textinhaltes eines Elements

Beispiel:

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

Erläuterung:

Sowohl das Microsoft- als auch das Gecko-Modell repräsentieren die gegenwärtige Auswahl in einem Selection-Objekten. Die Modelle lassen sich grundsätzlich über die Zugriffsweise auf das Selection-Objekt unterscheiden: Im Microsoft-Modell ist es in der Eigenschaft englischsprachige Seite document.selection gespeichert, im Gecko-Modell liefert es die Methode englischsprachige Seite 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.

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 englischsprachige Seite 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:

if (document.selection && 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 englischsprachige Seite moveToElementText des TextRange-Objektes mit dem gewünschten Element als Parameter auf. Schließlich wenden wir die Auswahl an mit dem Aufruf der englischsprachige Seite select-Methode.

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

  ...
} else if (document.createRange && 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 (vorige Versionen nicht getestet), Opera ab Version 9.23 sowie Safari 3.0.4 (vorige Versionen nicht getestet) 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

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 englischsprachige Seite selectAllChildren() aufrufen:

  ...
} 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.

nach obennach unten

Anwendungsbeispiel

Popup-Seite Anzeigebeispiel: So sieht's aus

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 Seite Unobtrusive JavaScript.

nach obennach unten

Ausblick

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 bereichsübergreifendes Kapitel 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 englischsprachige Seite window.clipboardData mit der Methode setData() zur Verfügung (deutschsprachige Seite deutsche Referenz bei HTMLWorld). Dies ist hinsichtlich Benutzerfreundlichkeit und Sicherheit allerdings nicht unproblematisch und sollte nur in besonderen Webanwendungen Verwendung finden.

Teil von SELFHTML aktuell Teil von Artikel Teil von JavaScript

© 2007 bereichsübergreifende Seite Impressum, für diese Seite: E-Mail molily@selfhtml.org