Як я можу змінити текст елемента, не змінюючи його дочірніх елементів?


120

Я хотів би динамічно оновити текст елемента:

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

Я новачок у jQuery, тому це завдання здається для мене досить складним. Чи може хтось вказати мені на функцію / селектор, який слід використовувати?

Якщо це можливо, я хотів би зробити це без додавання нового контейнера для тексту, який мені потрібно змінити.


7
"Якщо це можливо, я хотів би зробити це без додавання нового контейнера для тексту, який мені потрібно змінити." - справді? Тому що з контейнером там буде набагато простіше.
Пол Д. Уейт

Хлопці, я подивився на оригінальне запитання і не думаю, що ОП нічого не сказала про дитячі елементи ...
Šime Vidas

Відповідно до стандартів W3 div навіть не може мати текстові вузли (якщо я пам'ятаю правильно). Необхідно додати текстовий контейнер типу <p>, <h1> тощо.
Марк Байєнс

2
@Mark: можливо в (X) HTML Strict, але не більше. (HTML5 зараз тут.)
Пол Д. Уайт

@Matt Ball: Насправді html є теги span замість someChild.
ika

Відповіді:


75

Марк отримав краще рішення за допомогою jQuery , але ви можете це зробити і в звичайному JavaScript.

В Javascript childNodesвластивість дає всі дочірні вузли елемента, включаючи текстові.

Отже, якщо ви знали, що текст, який ви хочете змінити, завжди буде першим ділом у елементі, то надайте, наприклад, цей HTML:

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

Ви можете це зробити:

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

var text_to_change = your_div.childNodes[0];

text_to_change.nodeValue = 'new text';

Звичайно, ви все одно можете використовувати jQuery для вибору <div>в першу чергу (тобто var your_div = $('your_div').get(0);).


1
Не потрібно замінювати текстовий вузол. Просто змініть наявні dataчи nodeValueвластивості.
Тім Даун

@Tim: ах, я думав, що повинен бути простіший спосіб. Ніколи не робив багато JavaScript, перш ніж з'явився jQuery. Привіт, відповідь я відредагую відповідно.
Пол Д. Уейт

11
Тож більш актуальною є реалізація цієї відповіді$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Дін Мартін

До сих пір найкоротший і просте рішення - stackoverflow.com/a/54365288/4769218
Silver Ringvee

1
"Ви можете зробити це також у звичайному javascript"
duhaime

65

Оновлення 2018 року

Оскільки це досить популярна відповідь, я вирішив її трохи оновити та прикрасити, додавши селектор текстових вузлів до jQuery як плагін.

У фрагменті нижче ви бачите, що я визначаю нову функцію jQuery, яка отримує всі (і лише) текстові вузли. Цю функцію можна пов'язати також, наприклад, з first()функцією. Я обробляю текстовий вузол і перевіряю, чи не порожній він після обрізки, оскільки пробіли, вкладки, нові рядки тощо також розпізнаються як текстові вузли. Якщо вам потрібні ці вузли, просто видаліть їх з оператора if у функції jQuery.

Я додав приклад, як замінити перший текстовий вузол і як замінити всі текстові вузли.

Такий підхід полегшує читання коду та простіше використовувати його кілька разів та з різною метою.

Update 2017 (adrach) має працювати, а якщо ви віддаєте перевагу.

Як розширення jQuery

Javascript (ES) eqivilant


Оновлення 2017 року (adrach):

Схоже, кілька речей змінилися з моменту публікації. Ось оновлена ​​версія

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

Оригінальна відповідь (не працює для поточних версій)

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

Джерело: http://api.jquery.com/contents/


Ага! Я знав, що десь буде розумна функція jQuery. Єдина проблема з рішенням, як ви написали, полягає в тому, що кожен текстовий вузол отримує наступний текст (наприклад, включаючи той, який знаходиться в дочірніх елементах). Я думаю, що це було б не надто важко обмежити?
Пол Д. Уейт

10
У мене виникли проблеми з використанням .filter(':first'), але використання .first()замість цього працювало на мене. Дякую!
hollaburoo

16
Це не працює для мене .text()(див. Цю jsfiddle ), але це стосується .replaceWith()( jsfiddle тут ).
magicalex

2
Це працює для мене, некрасиво, оскільки це textObject = $ (myNode) .contents (). Filter (function () {return this.nodeType == 3;}) [0] $ (textObject) .replaceWith ("мій текст" );
Мараги

кому цікаво nodeType=3це означає середній тип фільтра "TEXT", w3schools.com/jsref/prop_node_nodetype.asp для отримання додаткової інформації
ktutnik

53

Дивіться в дії

Розмітка:

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


6
На сьогодні найкраще рішення, на яке я потрапив. Елегантний і простий.
Йоав Кадош

3
Однак це не збереже порядок вузлів. наприклад, якщо текст був у кінці елемента спочатку, то він був би на початку.
Мет

Він не збереже порядок, але в більшості випадків ви хочете, щоб текст був першим або останнім вузлом. Тож якщо додаток () не дає бажаного результату, спробуйте prepend (), якщо ви хочете, щоб наявні діти передували тексту.
Сенсей

До сих пір найкоротший і просте рішення - 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");
});

