Jak mogę zmienić tekst elementu bez zmiany jego elementów podrzędnych?

120

Chciałbym dynamicznie aktualizować tekst elementu:

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

Jestem nowy w jQuery, więc to zadanie wydaje mi się dość trudne. Czy ktoś mógłby wskazać mi funkcję / selektor do użycia?

Jeśli to możliwe, chciałbym to zrobić bez dodawania nowego kontenera na tekst, który muszę zmienić.

ika
źródło
7
„Jeśli to możliwe, chciałbym to zrobić bez dodawania nowego kontenera na tekst, który muszę zmienić”. - naprawdę? Ponieważ byłoby znacznie łatwiej z pojemnikiem w środku.
Paul D. Waite
Chłopaki, spojrzałem na oryginalne pytanie i nie sądzę, aby OP mówił cokolwiek o elementach dziecięcych ...
Šime Vidas
Zgodnie ze standardami W3 div nie może nawet mieć węzłów tekstowych (jeśli dobrze pamiętam). Należy dodać kontener tekstu, taki jak <p>, <h1> itd.
Mark Baijens
2
@Mark: prawdopodobnie w (X) HTML Strict, ale już nie. (HTML5 jest już dostępny.)
Paul D. Waite
@Matt Ball: W aktualnym html znajdują się tagi span zamiast jakiegoś dziecka.
ika

Odpowiedzi:

75

Mark ma lepsze rozwiązanie przy użyciu jQuery , ale możesz to zrobić również w zwykłym JavaScript.

W Javascript ta childNodeswłaściwość udostępnia wszystkie węzły potomne elementu, w tym węzły tekstowe.

Tak więc, jeśli wiesz, że tekst, który chcesz zmienić, zawsze będzie pierwszą rzeczą w elemencie, to masz np. Ten HTML:

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

Możesz to zrobić:

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

var text_to_change = your_div.childNodes[0];

text_to_change.nodeValue = 'new text';

Oczywiście nadal możesz użyć jQuery, aby wybrać <div>na pierwszym miejscu (tj var your_div = $('your_div').get(0);.).

Paul D. Waite
źródło
1
Nie ma potrzeby zastępowania węzła tekstowego. Po prostu zmień istniejącą datalub nodeValuenieruchomość.
Tim Down
@Tim: ach, pomyślałem, że musi być łatwiejszy sposób. Nigdy nie robiłem zbyt wiele JavaScript przed pojawieniem się jQuery. Pozdrawiam, odpowiednio poprawię odpowiedź.
Paul D. Waite
11
Tak więc bardziej aktualna realizacja tej odpowiedzi byłaby$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Dean Martin
Zdecydowanie najkrótsze i najprostsze rozwiązanie - stackoverflow.com/a/54365288/4769218
Silver Ringvee
1
„Możesz to zrobić również w zwykłym javascript”
duhaime
65

Zaktualizuj 2018

Ponieważ jest to dość popularna odpowiedź, postanowiłem ją zaktualizować i trochę upiększyć, dodając selektor textnode do jQuery jako wtyczkę.

W poniższym fragmencie widać, że definiuję nową funkcję jQuery, która pobiera wszystkie (i tylko) textNodes. Możesz również połączyć tę funkcję za pomocą na przykład first()funkcji. Przycinam węzeł tekstowy i sprawdzam, czy po przycięciu nie jest pusty, ponieważ spacje, tabulatory, nowe wiersze itp. Są również rozpoznawane jako węzły tekstowe. Jeśli potrzebujesz również tych węzłów, po prostu usuń to z instrukcji if w funkcji jQuery.

Dodałem przykład, jak zamienić pierwszy węzeł tekstowy i jak zamienić wszystkie węzły tekstowe.

Takie podejście ułatwia czytanie kodu i łatwiejsze korzystanie z niego wiele razy i do różnych celów.

Aktualizacja 2017 (adrach) powinna nadal działać jak dobrze, jeśli wolisz.

Jako rozszerzenie jQuery

Eqivilant Javascript (ES)


Aktualizacja 2017 (adrach):

Wygląda na to, że od czasu opublikowania tego artykułu zmieniło się kilka rzeczy. Oto zaktualizowana wersja

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

Oryginalna odpowiedź (nie działa w aktualnych wersjach)

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

Źródło: http://api.jquery.com/contents/

Mark Baijens
źródło
Aha! Wiedziałem, że gdzieś będzie sprytna funkcja jQuery. Jedynym problemem z rozwiązaniem, jakie napisałeś, jest to, że każdy węzeł tekstowy otrzymuje następny tekst (np. Włączając te w elementach potomnych). Myślę, że nie byłoby jednak trudno to ograniczyć?
Paul D. Waite
10
Miałem problemy z używaniem .filter(':first'), ale używanie .first()zamiast tego działało dla mnie. Dzięki!
hollaburoo
16
To nie działa dla mnie z .text()(zobacz to jsfiddle ), ale działa z .replaceWith()( jsfiddle tutaj ).
magicalex
2
To działa dla mnie, brzydkie, ponieważ jest textObject = $ (myNode) .contents (). Filter (function () {return this.nodeType == 3;}) [0] $ (textObject) .replaceWith ("mój tekst" );
Maragues
ktoś zaciekawiony nodeType=3to znaczy filtrować tylko typ węzła „TEXT”, w3schools.com/jsref/prop_node_nodetype.asp po więcej informacji
ktutnik
53

Zobacz w akcji

Narzut :

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


źródło
6
Zdecydowanie najlepsze rozwiązanie, z jakim się spotkałem. Eleganckie i proste.
Yoav Kadosh
3
Nie zachowa to jednak kolejności węzłów. np. jeśli tekst byłby najpierw na końcu elementu, teraz byłby na początku.
Matt,
Nie zachowa porządku, ale w większości przypadków chcesz, aby tekst był pierwszym lub ostatnim węzłem. Więc jeśli append () nie daje pożądanego wyniku, spróbuj przedpend (), jeśli chcesz, aby istniejące elementy podrzędne znajdowały się przed tekstem.
Sensei
Zdecydowanie najkrótsze i najprostsze rozwiązanie - 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");
});

To zmienia tylko pierwszy węzeł tekstowy

ilkin
źródło
9

Po prostu zawiń tekst, który chcesz zmienić, w zakres klasą do wybrania.

Niekoniecznie odpowiada na twoje pytanie, które znam, ale prawdopodobnie lepsza praktyka kodowania. Utrzymuj porządek i prostotę

<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
źródło
To najlepsze rozwiązanie!
Sleek Geek
5

W konkretnym przypadku, o którym wspomniałeś:

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

... to jest bardzo proste:

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

Nie mówisz, jak chcesz to uogólniać. Jeśli, powiedzmy, chcesz zmienić tekst pierwszego węzła tekstowego w obrębie <div>, możesz zrobić coś takiego:

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

Tutaj jest jeszcze inna metoda: 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
źródło
1

Wiele świetnych odpowiedzi tutaj, ale obsługują one tylko jeden węzeł tekstowy z dziećmi. W moim przypadku musiałem operować na wszystkich węzłach tekstowych i ignorować dzieci HTML, ALE ZACHOWAĆ ZAMÓWIENIE.

Więc jeśli mamy taki przypadek:

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

Możemy użyć poniższego kodu tylko do modyfikacji tekstu I ZACHOWANIA ZAMÓWIENIA

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

Oto jsfiddle z nim pracować

user1254723
źródło
0

Myślę, że szukasz .prependTo ().

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

Możemy też wybrać element na stronie i wstawić go do innego:

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

Jeśli element wybrany w ten sposób zostanie wstawiony w innym miejscu, zostanie przeniesiony do celu (nie sklonowany):

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

Jeśli jednak istnieje więcej niż jeden element docelowy, sklonowane kopie wstawionego elementu zostaną utworzone dla każdego elementu docelowego po pierwszym.

pitx3
źródło
2
Myślę, że nie. Wygląda na to, że OP szuka sposobu na zmianę tekstu, a nie dodawanie nowego tekstu.
Matt Ball
0

Prosta odpowiedź:

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

Problem z odpowiedzią Marka polega na tym, że otrzymujesz również puste textnodes. Rozwiązanie jako wtyczka jQuery:

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

$("div").textnodes()[0] = "changed text";
br4nnigan
źródło
0

To stare pytanie, ale możesz zrobić prostą funkcję, taką jak ta, aby ułatwić sobie życie:

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

Przykład:

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

Stosowanie:

$("#my-div").toText("helloworld");
rotaercz
źródło
0

Wersja 2019 - Krótka i prosta

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

Wyjaśnienie

document.querySelector('#your-div-id') służy do wyboru rodzica (elementu, którego tekst zamierzasz zmienić)

.childNodes[0] wybiera węzeł tekstowy

.nodeValue = 'new text' ustawia wartość węzła tekstowego na „nowy tekst”


Ta odpowiedź jest prawdopodobnie inspirowana komentarzem Deana Martina. Nie mogę tego powiedzieć na pewno, ponieważ korzystam z tego rozwiązania od lat. Pomyślałem, że powinienem tutaj zamieścić to prawdopodobieństwo, ponieważ niektórym ludziom zależy na tym bardziej niż na tym, że jest to najlepsze rozwiązanie.

Silver Ringvee
źródło
2
Właśnie skopiowałeś komentarz Deana Martina z 2014 roku i twierdzisz, że jest to Twoje rozwiązanie z 2019 roku. Użyj również niepotrzebnego połączenia jQuery i zwykłego Javascript, co moim zdaniem jest złą praktyką dla dobrej czytelności. Ostatnio nie podałeś żadnych szczegółów dotyczących działania twojego rozwiązania. Nie jest to najbardziej skomplikowane, ale też nie jest to problem, więc prawdopodobnie są to całkiem nowi programiści, którzy to czytają, którzy mogą użyć dodatkowego wyjaśnienia, na przykład, że uzyskujesz natywny węzeł DOM z obiektu jQuery, uzyskując dostęp do indeksu tablicy.
Mark Baijens
@MarkBaijens Myślę, że możesz mieć rację, komentarz Deana Martina może być miejscem, w którym dostałem ten fragment lata temu. W każdym razie używam go od tamtej pory i właśnie skopiowałem go z mojego osobistego repozytorium kodu (gdzie wymieniam przydatne skrypty) w styczniu. Dodano bardziej szczegółowe wyjaśnienie i usunięto niepotrzebne jQuery.
Silver Ringvee
Jest to zdecydowanie najprostsze rozwiązanie, które faktycznie działa i ludzie powinni z niego korzystać, chociaż nie dostajesz żadnych pochwał za zastawienie go jako własnego bez przyznania kredytu Deanowi Martinowi. Zgadzam się również z Markiem Baijensem, że implementacja czystego Vanilla JS w tym przypadku jest lepsza niż pomieszanie jQuery i Vanilla JS. Nie tylko dla lepszej czytelności, ale także dla lepszej wydajności, ponieważ jQuery spowalnia działanie.
Miqi180
@ Miqi180 - jak już wspomniałem powyżej, jest to możliwe, nie pamiętam, gdzie pierwszy raz dostałem to rozwiązanie. Od lat używam go w swojej pracy. Dodano uwagę w odpowiedzi. Miejmy nadzieję, że pomoże to ludziom takim jak ty, którzy nie dbają o najlepsze rozwiązanie tutaj na SO:
Silver Ringvee