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


当前回答

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

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

其他回答

你可以试试这个

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

没有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'

使用简单的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;
            }
        }
    }
}

不确定有多灵活或多少情况下,你需要它覆盖,但对于你的例子,如果文本总是出现在第一个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)