如何使用 jQuery 选择文本节点?

IT技术 javascript jquery dom
2021-01-12 20:28:36

我想获得一个元素的所有后代文本节点,作为一个 jQuery 集合。最好的方法是什么?

6个回答

jQuery 没有一个方便的功能。您需要组合contents(),它只提供子节点但包括文本节点,与find(),它提供所有后代元素但不提供文本节点。这是我想出的:

var getTextNodesIn = function(el) {
    return $(el).find(":not(iframe)").addBack().contents().filter(function() {
        return this.nodeType == 3;
    });
};

getTextNodesIn(el);

注意:如果您使用的是 jQuery 1.7 或更早版本,则上面的代码将不起作用。要解决此问题,请替换addBack()andSelf(). 从 1.8 开始andSelf()不推荐使用addBack()

与纯 DOM 方法相比,这有点低效,并且必须为 jQuery 的contents()函数重载包含一个丑陋的解决方法(感谢@rabidsnail 在评论中指出这一点),因此这里是使用简单递归函数的非 jQuery 解决方案。includeWhitespaceNodes参数控制空白文本节点是否包含在输出中(在 jQuery 中它们会被自动过滤掉)。

更新:修正了当 includeWhitespaceNodes 为假时的错误。

function getTextNodesIn(node, includeWhitespaceNodes) {
    var textNodes = [], nonWhitespaceMatcher = /\S/;

    function getTextNodes(node) {
        if (node.nodeType == 3) {
            if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
                textNodes.push(node);
            }
        } else {
            for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                getTextNodes(node.childNodes[i]);
            }
        }
    }

    getTextNodes(node);
    return textNodes;
}

getTextNodesIn(el);
@rabidsnail:我认为,使用.contents()anyways 意味着它也会搜索iframe。我不明白这怎么可能是一个错误。
2021-03-19 20:28:36
bugs.jquery.com/ticket/11275这是否真的是一个 bug 似乎有待讨论,但如果你在包含 iframe 的节点上调用 find('*').contents()被添加到 dom 你会在一个未定义的点得到一个异常。
2021-03-25 20:28:36
由于 jQuery 中的错误,如果您在 el 中有任何 iframe,则需要使用 .find(':not(iframe)') 而不是 .find('*') 。
2021-04-01 20:28:36
@crosenblum:你可以document.getElementById()打电话,如果这就是你的意思:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
2021-04-05 20:28:36
传入的元素可以是div的名字吗?
2021-04-07 20:28:36

Jauco 在评论中发布了一个很好的解决方案,所以我在这里复制它:

$(elem)
  .contents()
  .filter(function() {
    return this.nodeType === 3; //Node.TEXT_NODE
  });
实际上 $(elem) .contents() .filter(function() { return this.nodeType == Node.TEXT_NODE; }); 足够
2021-03-15 20:28:36
IE7 没有定义 Node 全局,所以你必须使用 this.nodeType == 3,不幸的是:stackoverflow.com/questions/1423599/node-textnode-and-ie7
2021-03-18 20:28:36
@Jauco,不,还不够!因为 .contents() 只返回直接的子节点
2021-03-18 20:28:36
这是否不仅返回作为元素的直接子元素的文本节点,而不是 OP 请求的元素的后代?
2021-03-29 20:28:36
当文本节点深入嵌套在其他元素中时,这将不起作用,因为 contents() 方法仅返回直接子节点api.jquery.com/contents
2021-04-04 20:28:36
$('body').find('*').contents().filter(function () { return this.nodeType === 3; });

jQuery.contents()可用于jQuery.filter查找所有子文本节点。稍加改动,您也可以找到孙文本节点。不需要递归:

$(function() {
  var $textNodes = $("#test, #test *").contents().filter(function() {
    return this.nodeType === Node.TEXT_NODE;
  });
  /*
   * for testing
   */
  $textNodes.each(function() {
    console.log(this);
  });
});
div { margin-left: 1em; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="test">
  child text 1<br>
  child text 2
  <div>
    grandchild text 1
    <div>grand-grandchild text 1</div>
    grandchild text 2
  </div>
  child text 3<br>
  child text 4
</div>

js小提琴

我试过这个。它会乱序打印标签名称。有没有办法按照标签名称出现的顺序打印它们?我在这里问了一个单独的问题stackoverflow.com/questions/63276378/...
2021-03-25 20:28:36

我使用接受的过滤器功能得到了很多空文本节点。如果您只对选择包含非空格的文本节点感兴趣,请尝试向nodeValue您的filter函数添加条件,例如简单的$.trim(this.nodevalue) !== ''

$('element')
    .contents()
    .filter(function(){
        return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
    });

http://jsfiddle.net/ptp6m97v/

或者为了避免内容看起来像空格但不是(例如软连&shy;字符、换行符\n、制表符等)的奇怪情况,您可以尝试使用正则表达式。例如,\S将匹配任何非空白字符:

$('element')
        .contents()
        .filter(function(){
            return this.nodeType === 3 && /\S/.test(this.nodeValue);
        });
我试过这个。它会乱序打印标签名称。有没有办法按照标签名称出现的顺序打印它们?我在这里问了一个单独的问题stackoverflow.com/questions/63276378/...
2021-04-05 20:28:36