Como posso alterar o texto de um elemento sem alterar seus elementos filho?

120

Eu gostaria de atualizar o texto do elemento dinamicamente:

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

Eu sou novo no jQuery, então esta tarefa parece ser bastante desafiadora para mim. Alguém poderia me indicar uma função / seletor para usar?

Se for possível, gostaria de fazer isso sem adicionar um novo contêiner para o texto que preciso alterar.

ika
fonte
7
“Se for possível, gostaria de fazer isso sem adicionar um novo contêiner para o texto que preciso alterar.” - realmente? Porque seria muito mais fácil com um contêiner ali.
Paul D. Waite
Pessoal, eu olhei para a pergunta original e não acho que o OP disse nada sobre elementos filhos ...
Šime Vidas
De acordo com os padrões W3, div não pode nem ter nós de texto (se bem me lembro correto). Um contêiner de texto como <p>, <h1>, etc. deve ser adicionado.
Mark Baijens
2
@Mark: possivelmente em (X) HTML Strict, mas não mais. (HTML5 já está disponível.)
Paul D. Waite
@Matt Ball: No html real, existem tags span em vez de someChild.
Ika

Respostas:

75

Mark tem uma solução melhor usando jQuery , mas você também pode fazer isso em JavaScript normal.

Em Javascript, o childNodes propriedade fornece todos os nós filhos de um elemento, incluindo nós de texto.

Portanto, se você sabia que o texto que deseja alterar sempre será a primeira coisa no elemento, forneça, por exemplo, este HTML:

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

Você poderia fazer isso:

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

var text_to_change = your_div.childNodes[0];

text_to_change.nodeValue = 'new text';

Claro, você ainda pode usar jQuery para selecionar o <div>em primeiro lugar (ou seja var your_div = $('your_div').get(0);).

Paul D. Waite
fonte
1
Não há necessidade de substituir o nó de texto. Basta alterar o existente dataou nodeValuepropriedade.
Tim Down
@Tim: ah, pensei que devia haver uma maneira mais fácil. Nunca fiz muito JavaScript antes do surgimento do jQuery. Saúde, vou editar a resposta de acordo.
Paul D. Waite
11
Portanto, uma implementação mais atualizada dessa resposta seria$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Dean Martin
De longe a solução mais curta e simples - stackoverflow.com/a/54365288/4769218
Silver Ringvee
1
"Você também pode fazer isso em javascript normal"
duhaime
65

Atualização 2018

Como essa é uma resposta muito popular, decidi atualizá-la e embelezá-la um pouco adicionando o seletor de textnode ao jQuery como um plugin.

No trecho abaixo você pode ver que eu defino uma nova função jQuery que obtém todos (e apenas) os textNodes. Você pode encadear esta função também com, por exemplo, ofirst() função. Eu faço um corte no nó de texto e verifico se não está vazio após o corte porque espaços, tabulações, novas linhas, etc. também são reconhecidos como nós de texto. Se você precisar desses nós também, basta removê-los da instrução if na função jQuery.

Eu adicionei um exemplo de como substituir o primeiro nó de texto e como substituir todos os nós de texto.

Essa abordagem torna mais fácil ler o código e usá-lo várias vezes e com finalidades diferentes.

A atualização de 2017 (adrach) ainda deve funcionar bem, se você preferir.

Como extensão jQuery

Javascript (ES) eqivilant


Atualização de 2017 (adrach):

Parece que várias coisas mudaram desde que isso foi postado. Aqui está uma versão atualizada

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

Resposta original (não funciona para as versões atuais)

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

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

Mark Baijens
fonte
Aha! Eu sabia que haveria uma função jQuery inteligente em algum lugar. O único problema com a solução que você escreveu é que cada nó de texto obtém o próximo texto (por exemplo, incluindo aqueles dentro dos elementos filhos). Eu acho que não seria muito difícil limitar isso?
Paul D. Waite
10
Tive alguns problemas para usar .filter(':first'), mas em .first()vez disso funcionou para mim. Obrigado!
hollaburoo
16
Isso não funciona para mim com .text()(veja este jsfiddle ), mas sim com .replaceWith()( jsfiddle aqui ).
magicalex
2
Isso funciona para mim, feio porque é textObject = $ (myNode) .contents (). Filter (function () {return this.nodeType == 3;}) [0] $ (textObject) .replaceWith ("my text" );
Maragues,
qualquer pessoa curiosa sobre nodeType=3isso significa filtrar apenas o tipo de nó de "TEXTO", w3schools.com/jsref/prop_node_nodetype.asp para mais informações
ktutnik
53

Veja em ação

Markup:

