Erschaffen und Vernichten von Elementen

Das Beste am DOM ist, dass man Elemente und Attribute nicht nur lesen und ändern, sondern auch neue erstellen oder alte löschen kann. Die folgenden Methoden helfen uns dabei.

Elemente und Inhalte erstellen

createElement(element)
Erstellt ein neues Element element.
createTextNode(string)
Erstellt einen neuen Textknoten mit dem Wert string.

Neu erschaffene Elemente werden nicht sofort dem Dokument hinzugefügt, sondern verweilen im Speicher bis wir sie irgendwo in der Baumstrukur einfügen. Daher müssen diese Methoden mit dem Dokumentobjekt anstatt eines Knotenobjektes aufgerufen werden.

JavaScript:
meinNeuerAbsatz=document.createElement('p');
meinNeuerText=document.createTextNode('this is a new paragraph');

Änderung bestehender Inhalte

setAttribute(attribute,value)
Fügt dem Objekt ein neues Attribut attribute mit dem Wert value hinzu.
appendChild(child)
Fügt dem Objekt den Kindknoten child hinzu. child muss ein Objekt sein, kein string.
cloneNode(bool)
Kopiert den gesamten Knoten - wenn bool true ist, inklusive aller Kindknoten.
hasChildNodes()
Prüft ob ein Objekt Kindknoten enthält und gibt true zurück, falls das der Fall ist.
insertBefore(newchild,oldchild)
Fügt newchild vor oldchild in das Dokument ein.
removeChild(oldchild)
Löscht den Kindknoten oldchild.
replaceChild(newchild,oldchild)
Ersetzt oldchild durch newchild.
removeAttribute(attribute)
Löscht das Attribut attribute des Elements.

Das Bildbeispiel

Nehmen wir an wir haben ein paar Verweise, die auf Bilder zeigen. In Webbrowsern ohne JavaScript sollen diese Bilder in einem neuen Fenster angezeigt werden. Webbrowser mit JavaScript sollen die Bilder direkt hinter den Verweisen anzeigen.

HTML:
<ul id="imglist">
 <li><a href="home.gif" target="_blank">Home 
 (new window)</a></li>
 <li><a href="home_on.gif" target="_blank">Home active 
 (new window)</a></li>
 <li><a href="jscsshtml.gif" target="_blank">HTML-CSS-Javascript 
 (new window)</a></li>
</ul>

Wenn JavaScript und das DOM vorhanden sind wollen wir:

Diese Funktion soll

Das erste Problem zu lösen ist einfach:

JavaScript:
function imgpop()
{
 var il,imga,imgatxt;

// gehe durch alle LI elemente in imglist
 il=document.getElementById('imglist').getElementsByTagName('li');
 for(i=0;i<il.length;i++)
 {

// nimm den ersten Verweis in dem derzeitigen LI
 imga=il[i].getElementsByTagName('a')[0];

// lösche (new window) im Verweistext 
// ( nodeValue des firstChild )  
 imgatxt=imga.firstChild;
 imgatxt.nodeValue=imgatxt.nodeValue.replace(/ \(new window\)/,'');

// füge die Aufrufe hinzu um popw() zu starten
 imga.onclick=function(){return popw(this);}
 //imga.onkeypress=function(){return popw(this);}
 }
}

In der Funktion popw() verwenden wir einige der bereits genannten Methoden:

Javascript:
function popw(o)
{
 var newimg;
// falls der übergeordnete Knoten (li) bereits ein Bild enthält 
 if(o.parentNode.getElementsByTagName('img').length>0)
 {
// lösche es
  o.parentNode.removeChild(o.parentNode.getElementsByTagName('img')[0]);
 } else {
// ansonsten, erschaffe ein neues Bild und rufe eine Funktion auf  
// die es löscht wenn der Benutzer es anklickt.
  newimg=document.createElement('img');
  newimg.style.display='block';
  newimg.onclick=function(){this.parentNode.removeChild(this);};
  newimg.src=o.href;
  o.parentNode.appendChild(newimg)
 }
 return false;
}
Probier dieses Beispiel aus

Das Datumpicker-Beispiel

Nehmen wir an, wir haben ein Formular, das Datumsfelder enthält und wir wollen Besuchern mit JavaScript einen Kalender anbieten, der die Auswahl erleichtert. Besucher ohne JavaScript sollen ein einfaches Textfeld erhalten.

Die Funktion des Kalenders lassen wir hierbei aussen vor, da es den Rahmen dieses Kurses sprengen würde, und konzentrieren uns auf den Aufruf eines fiktiven Kalenders.

Wir beginnen mit dem nötigen HTML. Die Elemente, die einen Kalender aufrufen sollen, markieren wir mit einer Klasse namens date.

HTML:
<h1>Flight booking</h1>
<form action="nosend.php" method="post" onsubmit="return check(this);">
<p>Step 1 of 4</p>
<h2>Please select your dates</h2>
<p>
 <label for="startdate">Start Date</label>
 <input type="text" class="date" id="startdate" name="startdate" />
</p>
<p>
 <label for="enddate34;>End Date</label>
 <input type="text" class="date" id="enddate" name="enddate" />
</p>
<p>
 <input type="submit" value="send" />
</p>
</form>

Wir gehen durch alle input Elemente im document, und prüfen, ob deren className date beinhaltet (Elemente können mehrere Klassen haben, es langt also nicht das className Attribut mit dem Namen zu vergleichen).

Falls dem so ist, erstellen wir ein neues Verweisobjekt und ein Textobjekt. Wir fügen den Text dem Verweis hinzu, und fügen den Verweis hinter dem Formularfeld ein.

