使用.text()仅检索未嵌套在子标记中的文本


386

如果我有这样的html:

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

我正在尝试.text()只检索字符串“ This is some text”,但是如果要说的话$('#list-item').text(),我会得到“ This is some textFirst span textSecond span text”。

有没有一种方法可以获取(可能通过类似的方法删除.text(""))标签中的自由文本,而不是其子标签中的文本?

HTML不是我编写的,因此这是我必须使用的。我知道在编写html时仅将文本包装在标签中会很简单,但是html是预先编写的。


因为我尚无足够的声誉来发表评论,并且我也不希望丢失该知识(希望对其他人有所帮助),所以我组合了macio.Jun'答案,RegExp和iStranger'答案用HTML替换textNode用Javascript?允许我在纯文本节点中搜索字符串,并用链接替换所有匹配项。
JDQ

Answers:


509

我喜欢基于此处clone()找到的方法的可重用实现仅获取父元素内的文本。

提供的代码易于参考:

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

5
使用此解决方案,您只会得到没有孩子的文本,但不能只替换文本。
BenRoe 2012年

1
我没有得到一件事:如果.end()返回所选元素,则text()应该复制带有子元素的原始文本。但实际上,我看到来自受控克隆的文本正在被复制。那么end()回到clone()吗?

68
这是一种非常低效的方式
billyonecan 2014年

5
@billyonecan,您能建议一种更有效的方法吗?这很吸引人,因为它“干净”且“短”。你有什么建议?
derekmx271 2015年

1
@ derekmx271看看Stuart的答案
billyonecan 2015年

364

简单答案:

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

38
我不明白为什么有效答案(不会生成无关的数据结构)的投票率不及看上去那么吓人的答案。如果可以的话,+ 5。
史蒂文·卢

16
简单有效的答案
Paul Carroll

9
这不仅更有效,而且是正确的!此解决方案适合于文本分散在子元素之间的情况。+5
Kyryll Tenin Baum 2014年

15
更清楚地说,如果您使用IE8 +,则可以使用this.nodeType == Node.TEXT_NODE代替this.nodeType == 3。易于阅读和理解IMO。
NorTicUs 2014年

8
如果将其用于没有文本的内容,则将中断。如果您将此功能用作函数,并且可能会或可能不会有文本,请将该.contents().filter(...)调用捕获到局部变量中并检查其长度,例如,var text = $(this).contents().filter(...); if (text.length) { return text[0].nodeValue; } return "";
Carl Bussema 2015年

157

这似乎是对我过度使用jquery的一种情况。以下内容将忽略其他节点来获取文本:

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

您需要对其进行修剪,但它只需一条简单的代码即可满足您的需求。

编辑

上面将得到text节点。要获取实际的文本,请使用以下命令:

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

31
最佳答案,您不需要为此或一个10个jQuery调用链所需的插件。$('.foo')[0].childNodes[0].nodeValue.trim()
2013年

5
如果将文本内容分成几个节点(如crlf,text,crlf序列)怎么办?是否有(种族寿命)保证由ua构建的dom使用最简单的结构?
collapsar 2013年

5
完全是最好的答案...为什么其他人有时会过度使用jQuery?
ncubica 2014年

11
这仅在<div id =“ listItem”>您要的文本<span>其他</ span> </ div>的情况下有效。它不适用于<div id =“ listItem”> <span>您要的其他</ span>文本</ div>
Spencer

1
有时候你没有document。来这里使用cheerio
Flash


28

与已接受的答案类似,但没有克隆:

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

这是用于此目的的jQuery插件:

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

这是使用此插件的方法:

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

t.children()中的t是什么?
FrekamAn

这是pbjk在1月15日编写的解决方案的重复解决方案...不过-看起来不错。
奥斯卡·霍尔姆克拉茨

1
不是,@ Oskar。这.contents()部分很关键!
DUzun

如果您的节点不使用ID,则是不好的解决方案。
AndroidDev

3
@AndroidDev您始终可以使用适合您的选项替换选择器。这只是为了说明技术!我还添加了一个插件版本以显示它即使没有ID也可以工作
DUzun

8

不是代码:

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版本的请求请求?
Jared Tomaszewski 2014年

8

尝试这个:

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

6

它将需要根据需求量身定制,这取决于您所呈现的结构。对于您提供的示例,此方法有效:

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

演示:http : //jquery.nodnod.net/cases/2385/run

但这很大程度上取决于标记与您发布的标记相似。


2
未来的读者要当心:此答案中的代码杀死了实际元素中的子元素。clone如果这不是预期的效果,则应在此处使用该方法。
曼恩

@DotNetWala的答案在下面,应该代替此答案。或至少使用.detach()方法代替.remove()
Don McCurdy 2013年


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

1
感谢您提供此代码段,它可能会立即提供帮助。通过说明为什么这是一个解决问题的好方法,适当的解释将大大提高其教育价值,并且对将来有相似但不相同的问题的读者来说更有用。请编辑您的答案以添加说明,并指出适用的限制和假设。
Toby Speight

