¿Cómo puedo cambiar el texto de un elemento sin cambiar sus elementos secundarios?

120

Me gustaría actualizar el texto del elemento de forma dinámica:

<div>
   **text to change**
   <someChild>
       text that should not change
   </someChild>
   <someChild>
       text that should not change
   </someChild>
</div>

Soy nuevo en jQuery, por lo que esta tarea parece ser bastante desafiante para mí. ¿Alguien podría indicarme una función / selector para usar?

Si es posible, me gustaría hacerlo sin agregar un nuevo contenedor para el texto que necesito cambiar.

ika
fuente
7
"Si es posible, me gustaría hacerlo sin agregar un nuevo contenedor para el texto que necesito cambiar". - ¿De Verdad? Porque sería mucho más fácil con un contenedor allí.
Paul D. Waite
Chicos, miré la pregunta original y no creo que el OP dijera nada sobre los elementos secundarios ...
Šime Vidas
De acuerdo con los estándares W3, div ni siquiera puede tener nodos de texto (si mal no recuerdo). Se debe agregar un contenedor de texto como <p>, <h1>, etc.
Mark Baijens
2
@Mark: posiblemente en (X) HTML Strict, pero ya no. (HTML5 ya está aquí.)
Paul D. Waite
@Matt Ball: En html real, hay etiquetas span en lugar de someChild.
ika

Respuestas:

75

Mark tiene una mejor solución usando jQuery , pero es posible que también pueda hacer esto en JavaScript normal.

En Javascript, la childNodespropiedad le brinda todos los nodos secundarios de un elemento, incluidos los nodos de texto.

Entonces, si supiera que el texto que desea cambiar siempre será lo primero en el elemento, entonces, dado, por ejemplo, este HTML:

<div id="your_div">
   **text to change**
   <p>
       text that should not change
   </p>
   <p>
       text that should not change
   </p>
</div>

Podrías hacer esto:

var your_div = document.getElementById('your_div');

var text_to_change = your_div.childNodes[0];

text_to_change.nodeValue = 'new text';

Por supuesto, aún puede usar jQuery para seleccionar <div>en primer lugar (es decir var your_div = $('your_div').get(0);).

Paul D. Waite
fuente
1
No es necesario reemplazar el nodo de texto. Simplemente cambie la propiedad datao la existente nodeValue.
Tim Down
@Tim: ah, pensé que debía haber una manera más fácil. Nunca hice mucho JavaScript antes de que apareciera jQuery. Saludos, editaré la respuesta en consecuencia.
Paul D. Waite
11
Entonces, una implementación más actualizada de esta respuesta sería$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Dean Martin
Con mucho, la solución más corta y simple - stackoverflow.com/a/54365288/4769218
Silver Ringvee
1
"Es posible que también pueda hacer esto en JavaScript normal"
duhaime
65

Actualización 2018

Como esta es una respuesta bastante popular, decidí actualizarla y embellecerla un poco agregando el selector de nodo de texto a jQuery como complemento.

En el fragmento a continuación, puede ver que defino una nueva función jQuery que obtiene todos (y solo) los textNodes. También puede encadenar esta función con, por ejemplo, la first()función. Hago un recorte en el nodo de texto y verifico si no está vacío después del recorte porque los espacios, pestañas, nuevas líneas, etc. también se reconocen como nodos de texto. Si también necesita esos nodos, simplemente elimínelos de la declaración if en la función jQuery.

Agregué un ejemplo de cómo reemplazar el primer nodo de texto y cómo reemplazar todos los nodos de texto.

Este enfoque facilita la lectura del código y facilita su uso varias veces y con diferentes propósitos.

La Actualización 2017 (adrach) también debería funcionar si lo prefiere.

Como extensión jQuery

Equivalente de Javascript (ES)


Actualización 2017 (adrach):

Parece que varias cosas han cambiado desde que se publicó. Aquí hay una versión actualizada

$("div").contents().filter(function(){ return this.nodeType == 3; }).first().replaceWith("change text");

Respuesta original (no funciona para versiones actuales)

$("div").contents().filter(function(){ return this.nodeType == 3; })
.filter(':first').text("change text");

Fuente: http://api.jquery.com/contents/

