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
attributemit dem Wertvaluehinzu. appendChild(child)- Fügt dem Objekt den Kindknoten
childhinzu.childmuss ein Objekt sein, keinstring. cloneNode(bool)- Kopiert den gesamten Knoten - wenn bool
trueist, inklusive aller Kindknoten. hasChildNodes()- Prüft ob ein Objekt Kindknoten enthält und gibt
truezurück, falls das der Fall ist. insertBefore(newchild,oldchild)- Fügt
newchildvoroldchildin das Dokument ein. removeChild(oldchild)- Löscht den Kindknoten
oldchild. replaceChild(newchild,oldchild)- Ersetzt
oldchilddurchnewchild. removeAttribute(attribute)- Löscht das Attribut
attributedes 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:
- Den Text "(new window)" in den Verweisen löschen.
- Einen Befehl hinzufügen, der die Funktion
popw()aufruft.
Diese Funktion soll
- Das Bild unterhalb des Verweises anzeigen, falls es nicht schon vorhanden sein sollte.
- Das Bild löschen falls es vorhanden ist (um eine mehrmalige Anzeige zu vermeiden).
- Das Bild löschen sobald der Besucher es anklickt.
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:
- Gehe auf Nummer Sicher: Überprüfe ob ein Element vorhanden ist,
bevor du es veränderst. Einige Webbrowser zucken mit den Achseln und
geben
falsezurück wenn wir aufobject.nextSibling.nodeNameprüfen, und es keinen weiteren Knoten auf der gleichen Ebene gibt oder dieser ein Textknoten ist. Andere Webbrowser brechen mit der Fehlermeldung ab, da wir versuchen ein Attribut eines nicht vorhandenen Elements zu ändern. - Versuche Dich soweit wie möglich unabhängig von HTML zu machen, da manche Browser Zeilenumbrüche als Knoten ansehen, und andere nicht. HTML Änderungen treten ausserdem dauernd auf, und sollten nicht Änderungen im JavaScript erfordern.
- Der Inhalt eines Elementes wird ausgelesen, indem man die Werte der Kindknoten
ausliest, nicht den Wert des Elementes!
document.getElementsByTagName('h2')[0].nodeValueist leer,document.getElementsByTagName('h2')[0].firstChild.nodeValueist, was wir wollen. - Wenn man auf
nodeNameoderattributeprüft sollte man sicher gehen, unabhängig von Groß- und Kleinschreibung zu bleiben. Abhängig vom Dokumententyp geben Browser diese Werte als groß oder klein zurück. - Vom DOM erstelltes HTML ist in den meisten Fällen nicht XML-konform; wenn man erstelltes HTML weiterverwenden will, sollte es vorher gesäubert werden.
- Versuche Schleifen zu vermeiden, wenn möglich benutze IDs.
- Beachte die richtige Schreibweise. Oft passiert es,
das man Code stundenlang ändert, nur weil man sich auf ein
getElementsByIdverlassen hat. - Mach Dich mit den Objekten und Attributen von JavaScript und HTML vertraut. Es bringt nichts auf Attribute zu hoffen, die von Anfang an nicht geplant waren.
- Nicht jeder schreibt Code wie man selbst. Es ist zum Beispiel besser zu prüfen ob das Klassenattribut einen bestimmten Text beinhaltet als sich darauf zu verlassen, dass nur eine CSS-Klasse verwendet wird.
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/
