Javascript 如何获取元素的文本节点?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6520192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 22:08:22  来源:igfitidea点击:

How to get the text node of an element?

javascriptjquery

提问by Val

<div class="title">
   I am text node
   <a class="edit">Edit</a>
</div>

I wish to get the "I am text node", do not wish to remove the "edit" tag, and need a cross browser solution.

我希望获得“我是文本节点”,不希望删除“编辑”标签,并且需要跨浏览器解决方案。

采纳答案by James Allardice

var text = $(".title").contents().filter(function() {
  return this.nodeType == Node.TEXT_NODE;
}).text();

This gets the contentsof the selected element, and applies a filter function to it. The filter function returns only text nodes (i.e. those nodes with nodeType == Node.TEXT_NODE).

这将获取contents所选元素的 ,并对其应用过滤器功能。filter 函数只返回文本节点(即那些带有 的节点nodeType == Node.TEXT_NODE)。

回答by Dogbert

You can get the nodeValue of the first childNode using

您可以使用获取第一个 childNode 的 nodeValue

$('.title')[0].childNodes[0].nodeValue

http://jsfiddle.net/TU4FB/

http://jsfiddle.net/TU4FB/

回答by Shadow Wizard is Ear For You

If you mean get the value of the first text node in the element, this code will work:

如果您的意思是获取元素中第一个文本节点的值,则此代码将起作用:

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    if (curNode.nodeName === "#text") {
        firstText = curNode.nodeValue;
        break;
    }
}

You can see this in action here: http://jsfiddle.net/ZkjZJ/

你可以在这里看到这个:http: //jsfiddle.net/ZkjZJ/

回答by Yuval A.

Another native JS solution that can be useful for "complex" or deeply nested elements is to use NodeIterator. Put NodeFilter.SHOW_TEXTas the second argument ("whatToShow"), and iterate over just the text node children of the element.

另一种可用于“复杂”或深度嵌套元素的原生 JS 解决方案是使用NodeIterator。将NodeFilter.SHOW_TEXT作为第二个参数(“whatToShow”),并逐一只是元素的文本节点孩子。

var root = document.querySelector('p'),
    iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT),
    textnode;

// print all text nodes
while (textnode = iter.nextNode()) {
  console.log(textnode.textContent)
}
<p>
<br>some text<br>123
</p>

You can also use TreeWalker. The difference between the two is that NodeIteratoris a simple linear iterator, while TreeWalkerallows you to navigate via siblings and ancestors as well.

您也可以使用TreeWalker. 两者之间的区别在于它NodeIterator是一个简单的线性迭代器,同时TreeWalker允许您通过兄弟姐妹和祖先进行导航。

回答by Anthony Rutledge

Pure JavaScript: Minimalist

纯 JavaScript:极简主义

First off, always keep this in mind when looking for text in the DOM.

首先,在 DOM 中查找文本时始终牢记这一点。

MDN - Whitespace in the DOM

MDN - DOM 中的空白

This issue will make you pay attention to the structure of your XML / HTML.

这个问题会让你注意你的 XML/HTML 的结构。

In this pure JavaScript example, I account for the possibility of multiple text nodesthat could be interleaved with other kinds of nodes. However, initially, I do not pass judgment on whitespace, leaving that filtering task to other code.

在这个纯 JavaScript 示例中,我考虑了多个文本节点可能与其他类型的节点交错可能性。但是,最初,我不会对空格进行判断,而是将过滤任务留给其他代码。

In this version, I pass a NodeListin from the calling / client code.

在这个版本中,我NodeList从调用/客户端代码传入。

