2010-01-27 15 views
7

ho il seguente codice che funziona in FF/ChromeIE solo javascript errore 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]); 
       } 
     } 
    } 
} 

Fondamentalmente ciò che fa è se trova [test = 25] nella pagina che lo converte in un link che indica example.com/25

in IE ottengo il seguente errore: JScript previsto oggetto sulla prima linea:

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

Questo errore si verifica sia in IE7 e IE8.

Qualsiasi aiuto sarebbe apprezzato.

Grazie.

risposta

12

Non è legale chiamare Array.prototype.slice su un oggetto NodeList come restituito dalla childNodes proprietà (o vari altri metodi DOM).

Normalmente non sarebbe legale per chiamare Thing.prototype.method su qualsiasi cosa, ma un'istanza di Thing, tuttavia i browser hanno tradizionalmente permesso - e lo standard ECMAScript Terza Edizione richiede - un caso speciale per molti Array.prototype metodi in modo che possano essere chiamati qualsiasi oggetto JavaScript nativo che sia sufficientemente simile a Array. Ciò significa, in particolare, che possono essere utilizzati sull'oggetto arguments, che assomiglia a un Array ma in realtà non lo è.

Tuttavia, NodeList e gli altri oggetti di raccolta nel DOM non sono definiti come oggetti JavaScript nativi; possono essere 'oggetti host', che sono implementati completamente dal browser e non dalla lingua. Tutte le scommesse sono spenti per gli oggetti di accoglienza ...

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

Quindi Array.prototype.slice non può funzionare per NodeList, e in IE prima della versione 8, in effetti, non lo farà.

Se si vuole fare una copia plain-Array di un NodeList, dovrete farlo nel lungo ma sicuro modo:

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

Per inciso, si può fare che linkifier un po 'più semplice utilizzando textnode.splitText, e sarei molto cauto nell'usare le proprietà globali RegExp, come se si verificasse un qualsiasi lavoro di espressioni regolari inatteso in una delle chiamate intermedie che andranno perse. Guardare l'oggetto della partita di solito è meglio. Vedi this question per un altro attacco praticamente allo stesso problema.

+0

+1. E puramente come una nota a margine: in alcuni benchmark JavaScript di Firefox che ho fatto di recente, ho scoperto che costruire un array usando un letterale vuoto '[]' e 'push' è, ironia della sorte, più veloce del metodo mostrato qui. –

+0

Interessante! 'push' non può ricreare una lista sparsa (con elementi mancanti) come sopra, ma per una NodeList non ne avrai bisogno. – bobince

+0

Grazie per l'informazione e soprattutto per il link a quell'altra domanda. Era esattamente quello che stavo cercando. – Rob

2

Provare a utilizzare questo, invece:

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

C'è un po 'funkyness con IE e di prototipazione/costruttori. Non riesco a testare adesso, su un mac.

Maggiori informazioni qui: Difference between Array.slice and Array().slice

3

Credo che questo sia perché getElementsByTagname restituisce un NodeList - non un array (anche se alcune cose come il [] lavoro dell'operatore che, come lavorano su array, essi non sono uguali)

Forse può essere risolto in un modo meno complicato:

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'uso di 'getElementsByTagName (" * ")' è una buona idea, ma ricorda che questo è un NodeList attivo: quando aggiungi dei collegamenti alla pagina, riceverà degli oggetti extra. Ciò significa che gli ultimi elementi con numero di elementi aggiunti non verranno controllati (a causa dell'ottimizzazione della memorizzazione di 'numEls'), e se il testo di sostituzione può contenere il testo cercato eseguirà una sostituzione ricorsiva pazzesca. Meglio prendere una copia del nodelist come è stato tentato in origine, o semplicemente per iterare la NodeList al contrario. – bobince

+0

@bobince: in effetti, hai ragione :) grazie per averlo indicato. Ma in quel caso, penso che proverei a memorizzare tutti i textnode che corrispondono in un array e poi fuori dal ciclo, ricomincio di nuovo l'array per fare la sostituzione - penso che dovrebbe essere possibile senza bisogno di posizioni esplicite del nodo. Dovrò pensare al tuo suggerimento di percorrere la lista in senso inverso ... Sono stato su troppo a lungo e ho bisogno di dormire. Ma ci penserò - grazie! –

Problemi correlati