treewalker: range / documentFragment / nodeIterator

Hallo Forum,

ist es möglich, auf die Nodes eines range-Objekts, bzw. eines documentFragments mit Hilfe des nodeIterators zuzugreifen?

Ich möchte die Textdaten einer Selektion manipulieren, was ja mit rekursivem Zugriff auf die DOM-Struktur ginge, mit dem nodeIterator aber viel schöner wäre.
toString() reicht nicht aus, da ich die Textbausteine hinterher wieder ihren Nodes zuordnen möchte.
Das Ganze ist für eine Firefox-Erweiterung gedacht, es ist also schon super, wenn es nur auf Mozilla funktioniert.

Mein bisheriger Ansatz:

  
function test() {  
 var selection = window.getSelection();  
 if (selection && !selection.isCollapsed) {  
  var text_parts = new Array;  
  var range = selection.getRangeAt(0);  
  var clone = range.cloneRange();  
  
  // content sollte nun ein Objekt vom Typ „documentFragment“ enthalten.  
  var content = clone.cloneContents();  
  
  // Der iterator nimmt content nicht.  
  var iterator = document.createNodeIterator(content, NodeFilter.SHOW_ELEMENT, null, false);  
  var node;  
  while((node = iterator.nextNode()) != null) {  
   text_parts.push(node.data);  
  }  
  return text_parts;  
 }  
}  

Geht so etwas überhaupt?
Ich hoffe, ihr könnt mir helfen.

Alles Gute & vielen Dank schon mal