/**
* Gets strings from text nodes. Minimalist. Non-robust. Pre-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length; // Because you may have many child nodes.

    for (var i = 0; i < length; i++) {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
        }
    }

    return null;
}

Of course, by testing node.hasChildNodes()first, there would be no need to use a pre-test forloop.

当然,通过node.hasChildNodes()先测试,就不需要使用预测试for循环。

/**
* Gets strings from text nodes. Minimalist. Non-robust. Post-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length,
        i = 0;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
         }

        i++;
    } while (i < length);

    return null;
}


Pure JavaScript: Robust

纯 JavaScript:健壮

Here the function getTextById()uses two helper functions: getStringsFromChildren()and filterWhitespaceLines().

这里的函数getTextById()使用了两个辅助函数:getStringsFromChildren()filterWhitespaceLines()



getStringsFromChildren()

getStringsFromChildren()

/**
* Collects strings from child text nodes.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @version 7.0
* @param parentNode An instance of the Node interface, such as an Element. object.
* @return Array of strings, or null.
* @throws TypeError if the parentNode is not a Node object.
*/
function getStringsFromChildren(parentNode)
{
    var strings = [],
        nodeList,
        length,
        i = 0;

    if (!parentNode instanceof Node) {
        throw new TypeError("The parentNode parameter expects an instance of a Node.");
    }

    if (!parentNode.hasChildNodes()) {
        return null; // We are done. Node may resemble <element></element>
    }

    nodeList = parentNode.childNodes;
    length = nodeList.length;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE)) {
            strings.push(nodeList[i].nodeValue);
         }

        i++;
    } while (i < length);

    if (strings.length > 0) {
        return strings;
    }

    return null;
}


filterWhitespaceLines()

filterWhitespaceLines()

/**
* Filters an array of strings to remove whitespace lines.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param textArray a String associated with the id attribute of an Element.
* @return Array of strings that are not lines of whitespace, or null.
* @throws TypeError if the textArray param is not of type Array.
*/
function filterWhitespaceLines(textArray) 
{
    var filteredArray = [],
        whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

    if (!textArray instanceof Array) {
        throw new TypeError("The textArray parameter expects an instance of a Array.");
    }

    for (var i = 0; i < textArray.length; i++) {
        if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
            filteredArray.push(textArray[i].trim());  // Trimming here is fine. 
        }
    }

    if (filteredArray.length > 0) {
        return filteredArray ; // Leave selecting and joining strings for a specific implementation. 
    }

    return null; // No text to return.
}


getTextById()

getTextById()

/**
* Gets strings from text nodes. Robust.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param id A String associated with the id property of an Element.
* @return Array of strings, or null.
* @throws TypeError if the id param is not of type String.
* @throws TypeError if the id param cannot be used to find a node by id.
*/
function getTextById(id) 
{
    var textArray = null;             // The hopeful output.
    var idDatatype = typeof id;       // Only used in an TypeError message.
    var node;                         // The parent node being examined.

    try {
        if (idDatatype !== "string") {
            throw new TypeError("The id argument must be of type String! Got " + idDatatype);
        }

        node = document.getElementById(id);

        if (node === null) {
            throw new TypeError("No element found with the id: " + id);
        }

        textArray = getStringsFromChildren(node);

        if (textArray === null) {
            return null; // No text nodes found. Example: <element></element>
        }

        textArray = filterWhitespaceLines(textArray);

        if (textArray.length > 0) {
            return textArray; // Leave selecting and joining strings for a specific implementation. 
        }
    } catch (e) {
        console.log(e.message);
    }

    return null; // No text to return.
}

Next, the return value (Array, or null) is sent to the client code where it should be handled. Hopefully, the array should have string elements of real text, not lines of whitespace.

接下来,返回值(数组或空值)被发送到应该处理它的客户端代码。希望数组应该包含真实文本的字符串元素,而不是空白行。

Empty strings ("") are notreturned because you need a text node to properly indicate the presence of valid text. Returning ("") may give the false impression that a text node exists, leading someone to assume that they can alter the text by changing the value of .nodeValue. This is false, because a text node does not exist in the case of an empty string.

