2010-01-27 8 views
7

Ich habe den folgenden Code, dienur IE Javascript-Fehler mit getElementsByTagName

var stack = [Array.prototype.slice.call(document.getElementsByTagName("body")[0].childNodes)], nodes, node, parent, text, offset; 
while (stack.length) { 
    nodes = stack.pop(); 
    for (var i=0, n=nodes.length; i<n; ++i) { 
     node = nodes[i]; 
     switch (node.nodeType) { 
      case Node.ELEMENT_NODE: 
       if (node.nodeName.toUpperCase() !== "SCRIPT") { 
        stack.push(Array.prototype.slice.call(node.childNodes)); 
       } 
       break; 
      case Node.TEXT_NODE: 
       text = node.nodeValue; 
       offset = text.indexOf("[test="); 
       if (offset >= 0 && text.substr(offset).match(/^(\[test=(\d+)\])/)) { 
        parent = node.parentNode; 
        var before = document.createTextNode(text.substr(0, offset)); 
         link = document.createElement("a"), 
         after = document.createTextNode(text.substr(offset + RegExp.$1.length)); 
        link.appendChild(document.createTextNode(text.substr(offset, RegExp.$1.length))); 
        link.setAttribute("href", "http://example.com/" + RegExp.$2); 
        parent.insertBefore(after, node); 
        parent.insertBefore(link, after); 
        parent.insertBefore(before, link); 
        parent.removeChild(node); 
        stack.push([after]); 
       } 
     } 
    } 
} 

Grundsätzlich in FF/Chrome funktioniert, was es tut, wenn er feststellt, [Test = 25] auf der Seite wandelt sie es auf einen Link, example.com/25 Punkte

in IE ich die folgende Fehlermeldung erhalten: auf erste Zeile Erwartete JScript Objekt:

var stack = [Array.prototype.slice.call(document.getElementsByTagName("body")[0].childNodes)], nodes, node, parent, text, offset; 

Dieser Fehler sowohl in IE7 und IE8 auftritt.

Jede Hilfe wäre willkommen.

Danke.

Antwort

12

Es ist nicht legal Array.prototype.slice auf ein NodeList Objekt aufzurufen, wie durch die childNodes Eigenschaft (oder verschiedene andere DOM-Methoden) zurückgegeben.

Normalerweise wäre es nicht legal sein Thing.prototype.method sondern eine Instanz von Thing auf irgendetwas zu nennen, haben jedoch Browsern traditionell erlaubt - und das ECMAScript Third Edition Standard verlangt - ein Sonderfall für viele Array.prototype Methoden, so dass sie aufgerufen werden können jedes native-JavaScript-Objekt, das ausreichend wie ein Array ist. Dies bedeutet insbesondere, dass sie auf dem Objekt arguments verwendet werden können, das wie ein Array aussieht, aber tatsächlich nicht ist.

Allerdings sind NodeList und die anderen Auflistungsobjekte im DOM nicht als systemeigene JavaScript-Objekte definiert. Sie dürfen "Host-Objekte" sein, die vollständig vom Browser und nicht von der Sprache implementiert werden. Alle Wetten sind weg für Host-Objekte ...

Whether the slice function can be applied successfully to a host object is implementation-dependent.

So Array.prototype.slice nicht für NodeList arbeiten kann, und in IE vor der Version 8, in der Tat, es wird nicht.

Wenn Sie ein plain-Array Kopie eines NodeList machen wollen, werden Sie sie für die lange, aber sichere Art und Weise zu tun haben:

Array.fromSequence= function(seq) { 
    var arr= new Array(seq.length); 
    for (var i= seq.length; i-->0;) 
     if (i in seq) 
      arr[i]= seq[i]; 
    return arr; 
}; 

var stack = [Array.fromSequence(document.body.childNodes)]; 

Im Übrigen können Sie das machen linkifier etwas einfacher durch Verwendung textnode.splitText, und ich würde sehr vorsichtig sein bei der Verwendung der globalen RegExp Eigenschaften, als ob unerwartete Regex-Arbeit in einem der dazwischenliegenden Anrufe auftreten, die sie verloren gehen. Das Spielobjekt zu betrachten ist normalerweise besser. Siehe this question für einen weiteren Angriff im Grunde das gleiche Problem.

+0

+1. Und rein als zufällige Randnotiz: In einigen Firefox-JavaScript-Benchmarks, die ich kürzlich gemacht habe, fand ich, dass das Konstruieren eines Arrays mit einem leeren Literal '[]' und 'push' ironischerweise schneller ist als die hier gezeigte Methode. –

+0

Interessant! 'push' kann keine Sparse-Liste (mit fehlenden Elementen) wie oben erstellen, aber für eine NodeList wird sie nicht benötigt. – bobince

+0

Danke für die Informationen und vor allem, dass Link zu dieser anderen Frage. Es war genau das, wonach ich suchte. – Rob

2

Versuchen Sie stattdessen mit:

var stack = [Array().slice.call(document.getElementsByTagName("body")[0].childNodes)] 

Es gibt einige mit IE und Funkyness Prototyp/Konstrukteure. Ich kann jetzt nicht auf einem Mac testen.

Mehr Infos hier: Difference between Array.slice and Array().slice

3

Ich denke, das liegt daran, dass getElementsByTagname eine Nodelist gibt - kein Array (auch wenn einige Dinge wie die Operator [] Arbeit, dass, wie sie auf Arrays arbeiten, sind sie nicht die gleichen)

Vielleicht kann es gelöst werden in einer weniger komplizierten Weise:

var els = document.body.getElementsByTagName("*"); 
for (var i=0, numEls=els.length, el; i<numEls; i++){ 
    el = els.item(i); 
    el.normalize();   
    for (var j=0, chs = el.childNodes, numChs=chs.length, ch; j<numChs; j++){ 
     ch = chs.item(j); 
     if (ch.nodeType==Node.TEXT_NODE){ 
      //you code for replacing text with link goes here 
      //ps i suggest using ch.data instead of ch.nodeValue 
     } 
    } 
} 
+0

Verwenden von 'getElementsByTagName (" * ")' ist eine gute Idee, aber denken Sie daran, dies ist eine Live-NodeList: Wenn Sie Links zu der Seite hinzufügen, wird es zusätzliche Elemente erhalten. Dies bedeutet, dass die letzten Elemente der Anzahl der hinzugefügten Elemente nicht überprüft werden (aufgrund der "numEls" -Erinnerungsoptimierung), und wenn der Ersatztext den gesuchten Text enthalten kann, wird er einen irreführenden rekursiven Ersatz vornehmen. Besser ist es, eine Kopie der Nodeliste zu machen, wie es ursprünglich versucht wurde, oder einfach die NodeList in umgekehrter Reihenfolge zu iterieren. – bobince

+0

@bobince: In der Tat, du hast Recht :) danke für das darauf hin. Aber in diesem Fall würde ich versuchen, alle textnodes zu speichern, die in einem Array übereinstimmen und dann außerhalb der Schleife das Array erneut durchlaufen, um die Ersetzung durchzuführen - ich denke, das sollte möglich sein, ohne explizite Knotenpositionen zu benötigen. Ich muss über deinen Vorschlag nachdenken, die Liste in umgekehrter Reihenfolge zu gehen ... Ich war zu lange auf und brauche Schlaf. Aber ich werde darüber nachdenken - danke! –

Verwandte Themen