JavaScript:
function addPickerLink()
{
 var inputs,pickLink,pickText;

// gehe durch alle input Elemente
 inputs=document.getElementsByTagName('input');
 for(i=0;i<inputs.length;i++)
 {
  
// prüfe, ob "date" in dem Wert der Klasse vorkommt
  if(/date/.test(inputs[i].className))
  {

// erstelle einen neuen Verweis und einen Text
   pickLink=document.createElement('a');
   pickText=document.createTextNode('pick a date');

// füge den Text dem Verweis hinzu
   pickLink.appendChild(pickText);

// setze den Verweis auf # und füge die Funktion hinzu.
   pickLink.setAttribute('href','#');
   pickLink.onclick=function(){picker(this);return false;};
   //pickLink.onkeypress=function(){picker(this);return false;};

// füge den Verweis als neuen Kindknoten dem Elternknoten 
// hinzu.   
   inputs[i].parentNode.appendChild(pickLink)
  }
 }
}
Probier das Pickerbeispiel aus.

Jetzt folgt jedem Datumsfeld ein Verweis, der die Funktion picker() aufruft.

Dieser Funktion muß jetzt beigebracht werden, wohin der Rückgabewert geschrieben werden soll.

Da wir den Verweis als Objekt der Funktion übergeben, ist das das vorherige Element auf der gleichen Ebene, das input Element.

JavaScript:
function picker(o)
{
 alert('This is a simulation only.') // no real function today
 o.previousSibling.value='26/04/1975';		
}

Fast richtig, allerdings kann das Einfügen des Verweises am Ende als neuer Kindknoten zu Problemen führen. Es kann beispielsweise sein, das im HTML auf dem Eingabefeld eine Leerzeile folgt, was von manchen Webbrowsern als eigener Knoten angesehen wird und damit zu einem Fehler führt.

Daher müssen wir prüfen, ob das vorherige Element auch wirklich ein Element ist, bevor wir den Wert ändern.

JavaScript:
function picker(o)
{
 alert('This is a simulation only.') // no real function today
 while(o.previousSibling.nodeType!=1)
 {
  o=o.previousSibling;
 }
 o.previousSibling.value='26/04/1975';		
}

Schleifen sind meistens langsam und geben den Eindruck von nicht optimiertem Code. Um die Schleife zu umgehen, müssen wir die Funktion umschreiben.

Optimieren der addPickerLink() Funktion

Die Verwendung von appendChild() ist kinderleicht, allerdings macht sie uns abhängig von dem verwendetem HTML. Was passiert wenn wir in der Zukunft beispielsweise ein SPAN mit einem Stern hinter dem Eingabefeld einfügen möchten um es als Pflichtfeld zu markieren?

Der Trick ist insertBefore() zusammen mit dem folgenden Knoten auf der gleichen Ebene, dem nextSibling, zu verwenden.

JavasSript:
function addPickerLink()
{
 var inputs,pickLink,pickText;
 inputs=document.getElementsByTagName('input');
 for(i=0;i<inputs.length;i++)
 {
   if(/date/.test(inputs[i].className))
   {
    pickLink=document.createElement('a');
    pickText=document.createTextNode('pick a date');
    pickLink.appendChild(pickText);
    pickLink.setAttribute('href','#'); 
    pickLink.onclick=function(){picker(this)};
    //pickLink.onkeypress=function(){picker(this)};

// füge den Verweis hinter dem Eingabefeld ein 
    inputs[i].parentNode.appendChild(pickLink)
    inputs[i].parentNode.insertBefore(pickLink,inputs[i].nextSibling);
    }
  }
}
Probier das optimierte Pickerbeispiel aus.

Dinge, die man sich merken sollte

Das ist Alles - mit diesen Hilfsmitteln können wir jedes einzelne Element im Dokument erreichen und ändern, und es dem Besucher einfacher machen ohne von JavaScript abhängig zu sein.

Am Anfang ist es etwas verwirrend, doch wenn man sich einmal in das DOM hineingedacht hat, wird es mit jedem mal einfacher.

Stolpersteine, die immer mal auftauchen können:

Und was ist mit innerHTML?

Als Microsoft den Internet Explorer 4 herausbrachte, kam auch innerHTML auf, ein weiterer Weg, Inhalte zu erstellen oder zu ändern. Es ist ein viel einfacherer Weg als der vom W3C vorgeschlagene, besonders wenn man die Inhalte von allen Kindknoten eines Elements auslesen will. Um das DOM-konform zu erreichen, muß man ziemlich komplexe Umwege [1] gehen.

innerHTML ist einfacher, hat aber auch seine Nachteile. Der größte Nachteil ist dass der zurückgegeben Wert ein Text ist, und keine Sammlung von Objekten. Ausserdem ist innerHTML nur auf HTML anwendbar, nicht auf XML, und DOM ist dazu gedacht, jede Art von strukturiertem Text zu verarbeiten. Ein Vergleich der beiden Techniken und Information darüber, welche Webbrowser innerHTML verstehen, kann in dem DOM Kapitel von Quirksmode.org[2] oder der Diskussion bei Developer-x[3] nachgelesen werden.

Links

[1] innerHTML für Mozilla http://www.webfx.nu/dhtml/mozInnerHTML/mozInnerHtml.html

[2] DOM Unterstützung und ein Vergleich mit innerHTML http://www.quirksmode.org

[3] DOM gegen innerHTML http://www.developer-x.com/content/innerhtml/

Creative Commons License Unless otherwise expressly stated, all original material of whatever nature created by Christian Heilmann and included in this web site and any related pages is licensed under the Creative Commons License.