$(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" />


fonte
6
De longe a melhor solução que encontrei. Elegante e simples.
Yoav Kadosh
3
Isso não preservará a ordem dos nós, entretanto. por exemplo, se o texto estava primeiro no final do elemento, agora estaria no início.
Matt
Não preservará a ordem, mas na maioria dos casos você deseja que o texto seja o primeiro ou o último nó. Portanto, se append () não obtiver o resultado desejado, tente prepend () se quiser os filhos existentes na frente do texto.
Sensei
De longe a solução mais curta e simples - stackoverflow.com/a/54365288/4769218
Silver 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");
});

Isso muda apenas o primeiro textnode

ilkin
fonte
9

Basta envolver o texto que deseja alterar em um intervalo com uma classe para selecionar.

Não necessariamente responde à sua pergunta, eu sei, mas, provavelmente, uma prática de codificação melhor. Mantenha as coisas limpas e 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
fonte
Essa é a melhor solução!
Sleek Geek
5

Para o caso específico que você mencionou:

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

... isso é muito fácil:

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

Você não declara como deseja generalizar isso. Se, digamos, você deseja alterar o texto do primeiro nó de texto dentro do <div>, você pode fazer algo assim:

var child = div.firstChild;
while (child) {
    if (child.nodeType == 3) {
        child.data = "New text";
        break;
    }
    child = child.nextSibling;
}
Tim Down
fonte
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
fonte
1

Aqui está ainda um outro 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
fonte
1

Muitas respostas boas aqui, mas eles lidam apenas com um nó de texto com filhos. No meu caso, eu precisava operar em todos os nós de texto e ignorar os filhos html, MAS PRESERVE A ORDEM.

Portanto, se tivermos um 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 usar o seguinte código para modificar apenas o texto E PRESERVAR O 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>

Aqui está a jsFiddle dele trabalhando

user1254723
fonte
0

Acho que você está procurando por .prependTo ().

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

Também podemos selecionar um elemento da página e inseri-lo em outra:

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

Se um elemento selecionado desta forma for inserido em outro lugar, ele será movido para o destino (não clonado):

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

Se houver mais de um elemento de destino, entretanto, cópias clonadas do elemento inserido serão criadas para cada destino após o primeiro.

pitx3
fonte
2
Eu acho que não. Parece que o OP está procurando uma maneira de alterar o texto, não de adicionar um novo texto.
Matt Ball
0

Resposta simples:

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

O problema com a resposta de Mark é que você também obtém textnodes vazios. Solução como plugin jQuery:

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

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

Esta é uma questão antiga, mas você pode fazer uma função simples como esta para tornar sua vida mais fácil:

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

Exemplo:

<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
fonte
0

Versão 2019 - curta e simples

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

Explicação

document.querySelector('#your-div-id') é usado para selecionar o pai (o elemento cujo texto você está prestes a alterar)

.childNodes[0] seleciona o nó de texto

.nodeValue = 'new text' define o valor do nó de texto para "novo texto"


Esta resposta é possivelmente inspirada no comentário de Dean Martin. Não posso dizer com certeza, já que uso essa solução há anos. Achei que deveria postar essa probabilidade aqui porque algumas pessoas se preocupam mais com ela do que com o fato de ser a melhor solução.

Silver Ringvee
fonte
2
Você acabou de copiar o comentário de Dean Martin de 2014 e reivindicar isso como sua solução de 2019. Use também uma combinação desnecessária de jQuery e JavaScript simples, o que, em minha opinião, é uma prática ruim para uma boa legibilidade. Por último, você não colocou detalhes sobre como sua solução funciona. Não é o mais complicado, mas também não é esse problema, portanto, provavelmente são os programadores muito novos que lêem isso que poderiam usar alguma explicação extra, por exemplo, que você obtenha o nó DOM nativo do objeto jQuery acessando o índice do array.
Mark Baijens
@MarkBaijens Acho que você pode estar certo, o comentário de Dean Martin pode ser de onde obtive este trecho anos atrás. De qualquer forma, tenho usado desde então e apenas copiei do meu repositório de código pessoal (onde listo scripts úteis) em janeiro. Adicionada uma explicação mais detalhada e removido jQuery desnecessário.
Silver Ringvee
Esta é de longe a solução mais simples que realmente funciona e as pessoas deveriam usá-la, embora você não receba nenhum elogio por penhorá-la como se fosse sua sem dar crédito a Dean Martin. Também concordo com Mark Baijens que a implementação de JS Vanilla puro neste caso é melhor do que misturar jQuery e Vanilla JS. Não apenas para melhor legibilidade, mas também para melhor desempenho, pois o jQuery torna as coisas mais lentas.
Miqi180
@ Miqi180 - como já disse acima, isso é possível, não me lembro de onde consegui essa solução. Eu tenho usado em meu trabalho há anos. Adicionou uma nota na resposta. Espero que ajude pessoas como você que não se importam com a melhor solução aqui no SO:
Silver Ringvee