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

если у меня есть 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(), Я получаю "это текстовый текст textSecond span".

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

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

22 ответов


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

код для ссылки:

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

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

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

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

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

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

редактировать

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

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

легче и быстрее:

$("#listItem").contents().get(0).nodeValue

похож на принятый ответ, но без клонирования:

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

и вот плагин jQuery для этой цели:

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

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

$("#foo").immediateText(); // get the text without children

не код:

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

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

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

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

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


попробуйте это:

$('#listItem').not($('#listItem').children()).text()

$($('#listItem').contents()[0]).text()

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

или get()

$($('#listItem').contents().get(0)).text()

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

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

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

    return str;
};

и просто сделать это:

$("#foo").myText();

jQuery.fn.ownText = function () {
    return $(this).contents().filter(function () {
        return this.nodeType === Node.TEXT_NODE;
    }).text();
};

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

$(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("");

просто поместите его в <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>

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

  1. вы получаете только текст
  2. текст, который вы хотите извлечь перед дочерними элементами

С учетом сказанного, вот код:

// '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);
}

Я предлагаю использовать 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>

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

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

он использует некоторые из ответов, представленных здесь, но, в частности, будет заменять текст только тогда, когда элемент 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)) он будет применять подстановку regex только к этим элементам.

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

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


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

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

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

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

чтобы обрезать результат, используйте 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] не будет работать с trim () jQuery.


используйте дополнительное условие, чтобы проверить, совпадают ли 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/


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

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

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


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

$('#listItem').children().first().text()