不会返回空字符串 ( ""),因为您需要一个文本节点来正确指示有效文本的存在。返回 ( ) 可能会给人一种错误的印象,即存在文本节点,导致有人认为他们可以通过更改 的值来更改文本。这是错误的,因为在空字符串的情况下文本节点不存在。"".nodeValue

Example 1:

示例 1

<p id="bio"></p> <!-- There is no text node here. Return null. -->

Example 2:

示例 2

<p id="bio">

</p> <!-- There are at least two text nodes ("\n"), here. -->

The problem comes in when you want to make your HTML easy to read by spacing it out. Now, even though there is no human readable valid text, there are still text nodes with newline ("\n") charactersin their .nodeValueproperties.

当您希望通过将 HTML 分隔开来使 HTML 易于阅读时,问题就出现了。现在,即使没有人类可读的有效文本,在其属性中仍然存在带有换行符 ( "\n") 字符的文本节点.nodeValue

Humans see examples one and two as functionally equivalent--empty elements waiting to be filled. The DOM is different than human reasoning. This is why the getStringsFromChildren()function must determine if text nodes exist and gather the .nodeValuevalues into an array.

人类将示例 1 和示例 2 视为功能等价的 - 等待填充的空元素。DOM 与人类推理不同。这就是getStringsFromChildren()函数必须确定文本节点是否存在并将.nodeValue值收集到数组中的原因。

for (var i = 0; i < length; i++) {
    if (nodeList[i].nodeType === Node.TEXT_NODE) {
            textNodes.push(nodeList[i].nodeValue);
    }
}

In example two, two text nodes do exist and getStringFromChildren()will return the .nodeValueof both of them ("\n"). However, filterWhitespaceLines()uses a regular expression to filter out lines of pure whitespace characters.

在示例二中,确实存在两个文本节点getStringFromChildren()并将返回.nodeValue它们的 ( "\n")。但是,filterWhitespaceLines()使用正则表达式过滤掉纯空白字符的行。

Is returning nullinstead of newline ("\n") characters a form of lying to the client / calling code? In human terms, no. In DOM terms, yes. However, the issue here is getting text, not editing it.There is no human text to return to the calling code.

返回null而不是换行符 ( "\n") 字符是一种对客户端/调用代码撒谎的形式吗?从人的角度来说,没有。在 DOM 术语中,是的。然而,这里的问题是获取文本,而不是编辑它。没有返回到调用代码的人工文本。

One can never know how many newline characters might appear in someone's HTML. Creating a counter that looks for the "second" newline character is unreliable. It might not exist.

人们永远无法知道在某人的 HTML 中可能出现多少换行符。创建一个寻找“第二个”换行符的计数器是不可靠的。它可能不存在。

Of course, further down the line, the issue of editing textin an empty <p></p>element with extra whitespace (example 2) might mean destroying (maybe, skipping) all but one text node between a paragraph's tags to ensure the element contains precisely what it is supposed to display.

当然,更进一步,在带有额外空格的空元素(示例 2)中编辑文本的问题<p></p>可能意味着销毁(可能,跳过)段落标签之间的所有文本节点,以确保元素准确包含它的内容应该显示。

Regardless, except for cases where you are doing something extraordinary, you will need a way to determine which text node's .nodeValueproperty has the true, human readable text that you want to edit. filterWhitespaceLinesgets us half way there.

无论如何,除非您正在做一些非凡的事情,否则您将需要一种方法来确定哪个文本节点的.nodeValue属性具有您想要编辑的真实的、人类可读的文本。filterWhitespaceLines让我们走到一半。

var whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

for (var i = 0; i < filteredTextArray.length; i++) {
    if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
        filteredTextArray.push(textArray[i].trim());  // Trimming here is fine. 
    }
}

At this point you may have output that looks like this:

此时,您的输出可能如下所示:

["Dealing with text nodes is fun.", "Some people just use jQuery."]

