2010-01-27 4 views
7

je le code suivant qui fonctionne en FF/ChromeIE uniquement javascript erreur avec 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]); 
       } 
     } 
    } 
} 

Fondamentalement, ce qu'il fait est si elle trouve [test = 25] dans la page, il la convertit en un lien qui souligne example.com/25

dans IE je reçois l'erreur suivante: JScript objet attendu sur la première ligne:

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

Cette erreur se produit dans les deux IE7 et IE8.

Toute aide serait appréciée.

Merci.

Répondre

12

Il est illégal d'appeler Array.prototype.slice sur un objet NodeList retourné par la propriété childNodes (ou d'autres méthodes DOM).

Normalement, il ne serait pas légal d'appeler Thing.prototype.method sur quoi que ce soit, mais une instance de Thing, mais les navigateurs ont toujours permis - et la norme ECMAScript Troisième édition exige - un cas particulier pour de nombreuses méthodes Array.prototype afin qu'ils puissent être appelés tout objet natif JavaScript qui est suffisamment semblable à Array. Cela signifie, notamment, qu'ils peuvent être utilisés sur l'objet arguments, qui ressemble à un Array mais ne l'est pas en réalité.

Cependant, NodeList et les autres objets de collection dans le DOM ne sont pas définis comme des objets JavaScript natifs; ils sont autorisés à être des 'objets hôtes', qui sont implémentés complètement par le navigateur et non par la langue. Tous les paris sont ouverts pour les objets d'accueil ...

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

Alors Array.prototype.slice ne peut travailler pour NodeList, et dans IE avant la version 8, en effet, il ne sera pas.

Si vous souhaitez effectuer une simple copie-Array d'un NodeList, vous devrez le faire le long chemin, mais en toute sécurité:

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)]; 

Soit dit en passant, vous pouvez faire ce linkifier un peu plus simple en utilisant textnode.splitText, et je serais très méfiant à propos de l'utilisation des propriétés globales RegExp, comme si un travail regex inattendu se produisait dans l'un des appels intermédiaires, ils seront perdus. Regarder l'objet de match est généralement meilleur. Voir this question pour une autre attaque à la base du même problème.

+0

+1. Et purement comme une note parallèle: Dans certains benchmarks Firefox JavaScript que j'ai fait récemment, j'ai trouvé que construire un tableau en utilisant un littéral vide '[]' et 'push' est, ironiquement, plus rapide que la méthode montrée ici. –

+0

Intéressant! 'push' ne peut pas recréer une liste clairsemée (avec des éléments manquants) comme ci-dessus, mais pour un NodeList, vous n'en aurez pas besoin. – bobince

+0

Merci pour l'information et surtout ce lien vers cette autre question. C'était exactement ce que je cherchais. – Rob

2

Essayez d'utiliser ceci:

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

Il y a quelques funkyness avec IE et prototypes/constructeurs. Je ne peux pas tester maintenant, sur un mac.

Plus d'infos ici: Difference between Array.slice and Array().slice

3

Je pense que c'est parce que getElementsByTagname retourne un nodelist - pas un tableau (bien que certaines choses comme le [] le travail de l'opérateur dans ce comme ils travaillent sur des tableaux, ils ne sont pas les mêmes)

Peut-être peut être résolu d'une manière moins compliquée:

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

L'utilisation de 'getElementsByTagName (" * ")' est une bonne idée, mais rappelez-vous qu'il s'agit d'une NodeList en direct: lorsque vous ajoutez des liens à la page, elle reçoit des éléments supplémentaires. Cela signifiera que les derniers éléments du nombre d'éléments ajoutés ne seront pas vérifiés (en raison de l'optimisation 'numEls'), et si le texte de remplacement peut contenir le texte recherché, il effectuera un remplacement récursif fou. Mieux vaut prendre une copie de la nodelist comme cela a été tenté à l'origine, ou simplement répéter la NodeList en sens inverse. – bobince

+0

@bobince: en effet, vous avez raison :) merci de le signaler. Mais dans ce cas, je pense que j'essaierais de stocker tous les textnodes qui correspondent dans un tableau puis en dehors de la boucle, reboucler le tableau pour faire le remplacement - je pense que cela devrait être possible sans avoir besoin de positions de nœuds explicites. Je vais devoir réfléchir à votre suggestion de marcher la liste à l'envers ... J'ai été debout trop longtemps et j'ai besoin de dormir. Mais je vais y réfléchir - merci! –

Questions connexes