2010-01-27 15 views
7

tengo el siguiente código que funciona en FF/ChromeIE sólo Error de JavaScript con 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]); 
       } 
     } 
    } 
} 

Básicamente lo que hace es si encuentra [test = 25] en la página se convierte en un enlace que apunta a example.com/25

en IE me sale el siguiente error: JScript objeto esperado de primera línea:

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

Este error se produce tanto en IE7 e IE8.

Cualquier ayuda sería apreciada.

Gracias.

Respuesta

12

No es legal para llamar Array.prototype.slice en un objeto NodeList devuelto por el childNodes propiedad (o varios otros métodos DOM).

Normalmente no sería legal para llamar Thing.prototype.method en otra cosa que una instancia de Thing, sin embargo, los navegadores han permitido tradicionalmente - y el estándar ECMAScript Tercera Edición requiere - un caso especial por muchas Array.prototype métodos para que puedan ser llamados cualquier objeto JavaScript nativo que sea suficientemente parecido a Array. Esto significa, en particular, que se pueden usar en el objeto arguments, que se parece a un Array pero en realidad no lo es.

Sin embargo, NodeList y los otros objetos de colección en el DOM no están definidos como objetos JavaScript nativos; se les permite ser 'objetos host', que se implementan completamente por el navegador y no por el idioma. Todas las apuestas están apagadas para los objetos de acogida ...

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

Así Array.prototype.slice puede no funcionar para NodeList, y en IE antes de la versión 8, de hecho, no lo hará.

Si desea hacer una copia simple-Array de un NodeList, tendrá que hacerlo de la manera larga, pero seguro:

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

Por cierto, puede hacer que linkifier un poco más simple mediante el uso de textnode.splitText, y sería muy cauteloso con el uso de las propiedades globales RegExp, ya que si se produce un trabajo de expresiones regulares inesperado en una de las llamadas intermedias, se perderán. Ver el objeto de coincidencia suele ser mejor. Ver this question para otro ataque en básicamente el mismo problema.

+0

+1. Y puramente como una nota de coincidencia: en algunos puntos de referencia de Firefox JavaScript que hice recientemente, encontré que construir una matriz usando un literal vacío '[]' y 'push' es, irónicamente, más rápido que el método que se muestra aquí. –

+0

¡Interesante! 'push' no puede recrear una lista dispersa (con elementos faltantes) como con la anterior, pero para una lista de nodos no la va a necesitar. – bobince

+0

Gracias por la información y especialmente ese enlace a esa otra pregunta. Era exactamente lo que estaba buscando. – Rob

2

Trate de usar esto en su lugar:

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

Hay un poco de funkyness con IE y prototipos/constructores. No puedo probar ahora, en un mac.

Más información aquí: Difference between Array.slice and Array().slice

3

Creo que esto se debe a que getElementsByTagname devuelve una lista de nodos - no una matriz (aunque algunas cosas como el trabajo del operador [] en el que al igual que ellos trabajan en matrices, que no son los mismos)

Tal vez se puede resolver de una manera menos complicada:

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

Usar 'getElementsByTagName (" * ")' es una buena idea, pero recuerde que esta es una lista de nodos activa: a medida que agrega enlaces a la página, recibirá elementos adicionales. Esto significará que los últimos ítems de los elementos agregados no se verificarán (debido a la optimización de recordatorio 'numEls'), y si el texto de reemplazo puede contener el texto buscado, hará un reemplazo recursivo loco. Es mejor tomar una copia de la lista de nodos como se intentó originalmente, o simplemente iterar la NodeList en reversa. – bobince

+0

@bobince: de hecho, tienes razón :) gracias por señalar eso. Pero en ese caso, creo que trataría de almacenar todos los nodos de texto que coinciden en una matriz y luego fuera del ciclo, vuelvo a recorrer la matriz para hacer la sustitución, creo que debería ser posible sin necesidad de posiciones de nodo explícitas. Tendré que pensar en tu sugerencia para andar la lista al revés ... He estado despierto demasiado tiempo y necesito dormir. Pero lo pensaré, ¡gracias! –