![]() |
Text automatisch markieren |
|
| |
| E-Mail: |
|---|
Bei Fragen zu diesem Beitrag bitte den Autor des Beitrags kontaktieren!
Dank geht an Roland Skop für seine Mitarbeit.
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:
<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
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.
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);
}
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
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.
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:
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
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.
...
} 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.
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:
... } 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.
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
Unobtrusive JavaScript.
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.
© 2007
Impressum, für diese Seite:
molily@selfhtml.org