Использование .text () для извлечения только текста, не вложенного в дочерние теги

341

Если у меня есть html:

<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>

Я пытаюсь использовать .text() для извлечения только строки "Это какой-то текст", но если бы я сказал $('#list-item').text(), я получаю "Это текст textFirst span textSecond span".

Есть ли способ получить (и, возможно, удалить через нечто вроде .text("")) только свободный текст внутри тега, а не текст в его дочерних тегах?

HTML не был написан мной, поэтому я должен работать. Я знаю, что было бы просто просто обернуть текст в тегах при написании html, но опять же, html предварительно написан.

  • 0
    К сожалению, правильный ответ похоронен внизу. Здесь нет необходимости в jQuery: document.getElementById ("listItem"). ChildNodes [0] .nodeValue;
Теги:
tags
text

23 ответа

437
Лучший ответ

Мне понравилась эта многократно используемая реализация, основанная на методе clone() найденном здесь, чтобы получить только текст внутри родительского элемента.

Код предоставлен для удобства пользования:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text();
  • 4
    С этим решением вы получаете только текст без дочернего элемента, но вы не можете заменить только текст.
  • 1
    Я не получаю 1 вещь: если .end () возвращается к выбранному элементу, то text () должен скопировать оригинальный текст с дочерними элементами. Но на практике я вижу, что текст с нашего манипулируемого клона копируется. Итак, end () возвращается к clone ()?
Показать ещё 15 комментариев
304

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

$("#listItem").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with" 
  • 33
    Я не понимаю, почему эффективные ответы (которые не генерируют посторонние структуры данных) оцениваются не так часто, как ответы, которые выглядят менее пугающими. +5 если бы мог.
  • 11
    простой и эффективный ответ
Показать ещё 12 комментариев
122

Это похоже на случай чрезмерного использования jquery для меня. Следующее возьмет текст, игнорируя другие узлы:

document.getElementById("listItem").childNodes[0];

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

ИЗМЕНИТЬ

Вышеприведенный текст получит node. Чтобы получить фактический текст, используйте это:

document.getElementById("listItem").childNodes[0].nodeValue;
  • 26
    Лучший ответ, вам не нужен плагин для этого или цепочка из 10 вызовов jQuery. $('.foo')[0].childNodes[0].nodeValue.trim()
  • 5
    Что если текстовое содержимое разбито на несколько узлов (например, последовательность crlf, text, crlf)? Существуют ли какие-либо (rael-life) гарантии того, что dom, построенный ua, будет использовать простейшую структуру?
Показать ещё 10 комментариев
50

Проще и быстрее:

$("#listItem").contents().get(0).nodeValue
  • 0
    Совместим ли этот кросс-браузер?
  • 0
    Конечно, он извлекает один из элементов, соответствующих объекту jQuery, заданному индексом: Jquery Docs .get () .
Показать ещё 2 комментария
25

Подобно принятому ответу, но без клонирования:

$("#foo").contents().not($("#foo").children()).text();

И для этого используется плагин jQuery:

$.fn.immediateText = function() {
    return this.contents().not(this.children()).text();
};

Вот как использовать этот плагин:

$("#foo").immediateText(); // get the text without children
  • 0
    Что такое t в t.children ()?
  • 0
    t это $ ("# foo"). Я обновлю ответ, спасибо!
Показать ещё 4 комментария
7

не является кодом:

var text  =  $('#listItem').clone().children().remove().end().text();

просто становится jQuery для jQuery саке? Когда простые операции включают в себя множество цепочечных команд и такую ​​(ненужную) обработку, возможно, пришло время написать расширение jQuery:

(function ($) {
    function elementText(el, separator) {
        var textContents = [];
        for(var chld = el.firstChild; chld; chld = chld.nextSibling) {
            if (chld.nodeType == 3) { 
                textContents.push(chld.nodeValue);
            }
        }
        return textContents.join(separator);
    }
    $.fn.textNotChild = function(elementSeparator, nodeSeparator) {
    if (arguments.length<2){nodeSeparator="";}
    if (arguments.length<1){elementSeparator="";}
        return $.map(this, function(el){
            return elementText(el,nodeSeparator);
        }).join(elementSeparator);
    }
} (jQuery));

для вызова:

var text = $('#listItem').textNotChild();

аргументы в случае возникновения другого сценария, например

