Table of Contents
|
Das Document Object Model (DOM) ist eine vom W3-Konsortium verabschiedete Norm, die den Programmiersprachen-Zugriff auf beliebige Elemente eines Auszeichnungssprachen-Dokuments beschreibt. Das DOM ist also weder selber eine Programmiersprache, noch ist es auf HTML beschränkt. Es definiert lediglich Objekte, Eigenschaften und Methoden, die eine Programmiersprache umsetzen sollte, wenn sie sich DOM-fähig bezeichnen will. Anwendbar sollen diese Objekte, Eigenschaften und Methoden auf alle Dokumente sein, die in einer XML-gerechten Auszeichnungssprache geschrieben sind.
Die JavaScript-Interpreter moderner Browser haben das DOM des W3-Konsortiums implementiert. Ähnlich wie bei anderen Sprachen ist der Implementierungsfortschritt dabei unterschiedlich. Die Grundlagen, die in diesem Abschnitt vorgestellt werden, funktionieren jedoch in allen heute verbreiteten Browsern. Das Anwenden des DOM in JavaScript wird auch als DOM-Scripting bezeichnet.
Eine Auszeichnungssprache wie HTML oder auch jede andere, XML-basierte Sprache ist als hierarchische Baumstruktur abbildbar. Die einzelnen Bestandteile einer solchen Baumstruktur werden als Knoten bezeichnet. Es gibt verschiedene Knotentypen. Innerhalb eines gewöhnlichen HTML-Dokuments gibt es auf jeden Fall drei wichtige Knotentypen, die Sie unterscheiden müssen: Elementknoten, Attributknoten und Textknoten.
Betrachten Sie zum Verständnis das folgende einfache HTML-Konstrukt:
<h1 lang="de">Hallo Welt</h1>
In diesem Konstrukt gibt es einen Elementknoten, nämlich den Elementknoten des h1-Elements. Ferner gibt es einen Attributknoten, nämlich den des lang-Attributs. Und schließlich gibt es Inhalte, die an zwei Stellen vorkommen: nämlich einmal der Inhalt des h1-Elements, und einmal der Inhalt der Wertzuweisung an das lang-Attribut. Diese Inhalte stellen selbst Knoten dar, nämlich Textknoten.
Ein weiteres Beispiel:
<h1 lang="de">Hallo <em>Welt</em></h1>
In diesem Beispiel ist die em-Auszeichnung um das Wort Welt hinzugekommen. Wichtig ist dabei zu verstehen, wie die Knotenhierarchie aussieht:
Das h1-Element ist in diesem kleinen Strukturbaum-Ausschnitt der Ausgangsknoten. Dieser Knoten hat nach den Regeln des DOM zwei Kindknoten und einen assoziierten Knoten: die Kindknoten sind zum einen der Textknoten mit dem Wort Hallo und dem Leerzeichen dahinter, sowie der Elementknoten des em-Elements. Das lang-Attribut im einleitenden <h1>-Tag ist dagegen kein Kindknoten, sondern ein assoziierter Knoten. Der Attributknoten hat jedoch selbst wiederum einen Kindknoten, nämlich den zugewiesenen Wert (de). Auch der Elementknoten des em-Elements hat wieder einen Kindknoten, nämlich den Textknoten seines Zeicheninhalts, also das Wort Welt.
Die Baumstruktur einer komplexen Web-Seite kann, wie sich aus diesen einfachen Beispielen erschließt, sehr umfangreich und tief verschachtelt sein. In einer Script-Sprache muss es aber möglich sein, möglichst schnell und effizient auf einzelne Knoten zugreifen zu können. Im DOM gibt es daher einige wichtige Methoden, um auf jeden beliebigen Elementknoten direkt zugreifen zu können:
Das nachfolgende Beispiel zeigt eine Anwendungsmöglichkeit von vielen. In dem HTML-Dokument wird eine nummerierte Liste von Großstädten zunächst alphabetisch sortiert ausgegeben. Unterhalb der Liste kann der Anwender jedoch mit Hilfe zweier Formular-Buttons zwischen alphabetischer Sortierung oder geographischer Sortierung wechseln.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Städte</title> <script> var geoArray = new Array(6, 7, 0, 1, 4, 3, 8, 9, 2, 5, 11, 12, 10); var Art = "ABC"; function ABC () { if (Art == "ABC") return; Art = "ABC"; var Staedte = new Array(); for (var i = 0; i < document.getElementsByTagName("li").length; i++) Staedte[Staedte.length] = document.getElementsByTagName("li")[i].firstChild.nodeValue; Staedte.sort(); for (i = 0; i < document.getElementsByTagName("li").length; i++) document.getElementsByTagName("li")[i].firstChild.nodeValue = Staedte[i]; document.getElementById("Art").firstChild.nodeValue = "alphabetisch von A bis Z"; } function GEO () { if (Art == "GEO") return; Art = "GEO"; var Staedte = new Array(); for (var i = 0; i < document.getElementsByTagName("li").length; i++) Staedte[Staedte.length] = document.getElementsByTagName("li")[i].firstChild.nodeValue; for (i = 0; i < document.getElementsByTagName("li").length; i++) document.getElementsByTagName("li")[i].firstChild.nodeValue = Staedte[geoArray[i]]; document.getElementById("Art").firstChild.nodeValue = "geographisch von Nord nach Süd"; } </script> </head> <body> <h1>Große Städte <span id="Art">alphabetisch von A bis Z</span></h1> <ol> <li>Berlin</li> <li>Dortmund</li> <li>Dresden</li> <li>Düsseldorf</li> <li>Essen</li> <li>Frankfurt</li> <li>Hamburg</li> <li>Hannover</li> <li>Köln</li> <li>Leipzig</li> <li>München</li> <li>Nürnberg</li> <li>Stuttgart</li> </ol> <form name="Formular" action=""> <input type="button" name="abc" value="alphabetisch" onclick="ABC()"> <input type="button" name="geo" value="geographisch" onclick="GEO()"> </form> </body> </html>
Im Beispiel ist die nummerierte Liste zunächst mit den Städtenamen in alphabetischer Sortierfolge notiert. In dem Formular unterhalb der Liste sind zwei Buttons notiert. Der eine ruft die Funktion ABC() auf, der andere die Funktion GEO(). Die Funktion GEO(), die im Scriptbereich im Dokumentkopf notiert ist, prüft zunächst über die Variable Art, ob die Liste bereits geographisch sortiert ist. Wenn dies der Fall ist, wird die Funktion vorzeitig mit return ohne Rückgabewert beendet. Andernfalls erfolgt die Sortierung. Dazu wird mit zuerst var Staedte = new Array(); ein neuer, leerer Array definiert. Das Vorhaben ist, diesen Array mit den Städtenamen aus der Liste zu füllen. Dazu greift die Funktion der Reihe nach in einer for-Schleife alle Elemente vom Typ li ab, die in dem Dokument vorkommen.
Der Zugriff erfolgt mit document.getElementsByTagName("li").
Über document.getElementsByTagName("li").length kann die Anzahl der li-Elemente im Dokument ermittelt werden. Diese Information benutzt die Funktion als Abbruchbedingung für die for-Schleife. Innerhalb der Schleife wird dem Array der Inhalt des jeweils aktuellen li-Elements hinzugefügt.
Mit document.getElementsByTagName("li")[i].firstChild.nodeValue wird dabei auf den Inhalt des jeweiligen li-Elements zugegriffen. Aus DOM-Sicht ist document.getElementsByTagName("li")[i] ein Knoten, nämlich der i.te li-Elementknoten im Dokument. firstChild ist eine Eigenschaft von DOM-Knoten. Über diese Eigenschaft können Sie auf den ersten Kindknoten eines Knotens zugreifen. Der erste Kindknoten aller li-Elemente im Dokument ist deren Textinhalt, also jeweils ein Städtenamen. Die Eigenschaft firstChild liefert aber noch nicht den Inhalt des Elements, sondern nur das Objekt des Inhalts. Um tatsächlich an den Inhalt heranzukommen, also an den konkreten Städtenamen, muss eine weitere Eigenschaft des Knoten-Objekts bemüht werden, nämlich die Eigenschaft nodeValue. Die Kombination firstChild.nodeValue können Sie sich ruhig merken. Diese Kombination wird häufig verwendet, um an den Inhalt eines Elements zu kommen.
Nachdem der Array mit den Städtenamen gefüllt ist, macht die Funktion GEO() einfach das Umgekehrte und schreibt den Array wieder zurück in die Liste - ebenfalls in einer for-Schleife.
Der Ausdruck document.getElementsByTagName("li")[i].firstChild.nodeValue steht diesmal links von der Zuweisung. Dadurch wird dem Inhalt des Listenelements dynamisch ein neuer Wert zugewiesen. Im Beispiel ist das der etwas vertrackst aussehende Wert Staedte[geoArray[i]]. Die Städte sollen ja geographisch ausgegeben werden. Nun gibt es keinen Algorithmus, der die Geographie kennt. Ganz oben im Script-Bereich ist daher ein Array namens geoArray notiert. Die Zahlen, mit denen er initialisiert wird, sind einfach die Indexnummern der alphabetisch sortierten Städte. So hat geoArray[0] beispielsweise den Wert 6. Dank dieser Information weiß die Funktion GEO(), dass die nördlichste der Städte diejenige ist, die in der alphabetischen Sortierung die Indexnummer 6 hat (Hamburg). Mit Staedte[geoArray[i]] benutzt die Funktion als aktuellen Index für die Stadt, die in die Liste geschrieben werden soll, also einfach den Zugriff auf geoArray.
Die Funktion ABC() geht ganz genauso vor wie die Funktion GEO(). Sie unterscheidet sich nur dadurch von letzterer, dass sie nach dem Einlesen des Staedte-Arrays die Objektmethode sort() auf den Array anwendet, um die Einträge zu sortieren. Dann schreibt sie den sortierten Array einfach zurück in die nummerierte Liste.
Ein weiteres Element wird von beiden Funktionen ebenfalls noch geändert: nämlich das span-Element innerhalb der Überschrift. Da für dieses Element im HTML-Code mit id="Art" ein ID-Wert notiert ist, lässt sich mit document.getElementById("Art") bequem darauf zugreifen. Mit der üblichen Syntax firstChild.nodeValue wird der Text des Elements angesprochen und dynamisch geändert.
Der HTML-Variante des DOM zufolge stellt jedes HTML-Element in einem HTML-Dokument ein Objekt dar. Der Zugriff auf die Elemente ist wie zuvor beschrieben über Objektmethoden wie document.getElementById() oder document.getElementsByTagName() möglich.
Jedes HTML-Element hat Eigenschaften. So stellt jedes erlaubte Attribut eines HTML-Elements eine DOM-Eigenschaft dieses Elements dar. Das HTML-Element input hat beispielsweise ein erlaubtes Attribut value, und das HTML-Element h1 hat ein erlaubtes globales Attribut title. Im DOM gibt es folglich ein input-Elementobjekt mit der Eigenschaft value, und ein h1-Elementobjekt mit der Eigenschaft title.
Darüber hinaus definiert das DOM für einige der HTML-Elemente auch Methoden. So gibt es beispielsweise für das form-Elementobjekt (also das DOM-Objekt des HTML-Elements form) die Methoden submit() (Formular absenden) und reset() (Formulareingaben verwerfen).
Das folgende Beispiel zeigt, wie Sie Verweise dynamisch ändern können.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Coole Links</title> <script> var Links = [ "http://www.storiesinflight.com/html5/", "http://html5boilerplate.com/", "http://www.cssclip.com/", "http://www.iconarchive.com/", "http://www.oswd.org/" ]; var AktuellerLink = 0; function CoolLink() { document.getElementById("CoolerLink").href = Links[AktuellerLink]; document.getElementById("CoolerLink").innerHTML = Links[AktuellerLink]; AktuellerLink += 1; if(AktuellerLink >= Links.length) AktuellerLink = 0; } </script> </head> <body onload="CoolLink();"> <div style="width: 500px; padding: 5px; border: 2px solid silver; font-size: 20px"> <a id="CoolerLink"></a> </div> <form style="width: 500px; padding: 5px; border: 2px solid silver; background-color: silver;"> <button type="button" onclick="CoolLink();">></button> </form> </body> </html>
Das Beispiel enthält im sichtbaren Bereich einen Hyperlink mit id="CoolerLink", der zunächst ein reiner Anker ist und weder ein Verweisziel definiert noch einen Elementinhalt hat. Unterhalb davon ist ein Formular mit einem Button notiert. Der Button enthält einen Event-Handler onclick, der beim Anklicken des Buttons ausgelöst wird. In diesem Fall wird die Funktion CoolLink() aufgerufen, die im Scriptbereich im Dokumentkopf notiert ist.
Diese Funktion greift mit document.getElementById("CoolerLink").href direkt auf das href-Attribut des Links mit <a id="CoolerLink"></a> zu. Wenn es wie im Beispiel in HTML noch gar nicht gesetzt ist, wird es durch die JavaScript-Anweisung gesetzt. Die Funktion weist dem href-Attribut einen der Links zu, die weiter oben im Scriptbereich als Array deklariert worden sind. Und zwar den jeweils nächsten Link. Dazu wird die Variable AktuellerLink als Kontrollzähler benutzt. Wenn AktuellerLink den Wert 0 hat, wird der erste Link zugewiesen, bei 1 der zweite Link usw. Damit sich die Links ändern, wird der Kontrollzähler mit AktuellerLink += 1; nach jedem Klick auf den Button um 1 erhöht. Wenn er die Anzahl der verfügbaren Links erreicht hat, wird er wieder auf 0 gesetzt. Auf diese Weise kann der Anwender mit dem Button endlos durch die verfügbaren Links klicken.
Im Browser hat die Zuweisung an das href-Attribut den Effekt, dass der Link nun ausführbar ist und zu der zugewiesenen HTTP-Adresse führt. Das ist aber nur die eine Hälfte der Aufgabe. Außerdem muss noch der sichtbare, anklickbare Linktext gesetzt bzw. geändert werden. Das geschieht in der Funktion CoolLink() durch die Wertzuweisung an innerHTML. Diese Objekteigenschaft ist zwar kein ganz sauberes DOM-Scripting, aber in der Praxis sehr bequem, um Elementen dynamisch einen Inhalt zuzuweisen. Im Browser hat das den Effekt, dass bei jedem Klick auf den Button nicht nur das href-Attribut des Links wechselt, sondern auch der angezeigte, anklickbare Linktext.
Seit der DOM-Version 2.0 wird auch geregelt, wie auf CSS-Eigenschaften eines HTML-Elements zugegriffen werden kann. Dabei hat man sich weitgehend an das seinerzeit von Microsoft eingeführte style-Objekt angelehnt. Das folgende Beispiel zeigt, wie Sie mit Hilfe der DOM-Technik eine kleine Animation schreiben können. Dazu dient die Möglichkeit, Elemente mit Hilfe von CSS-Eigenschaften absolut zu positionieren.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Kreisen ums Ich</title> <script> var rp, bp, ich; var rpGeschw = 10, bpGeschw = 20; var rpGrad = 0, bpGrad = 0; var rpX = 170, rpY = 170, bpX = 170, bpY = 170; var rpRadius = 150, bpRadius = 150; function Init () { rp = document.getElementById("roterPunkt"); bp = document.getElementById("blauerPunkt"); ich = document.getElementById("ich"); rp.style.position = "absolute"; rp.style.top = 20 + "px"; rp.style.left = 320 + "px"; bp.style.position = "absolute"; bp.style.top = 320 + "px"; bp.style.left = 320 + "px"; ich.style.position = "absolute"; ich.style.top = 110 + "px"; ich.style.left = 90 + "px"; ich.style.fontFamily = "Courier New,Courier"; ich.style.fontSize = "96px"; ich.style.fontWeight = "bold"; ich.style.color = "#009900"; rpKreis(); bpKreis(); } function rpKreis () { rpGrad += rpGeschw / 1000; if (rpGrad > 360) rpGrad = 0; rp.style.top = Math.round(rpY + (rpRadius * Math.cos(rpGrad))) + "px"; rp.style.left = Math.round(rpX + (rpRadius * Math.sin(rpGrad))) + "px"; window.setTimeout("rpKreis()", 100 / rpGeschw); } function bpKreis () { bpGrad += bpGeschw / 1000; if (bpGrad > 360) bpGrad = 0; bp.style.top = Math.round(bpY + (bpRadius * Math.cos(bpGrad))) + "px"; bp.style.left = Math.round(bpX + (bpRadius * Math.sin(bpGrad))) + "px"; window.setTimeout("bpKreis()", 100 / bpGeschw); } </script> </head> <body onload="Init()"> <div id="roterPunkt"> <img src="ichkreis1.gif" width="20" height="20" border="0" alt="roter Punkt"> </div> <div id="blauerPunkt"> <img src="ichkreis2.gif" width="20" height="20" border="0" alt="blauer Punkt"> </div> <div id="ich">ICH</div> </body> </html>
Innerhalb des body-Bereichs werden im Beispiel drei div-Bereiche ohne weitere Formatierungen notiert. Die ersten beiden enthalten jeweils eine kleine Grafik - ichkreis1.gif ist ein roter Punkt, ichkreis2.gif ein blauer Punkt. Beide Grafiken erhalten einen transparenten Hintergrund, was wichtig ist, da sich die Punkte im späteren Verlauf des Geschehens öfter überlagern werden.
Im einleitenden <body>-Tag ist der Event-Handler onload notiert. Dieser tritt in Aktion, sobald das Dokument und die Grafiken vollständig im Browser geladen sind. Dann wird die Funktion Init() aufgerufen, die im Kopfbereich notiert ist. Diese Funktion speichert zunächst einmal Objekte der drei div-Elemente in den drei Variablen rp, bp und ich, um die darauffolgenden Zugriffe auf diese Elemente zu verkürzen. Da alle drei div-Bereiche ein id-Attribut haben, ist der Zugriff auf den entsprechenden Elementknoten mit getElementById("roterPunkt") usw. möglich. Anschließend lässt sich mit rp usw. genauso arbeiten, als wenn man jedesmal wieder document.getElementById("roterPunkt") notieren würde.
Über die Variablen rp, bp und ich ist dann auch das style-Objekt ansprechbar. Die Funktion Init() stattet die drei div-Bereiche erst einmal mit anfänglichen CSS-Eigenschaften aus. Alle drei Bereiche werden mit style.position = "absolute" absolut im Browserfenster positioniert, also aus dem übrigen Elementfluss herausgelöst. Die linke obere Ecke jedes Bereichs wird mit style.left und style.top bestimmt. Der Inhalt des ich-Bereichs, also das Wort ICH, wird groß und auffällig formatiert.
Am Ende ruft die Funktion Init() die beiden anderen Funktionen rpKreis() und bpKreis() auf. Jede dieser beiden Funktionen kontrolliert die Kreisbewegung jeweils eines der beiden Punkte, indem sie letztlich die linke obere Ecke des div-Bereichs, der die jeweilige Grafik enthält, neu berechnet. Dabei kommen die Kreisfunktionen für Sinus (Math.sin()) und Cosinus (Math.cos()) zum Einsatz. Am Ende ruft jede der beiden Funktionen sich selber rekursiv mit window.setTimeout() wieder auf, um die nächste Position des div-Bereichs mit der Grafik zu setzen. Die Variablen wie bpGrad, bpGeschw usw., mit denen diese Funktionen arbeiten, wurden zuvor im globalen Bereich des Scripts initialisiert.
DOM-Scripting besteht nicht nur darin, auf bereits in HTML vorhandene Elemente zuzugreifen, sondern auch neue zu erzeugen und in den Strukturbaum einzuhängen. Wie das funktioniert, zeigt das folgende Beispiel. Es stellt einen primitiven Webseiten-Editor dar.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Webseiteninhalt bitte selber schreiben!</title> <script type="text/javascript"> function Hinzufuegen () { var Elementtyp = document.getElementById("Elementtyp"); var Elementinhalt = document.getElementById("Elementinhalt"); var Typ = Elementtyp.options[Elementtyp.selectedIndex].value; var Element = document.createElement(Typ); if (Typ != "HR") { var Text = document.createTextNode(Elementinhalt.value); Element.appendChild(Text); } document.getElementById("User").appendChild(Element); } </script> </head><body> <form action=""> <select id="Elementtyp" size="1"> <option value="h1" selected>Überschrift 1</option> <option value="h2">Überschrift 2</option> <option value="h3">Überschrift 3</option> <option value="p">Textabsatz</option> <option value="hr">Trennlinie</option> </select> <input type="text" id="Elementinhalt" size="50"> <input type="button" value="Hinzufügen" onclick="Hinzufuegen()"> </form> <div id="User"> </div> </body> </html>
Das Beispiel enthält im Dokumentkörper ein Formular mit einer Auswahlliste, einem Eingabefeld und einem Button. Unterhalb ist noch ein div-Bereich mit id="User" notiert, der jedoch noch keinen konkreten Inhalt hat. In der Auswahlliste des Formulars kann der Anwender einen HTML-Elementtyp auswählen - in den value-Attributen der option-Elemente sind die tatsächlichen HTML-Elementnamen der jeweiligen Auswahl gespeichert. Im Eingabefeld des Formulars kann der Anwender einen Textinhalt für das ausgewählte Element eingeben. Wenn er dann auf den Button klickt, wird onclick die Funktion Hinzufuegen() aufgerufen, die im Dokumentkopf in einem Scriptbereich notiert ist.
Im Scriptbereich im Dokumentkopf werden in den Variablen Elementtyp und Elementinhalt erst einmal die DOM-Objekte des select-Elements zur Elementtypauswahl und des input-Elements für den vom Anwender eingegebenen Elementinhalt gespeichert.
Die Anweisung var Typ = Elementtyp.options[Elementtyp.selectedIndex].value; initialisiert eine Variable namens Typ mit dem vom Anwender ausgewählten Eintrag aus der Auswahlliste. In Typ ist anschließend - je nach Auswahl - so etwas wie h1, h2, h3, p oder hr gespeichert.
Mit document.createElement(Typ) wird dann ein leeres, neues Element vom Typ Typ erzeugt, also je nach Wert der Variablen Typ z.B. ein h1-Element oder ein p-Element. Damit wird das Element aber noch nicht angezeigt. document.createElement() erzeugt lediglich den Elementknoten, hängt ihn aber noch nicht in den Strukturbaum des Dokuments ein. Der neue Elementknoten schwebt gewissermaßen noch im luftleeren Raum außerhalb des HTML-Dokuments.
Vor dem Einhängen des neuen Elements in die Dokumentstruktur muss das Beispielscript jedoch noch zwischen Elementen unterscheiden, die einen Textinhalt haben, und solchen, die keinen haben. Das hr-Element für Trennlinien, das der Anwender ebenfalls auswählen kann, kann keinen Textinhalt haben. Alle anderen auswählbaren Elemente dagegen können Textinhalt haben. Durch eine if-Anweisung wird daher abgefragt, ob die Variable Typ den Wert "hr" besitzt. Nur wenn dies nicht der Fall ist, wird der Textknoten mit der Methode document.createTextNode() erzeugt. Als Parameter erhält diese Methode den Text, den der Anwender ins Eingabefeld des Formulars eingegeben hat.
Auch erzeugte Textknoten schweben zunächst im luftleeren Raum, ohne Bezug zu anderen Knoten. Deshalb hängt das Script zunächst einmal den erzeugten Textknoten an den zuvor erzeugten Elementknoten an.
Für alle „Einhäng“-Vorgänge wird die Methode appendChild() verwendet. Anwendbar ist die Methode auf Knoten, die Kindknoten haben dürfen. Also beispielsweise Elementknoten. Als Parameter erwartet die Methode einen Knoten, der als Kindknoten eingehängt werden soll.
Wenn im Beispiel also steht: Element.appendChild(Text);, dann ist Element eine Variable, in der zuvor durch createElement() ein Elementobjekt erzeugt wurde, und Text ist eine Variable, die zuvor durch Aufruf von createTextNode() einen Textknoten gespeichert hat.
Mit document.getElementById("User").appendChild(Element) wird auf den zunächst leeren div-Bereich im Dokument zugegriffen. Diesem Element wird der gesamte neu erzeugte Elementknoten mit der appendChild()-Methode hinzugefügt.
Korrekturen, Hinweise und Ergänzungen
Bitte scheut euch nicht und meldet, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Diskutieren wählen. Es ist keine Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden allerdings kommentarlos entfernt.