Mark Baijens
fuente
¡Ajá! Sabía que habría una función jQuery inteligente en alguna parte. El único problema con la solución tal como la ha escrito es que cada nodo de texto obtiene el siguiente texto (por ejemplo, incluidos los que están dentro de los elementos secundarios). Sin embargo, supongo que no sería demasiado difícil limitarlo.
Paul D. Waite
10
Tuve algunos problemas para usar .filter(':first'), pero usarlo .first()funcionó para mí. ¡Gracias!
hollaburoo
16
Esto no me funciona con .text()(ver este jsfiddle ) pero sí con .replaceWith()( jsfiddle aquí ).
magicalex
2
Esto funciona para mí, por feo que sea textObject = $ (myNode) .contents (). Filter (function () {return this.nodeType == 3;}) [0] $ (textObject) .replaceWith ("my text" );
Maragues
Cualquiera que tenga curiosidad acerca de nodeType=3esto es el tipo de nodo de filtro medio de "TEXTO", w3schools.com/jsref/prop_node_nodetype.asp para obtener más información
ktutnik
53

Ver en acción

Margen :

$(function() {
  $('input[type=button]').one('click', function() {
    var cache = $('#parent').children();
    $('#parent').text('Altered Text').append(cache);
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">Some text
  <div>Child1</div>
  <div>Child2</div>
  <div>Child3</div>
  <div>Child4</div>
</div>
<input type="button" value="alter text" />


fuente
6
De lejos, la mejor solución que he encontrado. Elegante y sencillo.
Yoav Kadosh
3
Sin embargo, esto no preservará el orden de los nodos. por ejemplo, si el texto estaba al final del elemento primero, ahora estaría al principio.
Matt
No conservará el orden, pero en la mayoría de los casos, querrá el texto como primer o último nodo. Entonces, si append () no obtiene el resultado deseado, intente prepend () si desea que los elementos secundarios existentes estén delante del texto.
Sensei
Con mucho, la solución más corta y sencilla - stackoverflow.com/a/54365288/4769218
Plata Ringvee
11
<div id="divtochange">
    **text to change**
    <div>text that should not change</div>
    <div>text that should not change</div>
</div>
$(document).ready(function() {
    $("#divtochange").contents().filter(function() {
            return this.nodeType == 3;
        })
        .replaceWith("changed text");
});

Esto cambia solo el primer nodo de texto

ilkin
fuente
9

Simplemente envuelva el texto que desea cambiar en un intervalo con una clase para seleccionar.

No necesariamente responde a su pregunta, lo sé, pero probablemente sea una mejor práctica de codificación. Mantenga las cosas limpias y simples

<div id="header">
   <span class="my-text">**text to change**</span>
   <div>
       text that should not change
   </div>
   <div>
       text that should not change
   </div>
</div>

¡Voilà!

$('#header .mytext').text('New text here')
MrBojangles
fuente
¡Esta es la mejor solución!
Sleek Geek
5

Para el caso específico que mencionaste:

<div id="foo">
   **text to change**
   <someChild>
       text that should not change
   </someChild>
   <someChild>
       text that should not change
   </someChild>
</div>

... esto es muy facil:

var div = document.getElementById("foo");
div.firstChild.data = "New text";

No dice cómo quiere generalizar esto. Si, digamos, desea cambiar el texto del primer nodo de texto dentro del <div>, podría hacer algo como esto:

var child = div.firstChild;
while (child) {
    if (child.nodeType == 3) {
        child.data = "New text";
        break;
    }
    child = child.nextSibling;
}
Tim Down
fuente
2

$.fn.textPreserveChildren = function(text) {
  return this.each(function() {
    return $(this).contents().filter(function() {
      return this.nodeType == 3;
    }).first().replaceWith(text);
  })
}

setTimeout(function() {
  $('.target').textPreserveChildren('Modified');
}, 2000);
.blue {
  background: #77f;
}
.green {
  background: #7f7;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<div class="target blue">Outer text
  <div>Nested element</div>
</div>

<div class="target green">Another outer text
  <div>Another nested element</div>
</div>

geg
fuente
1

Aquí es todavía otro método: http://jsfiddle.net/qYUBp/7/

HTML

<div id="header">
   **text to change**
   <div>
       text that should not change
   </div>
   <div>
       text that should not change
   </div>
</div>

JQUERY

var tmp=$("#header>div").html();
$("#header").text("its thursday").append(tmp);
Yasser Shaikh
fuente
1

Aquí hay muchas respuestas excelentes, pero solo manejan un nodo de texto con niños. En mi caso, necesitaba operar en todos los nodos de texto e ignorar los niños html PERO PRESERVAR EL ORDEN.

Entonces, si tenemos un caso como este:

<div id="parent"> Some text
    <div>Child1</div>
    <div>Child2</div>
    and some other text
    <div>Child3</div>
    <div>Child4</div>
    and here we are again
</div>

Podemos utilizar el siguiente código para modificar solo el texto Y CONSERVAR EL PEDIDO

    $('#parent').contents().filter(function() {
        return this.nodeType == Node.TEXT_NODE && this.nodeValue.trim() != '';
    }).each(function() {
    		//You can ignore the span class info I added for my particular application.
        $(this).replaceWith(this.nodeValue.replace(/(\w+)/g,"<span class='IIIclassIII$1' onclick='_mc(this)' onmouseover='_mr(this);' onmouseout='_mt(this);'>$1X</span>"));
	});
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<div id="parent"> Some text
    <div>Child1</div>
    <div>Child2</div>
    and some other text
    <div>Child3</div>
    <div>Child4</div>
    and here we are again
</div>

Aquí está el jsfiddle de que funciona

usuario1254723
fuente
0

Creo que estás buscando .prependTo ().

http://api.jquery.com/prependTo/

También podemos seleccionar un elemento de la página e insertarlo en otro:

$ ('h2'). prependTo ($ ('. contenedor'));

Si un elemento seleccionado de esta manera se inserta en otro lugar, se moverá al destino (no se clonará):

<div class="container">  
  <h2>Greetings</h2>
  <div class="inner">Hello</div>
  <div class="inner">Goodbye</div> 
</div>

Sin embargo, si hay más de un elemento de destino, se crearán copias clonadas del elemento insertado para cada destino después del primero.

pitx3
fuente
2
Yo creo que no. Parece que el OP está buscando una forma de cambiar el texto, no agregar texto nuevo.
Matt Ball
0

Respuesta simple:

$("div").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with"
macio.Jun
fuente
0

El problema con la respuesta de Mark es que también obtienes nodos de texto vacíos. Solución como complemento jQuery:

$.fn.textnodes = function () {
    return this.contents().filter(function (i,n) {
        return n.nodeType == 3 && n.textContent.trim() !== "";
    });
};

$("div").textnodes()[0] = "changed text";
br4nnigan
fuente
0

Esta es una pregunta antigua, pero puede hacer una función simple como esta para hacer su vida más fácil:

$.fn.toText = function(str) {
    var cache = this.children();
    this.text(str).append(cache);
}

Ejemplo:

<div id="my-div">
   **text to change**
   <p>
       text that should not change
   </p>
   <p>
       text that should not change
   </p>
</div>

Uso:

$("#my-div").toText("helloworld");
rotaercz
fuente
0

Vesrsion 2019 - Corto y simple

document.querySelector('#your-div-id').childNodes[0].nodeValue = 'new text';

Explicación

document.querySelector('#your-div-id') se utiliza para seleccionar el padre (el elemento cuyo texto está a punto de cambiar)

.childNodes[0] selecciona el nodo de texto

.nodeValue = 'new text' establece el valor del nodo de texto en "texto nuevo"


Esta respuesta posiblemente esté inspirada en el comentario de Dean Martin. No puedo decirlo con certeza ya que he estado usando esta solución durante años. Pensé que debería publicar esta probabilidad aquí porque a algunas personas les importa más que el hecho de que esta es la mejor solución.

Ringvee de plata
fuente
2
Acaba de copiar el comentario de Dean Martin de 2014 y reclamarlo como su solución de 2019. También use una combinación innecesaria de jQuery y Javascript simple que, en mi opinión, es una mala práctica para una buena legibilidad. Por último, no proporcionó ningún detalle sobre cómo funciona su solución. No es el más complicado, pero tampoco lo es este problema, por lo que probablemente sean programadores bastante nuevos los que lean esto y que podrían usar una explicación adicional, por ejemplo, que obtiene el nodo DOM nativo del objeto jQuery accediendo al índice de la matriz.
Mark Baijens
@MarkBaijens Creo que puede que tengas razón, el comentario de Dean Martin podría ser donde obtuve este fragmento hace años. De todos modos, lo he estado usando desde entonces y lo copié de mi repositorio de código personal (donde enumero los scripts útiles) en enero. Se agregó una explicación más detallada y se eliminó jQuery innecesario.
Silver Ringvee
Esta es, con mucho, la solución más simple que realmente funciona y la gente debería usarla, aunque no recibe ningún elogio por empeñarla como suya sin darle crédito a Dean Martin. También estoy de acuerdo con Mark Baijens en que la implementación pura de Vanilla JS en este caso es mejor que mezclar jQuery y Vanilla JS. No solo para una mejor legibilidad, sino también para un mejor rendimiento, ya que jQuery ralentiza las cosas.
Miqi180
@ Miqi180: como ya dije anteriormente, esto es posible, no recuerdo dónde obtuve esta solución por primera vez. Lo he estado usando en mi trabajo durante años. Agregó una nota en la respuesta. Con suerte, ayudará a personas como usted a quienes no les importa la mejor solución aquí en SO:
Silver Ringvee