There is no guarantee that these two strings are adjacent to each other in the DOM, so joining them with .join()might make an unnatural composite. Instead, in the code that calls getTextById(), you need to chose which string you want to work with.

不能保证这两个字符串在 DOM 中彼此相邻,因此将它们连接起来.join()可能会产生不自然的组合。相反,在调用 的代码中getTextById(),您需要选择要使用的字符串。

Test the output.

测试输出。

try {
    var strings = getTextById("bio");

    if (strings === null) {
        // Do something.
    } else if (strings.length === 1) {
        // Do something with strings[0]
    } else { // Could be another else if
        // Do something. It all depends on the context.
    }
} catch (e) {
    console.log(e.message);
}


One could add .trim()inside of getStringsFromChildren()to get rid of leading and trailing whitespace (or to turn a bunch of spaces into a zero length string (""), but how can you know a priori what every application may need to have happen to the text (string) once it is found? You don't, so leave that to a specific implementation, and let getStringsFromChildren()be generic.

可以在.trim()里面添加getStringsFromChildren()以摆脱前导和尾随空格(或将一堆空格转换为零长度字符串 ( ""),但是您如何先验地知道每个应用程序可能需要对文本(字符串)发生什么?一旦找到?你没有,所以把它留给特定的实现,让我们getStringsFromChildren()通用。

There may be times when this level of specificity (the targetand such) is not required. That is great. Use a simple solution in those cases. However, a generalized algorithm enables you to accommodate simple and complex situations.

有时可能不需要这种级别的特异性(target等等)。太棒了。在这些情况下使用简单的解决方案。但是,通用算法使您能够适应简单和复杂的情况。

回答by jujule

ES6 version that return the first #text node content

返回第一个 #text 节点内容的 ES6 版本

const extract = (node) => {
  const text = [...node.childNodes].find(child => child.nodeType === Node.TEXT_NODE);
  return text && text.textContent.trim();
}

回答by Pranay Rana

.text() - for jquery

.text() - for jquery

$('.title').clone()    //clone the element
.children() //select all the children
.remove()   //remove all the children
.end()  //again go back to selected element
.text();    //get the text of element

回答by webx

This will ignore the whitespace as well so, your never got the Blank textNodes..code using core Javascript.

这也将忽略空格,因此您从未使用核心 Javascript 获得 Blank textNodes..code。

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    whitespace = /^\s*$/;
    if (curNode.nodeName === "#text" && !(whitespace.test(curNode.nodeValue))) {
        firstText = curNode.nodeValue;
        break;
    }
}

Check it on jsfiddle : - http://jsfiddle.net/webx/ZhLep/

在 jsfiddle 上检查: - http://jsfiddle.net/webx/ZhLep/

回答by doubleDown

You can also use XPath's text()node test to get the text nodes only. For example

您还可以使用 XPath 的text()节点测试来仅获取文本节点。例如

var target = document.querySelector('div.title');
var iter = document.evaluate('text()', target, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
var node;
var want = '';

while (node = iter.iterateNext()) {
    want += node.data;
}

回答by Damien

This is my solution in ES6 to create a string contraining the concatenated text of all childnodes (recursive). Note that is also visit the shdowroot of childnodes.

这是我在 ES6 中的解决方案,用于创建一个包含所有 childnodes (recursive) 的连接文本的字符串。请注意,这也是访问子节点的 shdowroot。

function text_from(node) {
    const extract = (node) => [...node.childNodes].reduce(
        (acc, childnode) => [
            ...acc,
            childnode.nodeType === Node.TEXT_NODE ? childnode.textContent.trim() : '',
            ...extract(childnode),
            ...(childnode.shadowRoot ? extract(childnode.shadowRoot) : [])],
        []);

    return extract(node).filter(text => text.length).join('\n');
}

This solution was inspired by the solution of https://stackoverflow.com/a/41051238./1300775.

该解决方案的灵感来自https://stackoverflow.com/a/41051238./1300775的解决方案。