<li>some text<a>more text</a>again more</li>
<li>second text<a>more text</a>again more</li>

var text = $("li").textNotChild(".....","<break>");

текст будет иметь значение:

some text<break>again more.....second text<break>again more
  • 1
    Ницца. Как насчет сделать этот запрос для следующей версии jQuery?
6

Попробуйте следующее:

$('#listItem').not($('#listItem').children()).text()
  • 0
    Отличное решение, спасибо - палец вверх!
6

Это должно быть нечто, соответствующее потребностям, которые зависят от структуры, которую вы представили. Для примера, который вы предоставили, это работает:

$(document).ready(function(){
     var $tmp = $('#listItem').children().remove();
     $('#listItem').text('').append($tmp);
});

Демо: http://jquery.nodnod.net/cases/2385/run

Но это довольно зависит от того, что разметка похожа на то, что вы разместили.

  • 2
    Будущий читатель, будьте осторожны: код в этом ответе убивает детей в фактическом элементе. Здесь следует использовать метод clone если это не намеченный эффект.
  • 0
    @ Ответ DotNetWala, ниже, и должен использоваться вместо этого. Или, по крайней мере, используйте метод .detach() вместо .remove() .
4
$($('#listItem').contents()[0]).text()

Короткий вариант Ответ Стюарта.

или с get()

$($('#listItem').contents().get(0)).text()
3
jQuery.fn.ownText = function () {
    return $(this).contents().filter(function () {
        return this.nodeType === Node.TEXT_NODE;
    }).text();
};
  • 1
    Спасибо за этот фрагмент кода, который может оказать некоторую немедленную помощь. Правильное объяснение значительно повысило бы его образовательную ценность, поскольку показало бы, почему это хорошее решение проблемы, и сделало бы его более полезным для будущих читателей с похожими, но не идентичными вопросами. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснение и указать, какие ограничения и предположения применяются.
2

Я предполагаю, что это тоже прекрасное решение - если вы хотите получить содержимое всех текстовых узлов, которые являются прямыми дочерними элементами выбранного элемента.

$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();

Примечание. Документация jQuery использует аналогичный код для объяснения функции содержимого: https://api.jquery.com/contents/

P.S. Там также немного уродливый способ сделать это, но это показывает более подробно, как все работает, и позволяет настраивать разделитель между текстовыми узлами (возможно, вы хотите, чтобы там была разбита строка)

$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");
2

Это старый вопрос, но главный ответ очень неэффективен. Здесь лучшее решение:

$.fn.myText = function() {
    var str = '';

    this.contents().each(function() {
        if (this.nodeType == 3) {
            str += this.textContent || this.innerText || '';
        }
    });

    return str;
};

И просто сделайте это:

$("#foo").myText();
  • 1
    Я с тобой согласен! спасибо большое, ты спас мне жизнь ...
0

Если index позиции текстового узла фиксирован среди его братьев и сестер, вы можете использовать

$('parentselector').contents().eq(index).text()
0

Точно так же, как вопрос, я пытался извлечь текст, чтобы сделать некоторую подстановку текста в регулярном выражении, но у меня были проблемы, когда мои внутренние элементы (то есть: <i>, <div>, <span> и т.д.) Также получали удален.

Следующий код, кажется, работает хорошо и решил все мои проблемы.

Он использует некоторые ответы, представленные здесь, но, в частности, будет заменять текст только тогда, когда элемент имеет nodeType === 3.

$(el).contents().each(function() { 
  console.log(" > Content: %s [%s]", this, (this.nodeType === 3));

  if (this.nodeType === 3) {
    var text = this.textContent;
    console.log(" > Old   : '%s'", text);

    regex = new RegExp("\\[\\[" + rule + "\\.val\\]\\]", "g");
    text = text.replace(regex, value);

    regex = new RegExp("\\[\\[" + rule + "\\.act\\]\\]", "g");
    text = text.replace(regex, actual);

    console.log(" > New   : '%s'", text);
    this.textContent = text;
  }
});