Це змінює лише перший текстовий вузол


9

Просто загортайте текст, який ви хочете змінити, в проміжок із класом, який потрібно вибрати.

Це не обов'язково відповідає на ваше запитання, яке я знаю, але, мабуть, краща практика кодування. Зберігайте речі чистими та простими

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

Вуаля!

$('#header .mytext').text('New text here')

Це найкраще рішення!
Гладкий вигляд

5

Для конкретного випадку ви згадали:

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

... це дуже просто:

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

Ви не заявляєте, як ви хочете це узагальнити. Якщо, скажімо, ви хочете змінити текст першого текстового вузла в межах <div>, ви можете зробити щось подібне:

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

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>


1

Ось ще один метод: 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>

ПІДКЛЮЧЕННЯ

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

1

Тут багато чудових відповідей, але вони обробляють лише один текстовий вузол з дітьми. У моєму випадку мені потрібно було оперувати всіма текстовими вузлами та ігнорувати html-діти.

Отже, якщо у нас є такий випадок:

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

Наступний код ми можемо використовувати лише для зміни тексту та ЗБЕРЕЖЕННЯ ЗАМОВЛЕННЯ

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

Ось jsfiddle його роботи


0

Я думаю, ти шукаєш .prependTo ().

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

Ми також можемо вибрати елемент на сторінці і вставити його в іншу:

$ ('h2'). prependTo ($ ('. контейнер'));

Якщо вибраний таким чином елемент буде вставлено в інше місце, він буде переміщений у ціль (не клонований):

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

Якщо є більше одного цільового елемента, однак для кожної цілі після першого буде створено клоновані копії вставленого елемента.


2
Я думаю, що не. Здається, що ОП шукає спосіб змінити текст, а не додати новий текст.
Метт Бал

0

Проста відповідь:

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

0

Проблема з відповіддю Марка полягає в тому, що ви також отримуєте порожні текстові коди. Рішення як плагін jQuery:

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

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

0

Це старе питання, але ви можете зробити просту функцію, як ця, щоб полегшити ваше життя:

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

Приклад:

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

Використання:

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

0

Вечірня 2019 року - коротка та проста

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

Пояснення

document.querySelector('#your-div-id') використовується для вибору батьків (елемент, текст якого ви збираєтеся змінити)

.childNodes[0] вибирає текстовий вузол

.nodeValue = 'new text' встановлює значення текстового вузла на "новий текст"


Можливо, ця відповідь надихнула коментаря Діна Мартіна. Не можу сказати точно, оскільки я вже багато років використовую це рішення. Я просто подумав, що я повинен розмістити цю ймовірність тут, тому що деякі люди переймаються цим більше, ніж тим, що це найкраще рішення.


2
Ви щойно скопіювали коментар Діна Мартіна з 2014 року і заявили про це як своє рішення на 2019 рік. Також використовуйте непотрібне поєднання jQuery та звичайного Javascript, що, на мою думку, є поганою практикою для гарної читабельності. Останнє ви не розміщували деталей про те, як працює ваше рішення. Це не найскладніше, але це не так, і це, мабуть, досить нові програмісти, які читають це, хто може скористатись додатковим поясненням, наприклад, що ви отримуєте нативний вузол DOM від об’єкта jQuery, отримуючи доступ до індексу масиву.
Марк Байєнс

@MarkBaijens Я думаю, ти можеш мати рацію, коментар Діна Мартіна може бути там, де я отримав цей фрагмент років тому. У будь-якому разі, я використовую його з тих пір і просто скопіював це з мого персонального коду репо (де я перелічу корисні сценарії) у січні. Додано більш детальне пояснення та видалено зайвий jQuery.
Silver Ringvee

Це, безумовно, найпростіше рішення, яке насправді працює, і люди повинні його використовувати, хоча ви не отримаєте жодних кудосів, щоб заставити його як власне, не надаючи кредиту Діну Мартіну. Я також погоджуюся з Марком Бейенсом, що чиста реалізація Vanilla JS в цьому випадку краще, ніж змішування jQuery і Vanilla JS. Не тільки для кращої читабельності, але й для покращення продуктивності, оскільки jQuery уповільнює роботу.
Miqi180

@ Miqi180 - як я вже говорив вище, це можливо, я не пам'ятаю, де я вперше отримав це рішення. Я вже багато років використовую його у своїй роботі. У відповідь додано примітку. Сподіваємось, це допомагає людям на кшталт вас, котрим не
цікаво
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.