Как я могу изменить текст элемента, не меняя его дочерние элементы?

120

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

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

Я новичок в jQuery, поэтому эта задача кажется мне довольно сложной. Может ли кто-нибудь указать мне функцию / селектор для использования?

Если возможно, я бы хотел сделать это, не добавляя новый контейнер для текста, который мне нужно изменить.

ика
источник
7
«Если возможно, я бы хотел сделать это, не добавляя новый контейнер для текста, который мне нужно изменить». - действительно? Потому что с контейнером было бы намного проще.
Пол Д. Уэйт
Ребята, я посмотрел на исходный вопрос и не думаю, что OP сказал что-либо о дочерних элементах ...
Шиме Видас
Согласно стандартам 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свойство.
Тим Даун
@ Тим: а, я подумал, что должен быть более простой способ. Никогда не делал много JavaScript до появления jQuery. Ура, я соответствующим образом отредактирую ответ.
Пол Д. Уэйт
11
Таким образом, более современная реализация этого ответа будет$('#yourDivId')[0].childNodes[0].nodeValue = 'New Text';
Дин Мартин
Безусловно, самое короткое и простое решение - stackoverflow.com/a/54365288/4769218
Silver Ringvee
1
«Возможно, вы тоже сможете сделать это в обычном javascript»
Духайм
65

Обновление 2018

Поскольку это довольно популярный ответ, я решил немного обновить и украсить его, добавив селектор текстового узла в jQuery в качестве плагина.

Во фрагменте ниже вы можете видеть, что я определяю новую функцию jQuery, которая получает все (и только) textNodes. Вы можете связать эту функцию, например, с first()функцией. Я обрезаю текстовый узел и проверяю, не пуст ли он после обрезки, потому что пробелы, табуляции, новые строки и т. Д. Также распознаются как текстовые узлы. Если вам тоже нужны эти узлы, просто удалите их из оператора if в функции jQuery.

Я добавил пример, как заменить первый текстовый узел и как заменить все текстовые узлы.

Такой подход упрощает чтение кода и упрощает его многократное использование с разными целями.

Update 2017 (adrach) должно работать, а если вы предпочитаете.

Как расширение jQuery

Эквивалент Javascript (ES)


Обновление 2017 (адрах):

Похоже, с тех пор, как это было опубликовано, несколько вещей изменились. Вот обновленная версия

$("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 ("мой текст" );
Maragues
любой, кому это интересно 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
Однако это не сохранит порядок узлов. например, если бы текст был в конце элемента первым, теперь он будет в начале.
Мэтт
Он не сохранит порядок, но в большинстве случаев вы хотите, чтобы текст был первым или последним узлом. Поэтому, если append () не дает желаемого результата, попробуйте 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')
MrBojangles
источник
Это лучшее решение!
Sleek Geek,
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>

Jquery

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 его работы

user1254723
источник
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>

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

Pitx3
источник
2
Думаю, нет. Похоже, OP ищет способ изменить текст, а не добавить новый текст.
Мэтт Болл
0

Простой ответ:

$("div").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with"
macio.Jun
источник
0

Проблема с ответом Марка в том, что вы также получаете пустые текстовые узлы. Решение как плагин jQuery:

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

$("div").textnodes()[0] = "changed text";
br4nnigan
источник
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");
rotaercz
источник
0

Версия 2019 - Short & Simple

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 - как я уже сказал выше, это возможно, не помню, где впервые взял это решение. Я использую его в своей работе уже много лет. Добавил примечание в ответ. Надеюсь, это поможет таким людям, как вы, которым наплевать на лучшее решение здесь, на SO:
Silver Ringvee