То, что сделано выше, это циклическое прохождение всех элементов данного el (которое было просто получено с помощью $("div.my-class[name='some-name']"); Для каждого внутреннего элемента оно в основном игнорирует их Для каждой части текста (как определено в if (this.nodeType === 3)) будет применена замена регулярного выражения только к этим элементам.

Часть this.textContent = text просто заменяет замещенный текст, который в моем случае я искал токены типа [[min.val]], [[max.val]] и т.д.

Эта выдержка из короткого кода поможет любому, кто пытается сделать то, о чем спрашивал вопрос... и немного больше.

0

Я предлагаю использовать createTreeWalker, чтобы найти все элементы текста, не прикрепленные к элементам html (эта функция может использоваться для расширения jQuery)

function textNodesOnlyUnder(el) {
  var resultSet = [];
  var n = null;
  var treeWalker  = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, function (node) {
    if (node.parentNode.id == el.id && node.textContent.trim().length != 0) {
      return NodeFilter.FILTER_ACCEPT;
    }
    return NodeFilter.FILTER_SKIP;
  }, false);
  while (n = treeWalker.nextNode()) {
    resultSet.push(n);
  }
  return resultSet;
}



window.onload = function() {
  var ele = document.getElementById('listItem');
  var textNodesOnly = textNodesOnlyUnder(ele);
  var resultingText = textNodesOnly.map(function(val, index, arr) {
    return 'Text element N. ' + index + ' --> ' + val.textContent.trim();
  }).join('\n');
  document.getElementById('txtArea').value = resultingText;
}
<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>
<textarea id="txtArea" style="width: 400px;height: 200px;"></textarea>
0

Я придумал конкретное решение, которое должно быть намного более эффективным, чем клонирование и модификация клона. Это решение работает только со следующими двумя оговорками, но должно быть более эффективным, чем принятое в настоящее время решение:

  • Вы получаете только текст
  • Текст, который вы хотите извлечь, находится перед дочерними элементами

С учетом сказанного здесь приведен код:

// 'element' is a jQuery element
function getText(element) {
  var text = element.text();
  var childLength = element.children().text().length;
  return text.slice(0, text.length - childLength);
}
0

просто поместите его в <p> или <font> и возьмите этот $('# listItem font'). text()

Первое, что пришло в голову

<li id="listItem">
    <font>This is some text</font>
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>
  • 5
    У меня нет контроля над размещением свободного текста в тегах, потому что код, над которым я работаю, не был создан мной. Если бы я мог взять только этот текст, я мог бы удалить его и заменить его тегами вокруг него или сделать все, что захочу. Но опять же, HTML уже написан заранее.
  • 0
    Ах хорошо. Тогда я думаю, что вам придется фильтровать результаты: S извините.
-1

Вы можете попробовать это

alert(document.getElementById('listItem').firstChild.data)
-2

Чтобы иметь возможность обрезать результат, используйте DotNetWala следующим образом:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text()
    .trim();

Я узнал, что использование более короткой версии, такой как document.getElementById("listItem").childNodes[0], не будет работать с jQuery trim().

  • 3
    Это связано с тем, что document.getElementById("listItem").childNodes[0] представляет собой простой javascript, вам нужно будет обернуть его в функцию jQuery $(document.getElementById("listItem").childNodes[0]).trim()
  • 0
    Хорошо, это имеет смысл. Ха-ха. Спасибо!
Показать ещё 1 комментарий
-2

Используйте дополнительное условие для проверки того, являются ли innerHTML и innerText одинаковыми. Только в этих случаях замените текст.

$(function() {
$('body *').each(function () {
    console.log($(this).html());
    console.log($(this).text());
    if($(this).text() === "Search" && $(this).html()===$(this).text())  {
        $(this).html("Find");
    }
})
})

http://jsfiddle.net/7RSGh/

-2

Это хороший способ для меня

   var text  =  $('#listItem').clone().children().remove().end().text();
  • 0
    Это точно так же, как ответ DotNetWala.
-3

Я не эксперт jquery, но как насчет

$('#listItem').children().first().text()
  • 1
    Если вы заметили эксперта по jquery, то почему бы не стать большим экспертом, прочитав сначала другие ответы? ... Один из них оказался практически таким же, как вы написали, с комментариями ниже, объясняющими, почему это не так. хорошая идея.
-4

Это не проверено, но я думаю, что вы можете попробовать что-то вроде этого:

 $('#listItem').not('span').text();

http://api.jquery.com/not/

  • 0
    не работает к вашему сведению Я не совсем уверен, почему, хотя хе
  • 3
    Потому что это то же самое, что и $('#listItem').text() . #listItem не является <span> поэтому добавление not('span') ничего не делает.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню