Comment puis-je modifier le texte d'un élément sans modifier ses éléments enfants?

120

Je souhaite mettre à jour le texte de l'élément de manière dynamique:

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

Je suis nouveau dans jQuery, donc cette tâche semble être assez difficile pour moi. Quelqu'un pourrait-il m'indiquer une fonction / sélecteur à utiliser?

Si c'est possible, j'aimerais le faire sans ajouter de nouveau conteneur pour le texte que je dois modifier.

ika
la source
7
"Si c'est possible, j'aimerais le faire sans ajouter de nouveau conteneur pour le texte que je dois modifier." - vraiment? Parce que ce serait beaucoup plus facile avec un conteneur là-dedans.
Paul D.Waite
Les gars, j'ai regardé la question initiale et je ne pense pas que le PO ait dit quoi que ce soit sur les éléments enfants ...
Šime Vidas
Selon les normes W3, div ne peut même pas avoir de nœuds de texte (si je me souviens bien). Un conteneur de texte comme <p>, <h1>, etc. doit être ajouté.
Mark Baijens
2
@Mark: éventuellement en (X) HTML Strict, mais pas plus. (HTML5 est ici maintenant.)
Paul D. Waite
@Matt Ball: En html réel, il y a des balises span au lieu de someChild.
ika

Réponses:

75

Mark a une meilleure solution en utilisant jQuery , mais vous pourrez peut-être le faire également en JavaScript standard.

En Javascript, la childNodespropriété vous donne tous les nœuds enfants d'un élément, y compris les nœuds de texte.

Donc, si vous saviez que le texte que vous vouliez changer allait toujours être la première chose dans l'élément, alors donné par exemple ce HTML:

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

Vous pouvez faire ceci:

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

var text_to_change = your_div.childNodes[0];

text_to_change.nodeValue = 'new text';

Bien sûr, vous pouvez toujours utiliser jQuery pour sélectionner le <div>en premier lieu (ie var your_div = $('your_div').get(0);).

Paul D. Waite
la source
1
Pas besoin de remplacer le nœud de texte. Changez simplement la propriété dataou la nodeValuepropriété existante .
Tim Down
@Tim: ah, je pensais qu'il devait y avoir un moyen plus simple. Jamais beaucoup de JavaScript avant l'arrivée de jQuery. Bravo, je modifierai la réponse en conséquence.
Paul D.Waite
11
Donc, une mise en œuvre plus à jour de cette réponse serait$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Dean Martin
De loin la solution la plus courte et la plus simple - stackoverflow.com/a/54365288/4769218
Silver Ringvee
1
"Vous pourrez peut-être le faire en javascript standard aussi"
duhaime
65

Mise à jour 2018

Comme il s'agit d'une réponse assez populaire, j'ai décidé de la mettre à jour et de l'embellir un peu en ajoutant le sélecteur de noeud de texte à jQuery en tant que plugin.

Dans l'extrait ci-dessous, vous pouvez voir que je définis une nouvelle fonction jQuery qui obtient tous (et uniquement) les textNodes. Vous pouvez également enchaîner cette fonction avec par exemple la first()fonction. Je fais une découpe sur le nœud de texte et vérifie s'il n'est pas vide après la découpe car les espaces, les tabulations, les nouvelles lignes, etc. sont également reconnus comme des nœuds de texte. Si vous avez également besoin de ces nœuds, supprimez simplement cela de l'instruction if de la fonction jQuery.

J'ai ajouté un exemple comment remplacer le premier nœud de texte et comment remplacer tous les nœuds de texte.

Cette approche facilite la lecture du code et facilite son utilisation plusieurs fois et à des fins différentes.

La mise à jour 2017 (adrach) devrait toujours fonctionner aussi si vous préférez cela.

En tant qu'extension jQuery

Javascript (ES) eqivilant


Mise à jour 2017 (adrach):

Il semble que plusieurs choses aient changé depuis sa publication. Voici une version mise à jour

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

Réponse originale (ne fonctionne pas pour les versions actuelles)

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

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

Mark Baijens
la source
Ah! Je savais qu'il y aurait une fonction jQuery intelligente quelque part. Le seul problème avec la solution telle que vous l'avez écrite est que chaque nœud de texte obtient le texte suivant (par exemple, y compris ceux des éléments enfants). Je suppose que ce ne serait pas trop difficile de le limiter?
Paul D.Waite
10
J'ai eu du mal à utiliser .filter(':first'), mais utiliser à la .first()place a fonctionné pour moi. Merci!
hollaburoo
16
Cela ne fonctionne pas pour moi avec .text()(voir ce jsfiddle ) mais avec .replaceWith()( jsfiddle ici ).
magicalex
2
Cela fonctionne pour moi, moche car il est textObject = $ (myNode) .contents (). Filter (function () {return this.nodeType == 3;}) [0] $ (textObject) .replaceWith ("my text" );
Maragues
quiconque est curieux à nodeType=3ce sujet est moyen de filtrer uniquement le type de nœud de "TEXT", w3schools.com/jsref/prop_node_nodetype.asp pour plus d'informations
ktutnik
53

Voir en action

Balisage:

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


