molily: Rekursive Auflistung aller Kindknoten

Hallo, zusammen.

Um mir das DOM zu verdeutlichen, habe ich versucht, ein Skript zu schreiben, welches mir alle Kindknoten in einem Dokument auflistet. So habe ich mir mal wieder eine sinnlose Aufgabe gestellt, an der ich verzweifle, wollte ich doch eigentlich nur mein Skript verbessern, welches dem Benutzer gestattet, W3C Core Styles dynamisch via DOM anzuwenden.
Ich habe es zwar geschafft, eine mehr oder weniger praktikable Lösung zu finden - womit ich gar nicht gerechnet hatte -, dennoch gibt es verschiedenartige Probleme.

Nun einmal zum Quellcode.

[Kopf gekürzt ;)]
--8<--
<html>
<head>
<script type="text/javascript">
<!--
var max_objekte=100; // Anzahl der maximal zu auszugebenden Knoten
var objektindex=0; // Objektzähler
var ebene=1; // Beinhaltet während der Laufzeit die momentane Verschachtelungsebene
var ausgabe="";

function liste_unterknoten (knoten) {
 objektindex++;
 ausgabe+="|"; for (k=1; k<ebene; k++) ausgabe+="   |";
 ausgabe+=("- Typ: ["+knoten.nodeType+"] Name: [<b>"+knoten.nodeName+"</b>] Wert: [<b>"+knoten.nodeValue+"</b>] Unterknoten: ["+knoten.childNodes.length+"]\n");
 if (knoten.hasChildNodes()) {
  ebene++;
  for (i=0; i<knoten.childNodes.length; i++) {
   knotenneu=knoten.childNodes[i];
   if (objektindex<max_objekte) liste_unterknoten(knotenneu); // Rekursion
  }
  ebene--;
 }
}
//-->
</script>
</head>

<body>

<!-- Beispieldokument (dieser Kommentar ist auch ein Unterknoten) -->
<h1>&Uuml;berschrift #1</h1>
<h2>&Uuml;berschrift #2</h2>
<p>eins <a href="corestyles-dom.html">zwei</a> drei <b>vier</b> fünf <i>sechs</i> sieben <u>acht</u> neun</p>

<script type="text/javascript">
<!--
liste_unterknoten(document.getElementsByTagName("body")[0]); // Rufe Funktion auf, beginne beim body-Element
document.write("<pre>"+ausgabe+"</pre>"); // Schreibe Ausgabe
//-->
</script>

</body>
</html>
-->8--

Zugegebenermaßen wirkt der Quelltext eher funktionell als elegant, da ich mich doch erst seit kurzem in das Thema einarbeite. Wieso gibt die Funktion die Daten nicht direkt aus, sondern reiht sie in der Variable ausgabe auf? Weil die Browserinstanzen das Zeitliche segnen, falls man ausgabe+= durch document.write ersetzt (daher noch die übriggebliebenen Klammern als Relikte vorheriger Versionen).

