outerHTML - und wie man mit JavaScript neue Attribute für eine Klasse definiert

Was macht man, wenn man in einer HTML-File ein Body-Tag mit Attributen hat und alles inklusive dieser Attribute auf einen Rutsch und möglichst einfach abgreifen möchte, zum Beispiel um eine Kopie auf den Server per AJAX abzulegen.

Nahe liegend ist zunächst der Gedanke: Da muss es doch etwas geben wie innerHTML. Und es gibt ja auch beim Internet Explorer etwas, das dort schlicht outerHTML heißt. Man kann nun darüber nachdenken, ob man nicht auch mit etwas rekursiven DOM-Gebrösele den Baum abläuft (hab ich vor längerem mal weiter unter beschrieben). Oder man googelt und schaut nach, ob nicht andere ein ähnliches Problem gelöst haben. Wie immer findet man irgendwann ein paar Lösungen, die mehr oder weniger elegant sind. Die folgende Lösung ist ein wenig abgekupfert und ein wenig ergänzt, damit sie auch schick ist.

<html>
<head>
<script>
var d = Element.prototype;
d.__defineGetter__("outerHTML", function() {
 var item=document.createElement("div");
 item.setAttribute("style","display:none");
 document.body.appendChild(item);
 var temp=this.cloneNode(true);
 item.appendChild(temp);
 var out=item.innerHTML;
 document.body.removeChild(item);
 return out;
 }
);
</script>
</head>
<body onload="alert(document.getElementById('a').outerHTML)">
<div id="a">
<p id="b">cde</p><p>xyz</p>
</div>
</body>

Zunächst ein Blick auf die letzten Zeilen. Dort ist ein <div>-Tag mit der id="a". Dieses soll per alert()-Funktion ausgegeben werden. Die Ausgabe wird im <body>-Tag mit dem onload-Event ausgelöst. Und hier steht, wie bei dem bekannten innerHTML nun outerHTML. Also als Eigenschaft (Property) einer DOM-Klasse und nicht als Methode, wie zu sehen ist. Das gefällt :)

Wie kann man nun aus einer Methode/Funktion ein Objekt-Attribut machen? Man muss eine prototype-getter-Implementation vornehmen. Also zunächst die Stelle in der Objekt-Hierarchie des DOMs suchen, wo das Attribut am besten hineinpasst: Document? Node? Element? Man achte bitte auf die Schreibweise mit großem Anfangsbuchstaben: Das sind die Klassen für die DOM-Objekte! Ich habs probiert und finde, dass Element eine gute Stelle für die Implementierung ist. Damit sind auch alle Navigationen (getElementById ... , firstChild, parentNode ...) abgedeckt, so dass man outerHTML wirklich wie innerHTML verwenden kann.

Also nachdem Element geeignet schient, die Definition vereinbart. Das geht mit prototype, einem Objekt, das für eine Konstruktorfunktion Eigenschaften und Methoden definiert, die allen mit dieser Konstruktorfunktion erzeugten Objekten gemeinsam sind. Anmerkung: Das läuft rekursiv und wirkt sich auch nachträglich aus.

Nun müssen wir noch einen Funktionsaufruf zum Attribut machen und das geschieht über die __defineGetter bzw. __defineSetter__-Methoden. Man vereinbart zunächst den Namen und beschreibt anschließend die Funktion. Und man kann in dieser Funktion auch mit dem this-Zeiger/Pointer arbeiten :), welcher später auf das Objekt zeigt, mit dem wir die Methode verwenden wollen.

So und wie erhalten wir nun ohne lange Übung die Attribute des Tags und aller darunter liegenden Tags? Wir legen ein neues unsichtbares <div>-Element an und machen einen Klon von dem Element, dessen outerHTML wir erhalten wollen. Danach weisen wir den Klon dem versteckten <div>-Element zu; holen uns den neuen Inhalt in eine Zwischenvariable, damit wir noch in Ruhe das nicht mehr benötigte <div>-Element wieder löschen können und geben das Ergebnis aus. Durch die cloneNode-Methode, wird die Lösung doch recht einfach.