la source
6
De loin la meilleure solution que j'ai trouvée. Élégant et simple.
Yoav Kadosh
3
Cependant, cela ne conservera pas l'ordre des nœuds. par exemple, si le texte était à la fin de l'élément en premier, il serait maintenant au début.
Matt
Il ne conservera pas l'ordre, mais dans la plupart des cas, vous voulez que le texte soit le premier ou le dernier nœud. Donc, si append () n'obtient pas le résultat souhaité, essayez prepend () si vous voulez que les enfants existants soient en face du texte.
Sensei
De loin la solution la plus courte et la plus simple - 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");
});

Cela ne change que le premier textnode

ilkin
la source
9

Enveloppez simplement le texte que vous souhaitez modifier dans une plage avec une classe à sélectionner.

Ne répond pas nécessairement à votre question, je sais, mais, probablement une meilleure pratique de codage. Gardez les choses propres et 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
la source
C'est la meilleure solution!
Sleek Geek
5

Pour le cas spécifique que vous avez mentionné:

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

... c'est très simple:

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

Vous ne dites pas comment vous voulez généraliser cela. Si, par exemple, vous souhaitez modifier le texte du premier nœud de texte dans le <div>, vous pouvez faire quelque chose comme ceci:

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

Voici encore une autre méthode: 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
la source
1

Beaucoup de bonnes réponses ici, mais elles ne gèrent qu'un seul nœud de texte avec des enfants. Dans mon cas, j'avais besoin d'opérer sur tous les nœuds de texte et d'ignorer les enfants html MAIS PRESERVEZ LA COMMANDE.

Donc, si nous avons un cas comme celui-ci:

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

Nous pouvons utiliser le code suivant pour modifier uniquement le texte ET CONSERVER LA COMMANDE

    $('#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>

Voici le jsfiddle de son fonctionnement

user1254723
la source
0

Je pense que vous recherchez .prependTo ().

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

Nous pouvons également sélectionner un élément sur la page et l'insérer dans un autre:

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

Si un élément sélectionné de cette manière est inséré ailleurs, il sera déplacé dans la cible (non cloné):

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

S'il y a plus d'un élément cible, cependant, des copies clonées de l'élément inséré seront créées pour chaque cible après le premier.

pitx3
la source
2
Je crois que non. Il semble que l'OP cherche un moyen de changer le texte, pas d'ajouter un nouveau texte.
Matt Ball
0

Réponse simple:

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

Le problème avec la réponse de Mark est que vous obtenez également des nœuds de texte vides. Solution comme 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
la source
0

C'est une vieille question mais vous pouvez créer une fonction simple comme celle-ci pour vous faciliter la vie:

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

Exemple:

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

Usage:

$("#my-div").toText("helloworld");
Rotaercz
la source
0

Vesrsion 2019 - Court et simple

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

Explication

document.querySelector('#your-div-id') est utilisé pour sélectionner le parent (l'élément dont vous êtes sur le point de modifier le texte)

.childNodes[0] sélectionne le nœud de texte

.nodeValue = 'new text' définit la valeur du nœud de texte sur "nouveau texte"


Cette réponse est peut-être inspirée du commentaire de Dean Martin. Je ne peux pas le dire avec certitude puisque j'utilise cette solution depuis des années maintenant. Je pensais juste que je devrais afficher cette probabilité ici parce que certaines personnes se soucient plus de cela que du fait que c'est la meilleure solution.

Ringvee en argent
la source
2
Vous venez de copier le commentaire de Dean Martin de 2014 et le revendiquez comme votre solution 2019. Utilisez également une combinaison inutile de jQuery et de Javascript brut, ce qui à mon avis est une mauvaise pratique pour une bonne lisibilité. Enfin, vous n'avez donné aucun détail sur le fonctionnement de votre solution. Ce n'est pas le plus compliqué mais ce problème ne l'est pas non plus, donc ce sont probablement de très nouveaux programmeurs qui liront ceci qui pourraient utiliser une explication supplémentaire, par exemple que vous obtenez le nœud DOM natif de l'objet jQuery en accédant à l'index du tableau.
Mark Baijens
@MarkBaijens Je pense que vous avez peut-être raison, le commentaire de Dean Martin est peut-être là où j'ai eu cet extrait il y a des années. Quoi qu'il en soit, je l'utilise depuis et je viens de le copier à partir de mon repo de code personnel (où je liste des scripts utiles) en janvier. Ajout d'une explication plus détaillée et suppression de jQuery inutile.
Silver Ringvee
C'est de loin la solution la plus simple qui fonctionne réellement et les gens devraient l'utiliser, même si vous n'obtenez pas de félicitations pour l'avoir mise en gage comme la vôtre sans donner de crédit à Dean Martin. Je suis également d'accord avec Mark Baijens pour dire que l'implémentation pure de Vanilla JS dans ce cas est meilleure que de mélanger jQuery et Vanilla JS. Non seulement pour une meilleure lisibilité, mais aussi pour de meilleures performances car jQuery ralentit les choses.
Miqi180
@ Miqi180 - comme je l'ai déjà dit ci-dessus, c'est possible, je ne me souviens pas d'où j'ai eu cette solution pour la première fois. Je l'utilise dans mon travail depuis des années maintenant. Ajout d'une note dans la réponse. Espérons que cela aide les gens comme vous qui ne se soucient pas de la meilleure solution ici sur SO:
Silver Ringvee