如果我有这样的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是预先编写的。


只需将它放在<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>

它需要是根据需求量身定制的,这取决于你所看到的结构。对于你提供的例子,这是有效的:

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

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

但这取决于标记是否与你发布的内容相似。


这是未经测试的,但我认为你可以尝试这样做:

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

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


对我来说,这似乎是一个过度使用jquery的例子。下面将抓取文本,忽略其他节点:

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

你需要修剪它,但它能让你在一个简单的线条中得到你想要的。

EDIT

上面将获得文本节点。要得到实际的文本,使用这个:

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

我喜欢这个基于clone()方法的可重用实现,它只获取父元素中的文本。

为方便参考而提供的代码:

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

这对我来说是个好方法

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

代码不是:

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

简单的回答是:

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

使用一个额外的条件来检查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/


你可以试试这个

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

更容易和更快:

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

我提出了一个具体的解决方案,应该比克隆和修改克隆更有效。这个解决方案只适用于以下两个保留,但应该比目前接受的解决方案更有效:

你得到的只是文本 要提取的文本位于子元素之前

话虽如此,下面是代码:

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

为了能够修剪结果,像这样使用DotNetWala's:

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


试试这个:

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

这是一个老问题,但上面的答案效率很低。这里有一个更好的解决方案:

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

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

    return str;
};

然后这样做:

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

类似于公认的答案,但没有克隆:

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

下面是一个jQuery插件用于此目的:

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

下面是如何使用这个插件:

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

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

斯图尔特回答的简短变种。

或者使用get()

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

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

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

我建议使用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>


我认为这也将是一个很好的解决方案-如果你想获得所有文本节点的内容是选定元素的直接子。

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

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

附注:还有一种更难看的方法,但这更深入地展示了事情是如何工作的,并允许在文本节点之间自定义分隔符(也许你想在那里换行)

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

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

就像问题一样,我试图提取文本,以便对文本进行一些正则表达式替换,但在我的内部元素(即:< 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),它将只对这些元素应用regex替换。

这。textContent = text部分只是替换替换的文本,在我的情况下,我正在寻找像[[min.val]], [[max.]]这样的标记。val]]等。

这段简短的代码摘录将帮助任何人试图做什么问题是问…再多一点。


如果文本节点的位置索引在其兄弟节点中是固定的,则可以使用

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

不确定有多灵活或多少情况下,你需要它覆盖,但对于你的例子,如果文本总是出现在第一个HTML标签之前-为什么不只是在第一个标签上分割内部HTML,并采取前者:

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

如果你需要更宽一点

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

如果你需要两个标记之间的文本,比如在一件事之后,但在另一件事之前,你可以做一些像(untesting)这样的事情,并使用if语句使它足够灵活,有一个开始或结束标记,或两者都有,同时避免null ref错误:

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

I generally make utility functions for useful things like this, make them error free, and then rely on them frequently once solid, rather than always rewriting this type of string manipulation and risking null references etc. That way, you can re-use the function in lots of projects and never have to waste time on it again debugging why a string reference has an undefined reference error. Might not be the shortest 1 line code ever, but after you have the utility function, it is one line from then on. Note most of the code is just handling parameters being there or not to avoid errors :)

例如:

