Formulare/WYSIWYG-Editor

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Formulare werden häufig benutzt, um dem Benutzer Einträge in Gästebüchern, Foren, Weblogs u. a.m. zu ermöglichen. Um diese Einträge optisch ansprechend gestalten zu können, z. B. durch Hervorheben von Textpassagen oder Anzeigen von Hyperlinks, wird oftmals das Formatieren von einzelnen Eingaben angeboten. Dazu gehört auch, dass dem Anwender eine komfortable Möglichkeit geboten wird, diese Formatierungen im Formular anwenden zu können, ohne detailliertes Wissen über BBCodes, HTML oder Markdown mitzubringen.

Der Kurs zeigt Ihnen, wie Sie anstelle eines nicht formatierbaren textarea-Elements einen kleinen WYSIWYG-Editor programmieren, mit dem Sie Text und ausgewählte Elemente einfügen, sowie markierten Text formatieren können.

Vorüberlegungen

Unser Editor soll sowohl eine WYSIWYG-Ansicht und HTML-Markup anzeigen. Über auch aus Textverarbeitungen bekannte Buttons soll der plain text eines Eingabefeldes zum mit Formatierungen angereicherten rich text editiert werden.

Dabei kann entweder durch Klicken eines Buttons Text in einer bestimmten Formatierung geschrieben, bzw. bereits vorhandener Text nachträglich ausgewählt und dann per Knopfdruck formatiert werden.

HTML-Markup

Im Unterschied zur HTML-Spielwiese soll zwischen der WYSIWYG-Ansicht und dem HTML-Markup umgeschaltet werden können, sodass die content-area zwei weitere Elemente enthält.

HTML-Grundgerüst
<div id="editor">
  <div id="toolbar"></div>
  <div id="content-area">
    <div id="wysiwyg" contenteditable></div>
    <textarea id="html"></textarea>
  </div>
</div>

Für die Toolbar würde sich das menu-Element eignen, das in HTML5.1 angeblich wiederbelebt werden sollte. Auch hier bildet ein div-Element die einfachere Lösung.

Toolbar

Markierter Text soll durch einen Klick auf einen Button in einer bestimmten Weise ausgezeichnet werden. Hier bieten sich button-Elemente geradezu an:

Toolbar mit Buttons
  <div id="toolbar">
    <button data-action="bold">F</button>
    <button data-action="cursive">k</button>
    <button data-action="underline">U</button>    
    <button data-action="quote">Zitat</button>
    <button data-action="send">Senden</button>
    <button data-action="view">Codeansicht</button>    
  </div>

Über die Data-Attribute können mit JavaScript Aktionen ausgelöst und die einzelnen Buttons mit CSS gestylt werden.

JavaScript

Aktivieren der Toolbar

Zunächst programmieren wir einige der Grundfunktionen:

Grundfunktionen ansehen …
document.addEventListener('DOMContentLoaded', function () {

   const editor = document.querySelector('#editor');
   const toolbar = editor.querySelector('#toolbar');	
   toolbar.addEventListener('click', getToolbarAction);	
   	
   function getToolbarAction(event) {
      let action = event.target.getAttribute('data-action');
      console.log(action);
      // Switching between different views
      if (action == 'view') {
         const wysiwygView = document.querySelector('#wysiwyg');
         const htmlView = document.querySelector('#html');
         if (wysiwygView.classList.contains('active')) { 
            htmlView.innerText = wysiwygView.innerHTML;
         } 
         else {  
            wysiwygView.innerHTML = htmlView.value;
         }
         wysiwygView.classList.toggle('active');
         htmlView.classList.toggle('active');	
      }
   }
});

Das Script wird geladen, sobald DOMContentLoaded ausgelöst hat.

Dann erhält die gesamte Toolbar über addEventListener eine Klickfunktionalität: Bei einen Klick (oder Auswählen mit der Tastatur) wird die Funktion getToolbar aufgerufen.

Sie ermittelt mit event.target, welcher Button geklickt wurde und weist den Wert des data-action-Attributs der Variablen action zu. Zur Kontrolle wird der ensprechende Wert in der Konsole ausgegeben.

