2012-07-17 11 views
7

Quiero reemplazar una cadena en particular en (el texto de) todos los elementos descendientes de un elemento dado.Reemplazar toda la aparición de una cadena en un elemento

innerHTML no se puede utilizar, ya que esta secuencia puede aparecer en los atributos. He intentado usar XPath, pero parece que la interfaz es esencialmente de solo lectura. Como esto está limitado a un elemento, las funciones como document.getElementsByTagName tampoco se pueden usar.

¿Podría alguien sugerir alguna forma de hacerlo? Cualquier jQuery o método DOM puro es aceptable.

Editar:

Algunas de las respuestas están sugiriendo el problema que estaba tratando de evitar: modificar el texto directamente sobre un elemento hará que todos los nodos secundarios no son de texto a ser removidos.

Por lo tanto, el problema se reduce esencialmente a cómo seleccionar eficientemente todos los nodos de texto en un árbol. En XPath, puede hacerlo fácilmente como //text(), pero la interfaz XPath actual no le permite cambiar estos nodos de texto, parece.

Una forma de hacerlo es por recursividad como se muestra en el answer por Bergi. Otro way es usar el selector find('*') de jQuery, pero esto es un poco más caro. Todavía estoy esperando para ver si hay mejores soluciones.

+0

[selectores jQuery] (http://api.jquery.com/category/selectors/) le permite recorrer y modificar el dom, acceder a los descendientes y los hermanos de un elemento. Y mucho más. –

+0

@ADC, desafortunadamente no hay nada como el selector text() de XPath .... –

Respuesta

4

sólo tiene que utilizar un simple DOM-iterador selfmade, que camina de forma recursiva sobre todos los nodos:

(function iterate_node(node) { 
    if (node.nodeType === 3) { // Node.TEXT_NODE 
     var text = node.data.replace(/any regular expression/g, "any replacement"); 
     if (text != node.data) // there's a Safari bug 
      node.data = text; 
    } else if (node.nodeType === 1) { // Node.ELEMENT_NODE 
     for (var i = 0; i < node.childNodes.length; i++) { 
      iterate_node(node.childNodes[i]); // run recursive on DOM 
     } 
    } 
})(content); // any dom node 
+1

+1 para una solución que no sea jQuery. Hace un cambio! – Matt

0

Lo sentimos, acabo yo mismo:

$('#id').find('*').each(function(){ 
    $.each(this.childNodes, function() { 
     if (this.nodeType === 3) { 
      this.data = this.data.toUpperCase(); 
     } 
    }) 
}) 

que utilizan toUpperCase() aquí para hacer el resultado más obvio, pero cualquier operación de cuerdas sería válida allí.

+1

Supongo que el objeto 'Text' no existe en IE8 ni en versiones anteriores. Mejor uso 'this.nodeType === 3'. – duri

+0

Esto * romperá * donde 'Texto' no es una referencia a un objeto, como en MSHTML <8 (IE <8). Lo mismo para la propiedad 'data'. También se romperá donde 'instanceof' no sea compatible, aunque esas implementaciones son raras hoy en día, y jQuery no es probable que las soporte en primer lugar. Debe agregar pruebas de características y seguir los consejos de duri. – PointedEars

0

Usando jQuery:

$('#parent').children().each(function() { 
    var that = $(this); 

    that.text(that.text().replace('test', 'foo')); 
}); 

Si prefiere buscar a través de todos los niños en lugar de sólo los niños inmediatos, utilice .find() lugar.

http://jsfiddle.net/ExwDx/

Editar: Documentación para children, each, text y find.

+0

Esto es en lo que estaba trabajando antes de publicar esta pregunta. El problema es que si el elemento tiene hijos no Text, se eliminarán como resultado de la llamada a 'text()'. Obviamente esto no es aceptable. –

+0

incluirá nodos de texto, y no eliminará los elementos secundarios que no sean de texto. – jbabey

+0

@jbabey: tienes razón, eliminé mi comentario. ¿Sabes qué versión '.children()' también devuelve TextNodes? – jAndy

1

Una solución podría ser navegar a través de todos los nodos disponibles (TextNodes incluidos) y aplicar un patrón de expresiones regulares en los resultados. Para capturar TextNodes también, necesita invocar jQuerys .contents(). Por ejemplo:

var search = "foo", 
    replaceWith = 'bar', 
    pattern = new RegExp(search, 'g'); 


function searchReplace(root) { 
    $(root).contents().each(function _repl(_, node) { 
     if(node.nodeType === 3) 
      node.nodeValue = node.nodeValue.replace(pattern, replaceWith); 
     else searchReplace(node); 
    }); 
} 

$('#apply').on('click', function() { 
    searchReplace(document.getElementById('rootNode')); 
}); 

Ejemplo: http://jsfiddle.net/h8Rxu/3/

Referencia: .contents()

+0

Creo que la asignación a textContent eliminará todos los nodos no textuales de manera similar a la llamada a 'text()' de jQuery, tal como lo sugiere jbabey. –

+0

@ billc.cn: correcto, actualicé el código y el ejemplo, la recursión debería hacer el trabajo allí. – jAndy

+0

Ese código es demasiado complicado y jQuery mezclado con métodos DOM nativos. ¿Por qué, por ejemplo, aplica String.trim en nodeValues ​​antes de replace()? – Bergi

Cuestiones relacionadas