/**
* 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)

使用简单的JavaScript在IE 9+兼容语法在短短几行:

const childNodes = document.querySelector('#listItem').childNodes;

if (childNodes.length > 0) {
    childNodesLoop:
    for (let i = 0; i < childNodes.length; i++) {
        //only target text nodes (nodeType of 3)
        if (childNodes[i].nodeType === 3) {
            //do not target any whitespace in the HTML
            if (childNodes[i].nodeValue.trim().length > 0) {
                childNodes[i].nodeValue = 'Replacement text';
                //optimized to break out of the loop once primary text node found
                break childNodesLoop;
            }
        }
    }
}

现场演示

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

<input id="input" style="width: 300px; margin-top: 10px;">

    <script type="text/javascript">
$("#input").val($("#listItem").clone().find("span").remove().end().text().trim());
    //use .trim() to remove any white space
    </script>

对于初学者来说:

我更喜欢@都尊的答案,因为它简单易懂,比公认的答案更有效率。但它只部分适用于我,因为你不能直接传递元素与类选择器像这样

$(".landing-center .articlelanding_detail").get(0).immediateText() //gives .immediateText is not a function error

或者这个

$(".landing-center .articlelanding_detail")[0].immediateText() //gives .immediateText is not a function error

因为一旦你从$()函数中使用[index]或.get(index)提取了原生元素,你就失去了这里提到的jQuery对象方法的可链性。大多数解决方案只在id的上下文中使用,对于带有类选择器的元素多次使用不是很优雅。

所以,我写了jQuery插件:

$.fn.mainText = function(x=0) {
    return $.trim(this.eq(x).contents().not(this.eq(x).children()).text().replace(/[\t\n]+/g,' '));
};

这将返回元素的文本,而不管是否使用id或class作为排除子元素的选择器。也将删除任何\t或\n以获得一个干净的字符串。 像这样使用它:

案例1

$("#example").mainText(); // get the text of element with example id

案例2

$(".example").mainText(); // get the text of first element with example class

案例3

$(".example").mainText(1); // get the text of second element with example class and so on..

我不会为此而使用jQuery,尤其是那些对元素进行不必要克隆的解决方案。您所需要的就是一个简单的循环抓取文本节点。在现代JavaScript中(在撰写本文时,“现代”是一个移动的目标!),并从结果的开头和结尾删除空白:

const { childNodes } = document.getElementById("listItem");
let text = "";
for (const node of childNodes) {
    if (node.nodeType === Node.TEXT_NODE) {
        text += node.nodeValue;
    }
}
text = text.trim();

生活例子:

const {childNodes} = document.getElementById("listItem"); Let text = ""; (childNodes的const节点){ 如果节点。nodeType === Node.TEXT_NODE) { text += node.nodeValue; } } console.log(文本); <李id = "列" > 这是一些文本 <span id="firstSpan">第一个span文本</span> <span id="secondSpan">Second span text</span> . <span id="secondSpan"> 李< / >

有些人会用reduce来表示。我不是一个粉丝,我认为一个简单的循环更清楚,但这种用法确实在每次迭代时更新累加器,所以它实际上并没有滥用reduce:

const { childNodes } = document.getElementById("listItem");
const text = [...childNodes].reduce((text, node) =>
    node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text
, "").trim();

const {childNodes} = document.getElementById("listItem"); const text =[…childNodes]。Reduce ((text, node) => 节点。nodeType ===节点。TEXT_NODE吗?文本+节点。nodeValue:文本 ”、“).trim (); console.log(文本); <李id = "列" > 这是一些文本 <span id="firstSpan">第一个span文本</span> <span id="secondSpan">Second span text</span> . <span id="secondSpan"> 李< / >

或者不创建临时数组:

const { childNodes } = document.getElementById("listItem");
const text = Array.prototype.reduce.call(childNodes, (text, node) =>
    node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text
, "").trim();

const {childNodes} = document.getElementById("listItem"); const text = Array.prototype.reduce。call(childNodes, (text, node) => 节点。nodeType ===节点。TEXT_NODE吗?文本+节点。nodeValue:文本 ”、“).trim (); console.log(文本); <李id = "列" > 这是一些文本 <span id="firstSpan">第一个span文本</span> <span id="secondSpan">Second span text</span> . <span id="secondSpan"> 李< / >


没有JQuery的替代版本的答案

[...document.getElementById("listItem").childNodes].find(c => c.nodeType === Node.TEXT_NODE).nodeValue

在2022年获取一个元素中的所有文本而没有任何子元素中的文本似乎仍然不是简单的事情。 但是不需要jQuery。

获取所有原始文本节点(s)内容:

const getElementTextWithoutChildElements = (el) =>
  Array.from(el.childNodes)               // iterator to array
    .filter(node => node.nodeType === 3)  // only text nodes
    .map(node => node.textContent)        // get text
    .join('')                             // stick together
;

或者类似的,使用reduce:

const getElementTextWithoutChildElements = (el) =>
  [].reduce.call(
    el.childNodes, 
    (a, b) => a + (b.nodeType === 3 ? b.textContent : ''),
    ''
  );

应该这样做:

<div>
  you get this
  <b>not this</b>
  you get this   too
</div>

将返回:


  you get this

  you get this   too

元素之间的空白可能很棘手,建议使用.trim()和/或规范化所有空白。 对于调试和记录快速识别元素,我发现这通常是足够的:

getElementTextWithoutChildElements(...).replace(/\s+/g, ' ').trim();
// 'you get this you get this too'

尽管您可能希望以不同的方式调整空白,但可以在reduce()函数本身中处理每个节点的空白。

例如,每个节点的空格处理:

const getElementTextWithoutChildElements_2 = (el) =>
  Array.from(el.childNodes)
    .filter(node => node.nodeType === 3)
    .map(node => node.textContent.trim()) // added .trim()
    .join(',')                            // added ','
;

以上内容的快速测试:

document.body.innerHTML = `
  you get this
  <b>not this</b>
  you get this   too
`;
// '\n  you get this\n  <b>not this</b>\n  you get this   too\n'

getElementTextWithoutChildElements(document.body);
// '\n  you get this\n  \n  you get this   too\n'

getElementTextWithoutChildElements(document.body).replace(/\s+/g, ' ').trim();
// 'you get this you get this too'

getElementTextWithoutChildElements_2(document.body);
// 'you get this,you get this   too'