Falls der view-Button geklickt wurde, wird mit ClassList.contains überprüft, ob wysiwygView die Klasse active besitzt. In diesem Falle werden mit innerHTML alle Kindelemente ausgelesen und dem value des textarea-Eingabefelds zugewiesen. Mit classList.toggle wird die Klasse active zwischen der WYSIWYG-Ansicht und dem Markup innerhalb des textarea-Felds umgeschaltet.

Die Sackgasse: execCommand

Eigentlich erledigt das contenteditable-Attribut ja schon die eine Hälfte der Arbeit. Darauf aufsetzend bot die document.execCommand()-Methode die Möglichkeit, ausgewählten Text problemlos zu formatieren, bzw. neue Elemente wie Links und Zeilenumbrüche einzufügen.[1]

Da sich die Browser-Hersteller bei der Umsetzung nicht auf einen Standard einigen konnten, ist diese Methode aber bereits wieder obsolet.[2]

 selectedText.execCommand(bold, false);

Diese Methode hätte selektierten Text fett erscheinen lassen, indem es den Text in ein b-Element, im IE in ein strong-Element, gepackt hätte.

Auswahl von Text

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 erreichen Sie mit getSelection().

Auswahl von Text ansehen …
	function getSelectedText() {
		var selectedText = '';
       	if (document.activeElement && document.activeElement.tagName.toLowerCase () == 'textarea' ) {
			var text = document.activeElement.value;
			selectedText = text.substring (document.activeElement.selectionStart, 
			document.activeElement.selectionEnd);
		}
		else { 
			var selRange = window.getSelection ();
			selectedText = selRange.toString ();
		}
        if (selectedText !== '') {
			console.log('selectedText = ' + selectedText);
        }
	}

Die Funktion getSelectedText startet damit, dass sie das gerade fokussierte Element mit Document.activeElement aufruft.

Dann wird im nächsten Zweig geprüft, ob der Browser die Eigenschaft selectionStart unterstützt. Dies ist bei Browsern mit Gecko-Engine (z. B. Firefox, Chrome, Safari und Edge) der Fall.

Beachten Sie: Da getSelectedText das gesamte Dokument anspricht, können Sie auch die Überschrift und den anschließenden Absatz selektieren - im fertigen Editor wird dies geändert.

Range

Eine Textmarkierung kann als eine Range (englisch für Bereich, Ausschnitt, Spanne) dargestellt werden, die – vereinfacht gesagt – von einem Punkt im Dokument bis zu einem anderen reicht.[3] Bekanntlich betrachtet das DOM ein Dokument als Baumstruktur bestehend aus Knoten, vor allem Element- und Textknoten. Eine solche Range kann in einem Textknoten beginnen und in demselben oder einem anderen enden. Sie kann somit mehrere Knoten und deren (Teil-)Inhalt umfassen.[4]

Einfügen von HTML-Markup

Der selektierte Text soll nun durch die Toolbar-Buttons mit HTML-Markup passend formatiert werden. Falls kein Text selektiert wurde, soll an der Cursor-Position ein neuer Elementknoten erzeugt werden, in dem dann weitergeschrieben wird.

Im Originalartikel wurde der Inhalt der textarea ausgelesen, in die 3 Bestandteile Anfang, Selektierter Text und Ende getrennt und dann mit String-Methoden zusammen mit den Zeichenketten für Anfangs- und End-Tags verbunden:

 input.value = input.value.substr(0, start) + aTag + insText + eTag + input.value.substr(end);

So konnten aber auch verschachtelte Link-Elemente eingefügt, bzw. im HTML-Markup vorhandene Tags „zerschossen“ werden.

  1. MDN: Making content editable
    • inkl. A simple but complete rich text editor
  2. W3C: HTML Editing APIs Work in Progress — Last Update 13 February 2014
    The features documented herein are obsolete. Authors should not use these features directly, but instead use JavaScript editing libraries. The features described in this document are not implemented consistently or fully by user agents, and it is not expected that this will change in the foreseeable future.
  3. W3C: DOM 2 Range
  4. Einen guten Einstieg zum Thema Ranges bietet Peter-Paul Kochs Introduction to Range