Ausgabe ist wie folgt (IE):
|- Typ: [1] Name: [BODY] Wert: [null] Unterknoten: [5]
|   |- Typ: [8] Name: [#comment] Wert: [ Beispieldokument (dieser Kommentar ist auch ein Unterknoten) ] Unterknoten: [0]
|   |- Typ: [1] Name: [H1] Wert: [null] Unterknoten: [1]
|   |   |- Typ: [3] Name: [#text] Wert: [Überschrift #1] Unterknoten: [0]
|   |- Typ: [1] Name: [H2] Wert: [null] Unterknoten: [1]
|   |   |- Typ: [3] Name: [#text] Wert: [Überschrift #2] Unterknoten: [0]
|   |- Typ: [1] Name: [H2] Wert: [null] Unterknoten: [1]
|   |   |- Typ: [3] Name: [#text] Wert: [Überschrift #2] Unterknoten: [0]
... ...

Das Script ist lauffähig (getestet auf IE 6.0 und K-Meleon 0.6), ich hoffe, mein Ergebnis ist reproduzierbar. Opera reagiert scheinbar gar nicht.
Das Problem nun liegt darin, dass die Funktion nach ein paar Durchläufen bei zwei Objekten stehenbleibt (s.o.) und diese immer wieder endlos ausgibt. Dies hat mir dutzende Programmabstürze gebracht, bis ich die Limitierung eingebaut habe. Komischerweise crashten die Benutzeragenten auch mit auskommentiertem Rekursionsbefehl - alles in allem erfreulich chaotisch.
Änderungen am Algorithmus haben mich nicht weitergebracht. Ich sehe den Wald vor lauter Bäumen nicht.
Interessanterweise hängt die Rekursionsschleife bei K-Meleon an einer anderen Stelle.

Ausgabe K-Meleon:
|- Typ: [1] Name: [BODY] Wert: [] Unterknoten: [10]
|   |- Typ: [3] Name: [#text] Wert: [

] Unterknoten: [0]
|   |- Typ: [8] Name: [#comment] Wert: [ Beispieldokument (dieser Kommentar ist auch ein Unterknoten) ] Unterknoten: [0]
|   |- Typ: [3] Name: [#text] Wert: [
] Unterknoten: [0]
|   |- Typ: [1] Name: [H1] Wert: [] Unterknoten: [1]
|   |   |- Typ: [3] Name: [#text] Wert: [Überschrift #1] Unterknoten: [0]
|   |- Typ: [3] Name: [#text] Wert: [
] Unterknoten: [0]
|   |- Typ: [1] Name: [H1] Wert: [] Unterknoten: [1]
|   |   |- Typ: [3] Name: [#text] Wert: [Überschrift #1] Unterknoten: [0]
|   |- Typ: [3] Name: [#text] Wert: [
] Unterknoten: [0]
... ...

Die Laus bzw. die Eigentümlichkeit bezüglich der Identifikation der (\r)\n's (<- Plural, nicht Genitiv ;) als Unterknoten ist bei geckoiden Browsern bekannt. Dürfte hier aber nicht der Auslöser des Problems sein. Kann mir jemand helfen...? Vielleicht existiert dort draußen schon eine Lösung. (Selber machen ist natürlich herausfordernder, schließlich ist der Weg das Ziel.)

Grüße,
Ma-'Verwunderung ist der erste Weg zur Weisheit'-thias

  1. Hallo molily!

    Ich habe mir das Skript auch drei Mal ansehen müssen bevor ich den Fehler gefunden hatte. Vorsicht bei Java & JavaScript mit nicht explizit als Variablen deklarierten Variablen bein Rekusion.

    Was war nu falsch????

    for (<b>var</b> i=0; i<knoten.childNodes.length; i++) {
       knotenneu=knoten.childNodes[i];
       if (objektindex<max_objekte) liste_unterknoten(knotenneu); // Rekursion
      }

    Es muß ein "var" vor die Deklaration dem temp Variable i. Wenn man das nicht macht, dann der der Interpreter ärger wären der Rekusion die Variable ein-eindeutig einer Ebene zuzuordnen [die Variable i zählt 0 2 0 2 0 2 0 2 am Anfang mal ne 1, aber das ist eben die Startsituation -> daraus kann man nicht auf das Rekursionsverhalten schließen!!!]

    Zu Deiner zweiten Frage: Wie so nicht document.write()???

    Kannst Du schon, aber ich glaube, dann muß das Skript nach dem Body kommen. Ein Aufruf von document.write zerstört das aktuelle Dokument und macht es wieder zum Schreiben bereit. Wenn also erst das Skript kommt, dann lese ich den ersten Knoten, denn wird das Dokument weggeschmissen ==> dann gibts nur noch den Knoten, den DU eben geschrieben hast, will heißen: ENDLOSSCHLIEFE...

    Ich hoffe mal, daß ich Dir geholfen habe. Bei dem letzten Punkt bin ich mir nicht 100%ig sicher, aber sollte stimmen. Dein Skipt hat bei mir nach der Modif. jendenfalls funktioniert.

    GVT

    1. Hallo, Shir Khan!

      Vielen Dank für Deine Hinweise, jetzt klappt es und ich habe auch verstanden, worin der Fehler lag.

      Zu Deiner zweiten Frage: Wie so nicht document.write()???

      Kannst Du schon, aber ich glaube, dann muß das Skript nach dem Body kommen. Ein Aufruf von document.write zerstört das aktuelle Dokument und macht es wieder zum Schreiben bereit.

      Jein, das ist hier nicht entscheidend. Denn das Script welches die Funktion aufruft, die die Ausgabe vornimmt, kommt nach dem Body (bzw. am Ende des Bodys, da sich nach </body> kein Script mehr aufhalten darf).
      Da ist es eigentlich unwichtig, wo die Funktion definiert ist, entscheidend ist, wo im Dokument die Ausgabe erfolgt.

      Und dass das Dokument geleert wird, glaube ich auch nicht. Denn folgendes funktioniert:
      <html>
      <head>
      <script type="text/javascript">
      function ausgabe (s) {
       document.write(s+"<br>");
      }
      </script>
      </head>
      <body>
      eins<br>
      <script type="text/javascript">
      ausgabe("zwei")
      </script>
      drei<br>
      <script type="text/javascript">
      ausgabe("vier")
      </script>
      </html>

      Ausgabe ist wie gewünscht:
      eins
      zwei
      drei
      vier

      Ich kann einfach nicht vorhersagen, wann der Dokumentinhalt geleert wird und wann nicht... Wenn man die bspw. Funktion mit setTimeout zeitversetzt aufruft, dann findet eine Löschung statt.

      Wenn also erst das Skript kommt, dann lese ich den ersten Knoten, denn wird das Dokument weggeschmissen ==> dann gibts nur noch den Knoten, den DU eben geschrieben hast, will heißen: ENDLOSSCHLIEFE...

      Ja, das ist wohl die Fehlerursache. Also nicht dass nur noch ein Knoten da ist durch die Löschung des Inhalts, sondern dass immer neue Knoten am Ende des Dokuments entstehen.
      Jede Funktionsinstanz erzeugt mehrere Knoten, welche dann von den folgenden Instanzen untersucht werden - diese geben aber wieder dutzende Knoten aus...
      Kaskadenartig steigt so die Anzahl der noch zu bearbeitenden Knoten, daher die Endlosschleife.
      Ich hab's noch einmal mit document.write getestet, aber die Ausgabe limitiert, sodass keine Endlosschleife entsteht. Tatsächlich durchläuft das Skript alle Knoten des Dokumentes und dann die Knoten, die es selbst ausgegeben bzw. eingefügt hat.

      Grüße,
      Mathias