3

这是一个老问题,但最重要的答案是效率很低。这是一个更好的解决方案:

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

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

    return str;
};

只需这样做:

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

3

我想这也是一个很好的解决方案-如果您想获取所有文本节点的内容,这些文本节点是所选元素的直接子代。

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

注意:jQuery文档使用类似的代码来解释内容功能:https : //api.jquery.com/contents/

PS还有一种比较丑陋的方法,但是它更深入地显示了工作原理,并允许在文本节点之间自定义分隔符(也许您希望在其中换行)

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

1

我建议使用createTreeWalker查找未附加到html元素的所有texts元素(此函数可用于扩展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>


1

如果index文本节点的位置在同级节点之间固定,则可以使用

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

1

不确定是否需要灵活处理,或者需要涵盖多少种情况,但是以您的示例为例,如果文本始终位于第一个HTML标记之前–为什么不将内部html拆分为第一个标记并采用前一个标记:

$('#listItem').html().split('<span')[0]; 

如果您需要更广泛的

$('#listItem').html().split('<')[0]; 

并且如果您需要两个标记之间的文本(例如在一件事之后但又在另一件事之前),则可以执行(未测试)之类的操作,并使用if语句使其具有足够的灵活性以使其具有开始或结束标记,或者同时具有两者,同时避免空引用错误:

var startMarker = '';// put any starting marker here
var endMarker = '<';// put the end marker here
var myText = String( $('#listItem').html() );
// if the start marker is found, take the string after it
myText = myText.split(startMarker)[1];        
// if the end marker is found, take the string before it
myText = myText.split(endMarker)[0];
console.log(myText); // output text between the first occurrence of the markers, assuming both markers exist.  If they don't this will throw an error, so some if statements to check params is probably in order...

我通常使实用程序函数用于诸如此类的有用的事情,使它们无错误,然后可靠地频繁使用它们,而不是总是重写这种类型的字符串操作并冒空引用之类的风险。这样,您就可以重新使用该函数在许多项目中,不必浪费时间再调试为什么字符串引用具有未定义的引用错误。可能不是有史以来最短的1行代码,但是有了实用程序功能之后,它就是一行。请注意,大多数代码只是在处理是否存在参数,以避免出现错误:)

例如:

/**
* Get the text between two string markers.
**/
function textBetween(__string,__startMark,__endMark){
    var hasText = typeof __string !== 'undefined' && __string.length > 0;
    if(!hasText) return __string;
    var myText = String( __string );
    var hasStartMarker = typeof __startMark !== 'undefined' && __startMark.length > 0 && __string.indexOf(__startMark)>=0;
    var hasEndMarker =  typeof __endMark !== 'undefined' && __endMark.length > 0 && __string.indexOf(__endMark) > 0;
    if( hasStartMarker )  myText = myText.split(__startMark)[1];
    if( hasEndMarker )    myText = myText.split(__endMark)[0];
    return myText;
}

// now with 1 line from now on, and no jquery needed really, but to use your example:
var textWithNoHTML = textBetween( $('#listItem').html(), '', '<'); // should return text before first child HTML tag if the text is on page (use document ready etc)

如果您需要替换文字,请使用$('#listItem').html( newHTML ); 其中newHTML是一个已经包含精简文本的变量。
OG肖恩


0

我想出了一个特定的解决方案,该解决方案应该比克隆和修改克隆的效率更高。此解决方案仅适用于以下两个保留,但应比当前接受的解决方案更有效:

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

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

这段简短的代码摘录将帮助任何试图做这个问题的人……等等。


-1

只是把它放在一个<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>

6
我无法控制将自由文本放入标签中,因为我要处理的代码不是我创建的。如果我只能抓取该文本,则可以将其删除并替换为周围的标签,或者执行我想做的任何事情。但是同样,html已经被预先编写。
MegaMatt 2010年

喔好吧。然后,我认为您将不得不过滤结果:S对不起。

-1

你可以试试这个

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

-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

为了能够修剪结果,请像下面这样使用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 这样的较短版本不适用于jQuery的trim()。


3
那是因为document.getElementById("listItem").childNodes[0]是普通的javascript,您必须将其包装在jQuery函数中$(document.getElementById("listItem").childNodes[0]).trim()
Red Taz 2015年

好的,这很有意义。哈哈。谢谢!
马里恩(Marion Go)

1
这几乎与DotNetWala的答案相同。您所做的全部添加.trim()到最后。这个答案有必要吗?
所有工人都

-3

我不是jquery专家,但是如何,

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

1
如果您是一名jQuery专家,那么为什么不先阅读其他答案就成为更多的专家呢?...其中一个恰好与您撰写的内容相同,下面的注释说明了为什么不这样一个好主意。
奥斯卡·霍尔姆克拉茨

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.