treewalker

  1. Hallo,

    Ich verstehe ehrlich gesagt deinen Ansatz nicht.

    Wenn du *eine* Range abfragst mit getRangeAt(0), bekommst du immer nur eine Range, die *eine* Teilmarkierung wiedergibt. Das entspricht einem String. Das DocumentFragment, das cloneContents() zurückgibt, hat daher auch immer nur einen Kindknoten, das ist ein Textknoten. Warum willst du da mit einem NodeIterator arbeiten? Da gibt es doch nichts zu iterieren... ;)

    Suchst du vielleicht eher das hier? Willst du alle Markierungen im Dokument?

    <script type="application/javascript;version=1.7">  
    [code lang=javascript]function getSelectedTexts () {  
     var selection = window.getSelection();  
     if (!selection || selection.isCollapsed) return;  
     var text_parts = [];  
     for (let i = 0, l = selection.rangeCount; i < l; i++) {  
      text_parts.push(selection.getRangeAt(i).cloneContents().childNodes[0].data);  
     }  
     return text_parts;  
    }
    

    </script>[/code]

    Wenn mit Strg mehrere Texte markieren werden, gibt das alle Strings zurück.

    Mathias

    1. Hallo,

      Hallo, danke für deine Antwort.

      Ich verstehe ehrlich gesagt deinen Ansatz nicht.

      Ich versuche es nochmal deutlich zu machen. ;-)

      Wenn du *eine* Range abfragst mit getRangeAt(0), bekommst du immer nur eine Range, die *eine* Teilmarkierung wiedergibt. Das entspricht einem String. Das DocumentFragment, das cloneContents() zurückgibt, hat daher auch immer nur einen Kindknoten, das ist ein Textknoten. Warum willst du da mit einem NodeIterator arbeiten? Da gibt es doch nichts zu iterieren... ;)

      Das ist so nicht richtig. Das DocumentFragment enthält eine komplette Baumstruktur.

      Suchst du vielleicht eher das hier? Willst du alle Markierungen im Dokument?

      <script type="application/javascript;version=1.7">

      [code lang=javascript]function getSelectedTexts () {
      var selection = window.getSelection();
      if (!selection || selection.isCollapsed) return;
      var text_parts = [];
      for (let i = 0, l = selection.rangeCount; i < l; i++) {
        text_parts.push(selection.getRangeAt(i).cloneContents().childNodes[0].data);
      }
      return text_parts;
      }

      
      > </script>[/code]  
      >   
      > Wenn mit Strg mehrere Texte markieren werden, gibt das alle Strings zurück.  
        
      Wenn du in einem Dokument etwa folgendes markierst:  
        
      ~~~html
        
      <p>Heute <b>ist</b></p><p>ein schöner Tag</p>  
      
      

      und in deinem Skript Zeile 8 durch diese beiden ersetzt:

      text_parts.push(selection.getRangeAt(i).cloneContents().childNodes[0].childNodes[0].data);  
      text_parts.push(selection.getRangeAt(i).cloneContents().childNodes[1].childNodes[0].data);  
      
      

      enthält text_parts nach der Ausführung die beiden Strings „Heute“ und „ein schöner Tag“.

      Mathias

      Auf http://developer.mozilla.org/en/DOM/range heißt es dazu: „The Range object represents a fragment of a document that can contain nodes and parts of text nodes in a given document.“.

      Es geht mir nicht darum, verschiedene ranges zu durchlaufen, sondern die nodes innerhalb einer range.
      Gibt dafür keine andere Lösung als die komplizierte Rekursion? Der nodeIterator wäre doch wie dafür geschaffen...

      Wenn jemand einen tollen Ansatz für eine Rekursion hat, wäre ich auch dankbar.

      Einen schönen Tag wünscht, weiter auf eine Lösung hoffend

      treewalker

      1. Hallo,

        Wenn du in einem Dokument etwa folgendes markierst:

        <p>Heute <b>ist</b></p><p>ein schöner Tag</p>

          
        
        > Es geht mir nicht darum, verschiedene ranges zu durchlaufen, sondern die nodes innerhalb einer range.  
          
        Ups, sorry, das habe ich tatsächlich nicht bedacht.  
          
        
        > Gibt dafür keine andere Lösung als die komplizierte Rekursion? Der nodeIterator wäre doch wie dafür geschaffen...  
          
        Das ist einfach ein dummer Gecko-Fehler, im Prinzip sollte der NodeIterator auch ein DocumentFragment als Parameter enthalten können.  
        Gilt dasselbe auch für TreeWalker? (Ich denke mal, du hast es ausprobiert, wenn du dich treewalker nennst. ;))  
        Alternativ könntest du versuchen, das DocumentFragment mit einem normalen Element zu wrappen. Wie verhält sich Gecko dann?  
          
        
        > Wenn jemand einen tollen Ansatz für eine Rekursion hat  
          
        Da gibt es ja eigentlich nur einen möglichen Algorithmus...  
          
        Mathias
        
        -- 
        [SELFHTML aktuell Weblog](http://aktuell.de.selfhtml.org/weblog/)
        
        1. Gilt dasselbe auch für TreeWalker?

          Sieht ganz gut aus:

           var tw = document.createTreeWalker(content, NodeFilter.SHOW_TEXT, null, true);  
           tw.firstChild();  
           do {  
            console.log(tw.currentNode);  
            text_parts.push(tw.currentNode.data);  
           } while (tw.nextSibling());
          
          1. Gilt dasselbe auch für TreeWalker?

            Sieht ganz gut aus:

            var tw = document.createTreeWalker(content, NodeFilter.SHOW_TEXT, null, true);

            tw.firstChild();
            do {
              console.log(tw.currentNode);
              text_parts.push(tw.currentNode.data);
            } while (tw.nextSibling());

              
            Danke sehr!
            
        2. Das ist einfach ein dummer Gecko-Fehler, im Prinzip sollte der NodeIterator auch ein DocumentFragment als Parameter enthalten können.
          Gilt dasselbe auch für TreeWalker? (Ich denke mal, du hast es ausprobiert, wenn du dich treewalker nennst. ;))

          Mathias

          Super, das war ein toller Tip!
          Es scheint tatsächlich ein Fehler des Browsers zu sein, mit treeWalker funktioniert es (ich hatte es nicht ausprobiert... ;-) )!

          So sieht es dann aus:

            
          function walking_on_sunshine() {  
           var selection = window.getSelection();  
           if (!selection || selection.isCollapsed) return;  
           var text_parts = [];  
           var content = selection.getRangeAt(0).cloneContents();  
           var walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT, null, false);  
           var node;  
           while((node = walker.nextNode()) != null) {  
            text_parts.push(node.data);  
           }  
           return text_parts;  
          }  
          
          

          Vielen Dank für die fantastische Hilfe, Mathias!

          Allen einen wunderschönen Tag

          treewalker

        3. im Prinzip sollte der NodeIterator auch ein DocumentFragment als Parameter enthalten können.

          Vielleicht sollte ich noch nachtragen, dass createNodeIterator zwar existiert, ich aber in jedem meiner Tests einen Not-implemented-Exception bekommen habe. Auch ohne DocumentFragments.

          Ehrlich gesagt ralle ich das nicht. Wieso gibt es die Methode im Gecko, wenn jeder Aufruf »not implemented« ergibt?? Und wieso funktioniren TreeWalker problemlos?

          Jedenfalls werden künftige Geckoe NodeIterator hoffentlich implementieren:
          https://bugzilla.mozilla.org/show_bug.cgi?id=132824
          Wird glaube ich in FF 3.1 drin sein.