【问题标题】:JavaScript: How to get text from all descendents of an element, disregarding scripts?JavaScript:如何从元素的所有后代中获取文本,而不考虑脚本?
【发布时间】:2011-02-01 16:30:27
【问题描述】:

我当前的项目涉及根据提供的选择器从一个元素及其所有后代中收集文本内容。

例如,当提供选择器 #content 并针对此 HTML 运行时:

<div id="content">
  <p>This is some text.</p>
  <script type="text/javascript">
    var test = true;
  </script>
  <p>This is some more text.</p>
</div>

我的脚本会返回(经过一点空白清理):

这是一些文字。变种测试=真;这是更多的文字。

但是,我需要忽略出现在 &lt;script&gt; 元素中的文本节点。

这是我当前代码的摘录(从技术上讲,它基于一个或多个提供的选择器进行匹配):

// get text content of all matching elements
for (x = 0; x < selectors.length; x++) { // 'selectors' is an array of CSS selectors from which to gather text content
  matches = Sizzle(selectors[x], document);
  for (y = 0; y < matches.length; y++) {
    match = matches[y];
    if (match.innerText) { // IE
      content += match.innerText + ' ';
    } else if (match.textContent) { // other browsers
      content += match.textContent + ' ';
    }
  }
}

这有点过于简单了,因为它只返回与提供的选择器匹配的元素(及其后代)中的所有文本节点。我正在寻找的解决方案将返回除 &lt;script&gt; 元素之外的所有文本节点。它不需要特别高性能,但我确实需要它最终能够跨浏览器兼容。

我假设我需要以某种方式遍历与选择器匹配的元素的所有子元素,并累积除 &lt;script&gt; 元素中的所有文本节点以外的所有文本节点;一旦它已经滚入从所有文本节点累积的字符串中,看起来就没有任何方法可以识别 JavaScript。

我不能使用 jQuery(出于性能/带宽原因),尽管您可能已经注意到我确实使用了它的 Sizzle 选择器引擎,所以 jQuery 的选择器逻辑是可用的。

提前感谢您的帮助!

【问题讨论】:

    标签: javascript string dom text textnode


    【解决方案1】:
    function getTextContentExceptScript(element) {
        var text= [];
        for (var i= 0, n= element.childNodes.length; i<n; i++) {
            var child= element.childNodes[i];
            if (child.nodeType===1 && child.tagName.toLowerCase()!=='script')
                text.push(getTextContentExceptScript(child));
            else if (child.nodeType===3)
                text.push(child.data);
        }
        return text.join('');
    }
    

    或者,如果允许您更改 DOM 以删除 &lt;script&gt; 元素(通常不会有明显的副作用),那么更快:

    var scripts= element.getElementsByTagName('script');
    while (scripts.length!==0)
        scripts[0].parentNode.removeChild(scripts[0]);
    return 'textContent' in element? element.textContent : element.innerText;
    

    【讨论】:

    • 太棒了,谢谢,bobince!我采用了第一种方法 - 您可能是对的,删除 &lt;script&gt; 元素通常不会产生副作用,但我将在野外使用此代码并且不想冒险。我以前没有见过.data 的用法——我读过它,它听起来很健壮。是跨浏览器回到 IE 6 吗?我是否正确,它不会从嵌套的评论节点中提取任何文本 - 只是节点本身的文本内容?
    • 它是 DOM Level 1 Core (w3.org/TR/REC-DOM-Level-1/level-one-core.html),所有浏览器和纯 XML DOM 都支持。上面的代码只会查看文本节点中的数据(3Node.TEXT_NODE,但 IE 无法提供该符号常量)。在 XML 文档中,您可能还想从 CDATA_SECTION_NODE (4) 中获取数据。 COMMENT_NODE8 并被忽略。
    【解决方案2】:

    编辑

    首先让我说我对 Sizzle 的寂寞不太熟悉,jsut 在使用它的库中......也就是说......

    如果我必须这样做,我会这样做:

    var selectors = new Array('#main-content', '#side-bar');
    function findText(selectors) {
        var rText = '';
        sNodes = typeof selectors = 'array' ? $(selectors.join(',')) : $(selectors);
        for(var i = 0; i <  sNodes.length; i++) {
           var nodes = $(':not(script)', sNodes[i]);
           for(var j=0; j < nodes.length; j++) {
             if(nodes[j].nodeType != 1 && node[j].childNodes.length) {
                 /* recursion - this would work in jQ not sure if 
                  * Sizzle takes a node as a selector you may need 
                  * to tweak.
                  */
                 rText += findText(node[j]); 
             }  
           }
        }
    
        return rText;
    }
    

    我没有测试任何这些,但它应该给你一个想法。希望其他人能有更多的方向:-)


    你不能只抓住父节点并检查循环中的nodeName...比如:

    if(match.parentNode.nodeName.toLowerCase() != 'script' && match.nodeName.toLowerCase() != 'script' ) {
        match = matches[y];
        if (match.innerText) { // IE
          content += match.innerText + ' ';
        } else if (match.textContent) { // other browsers
          content += match.textContent + ' ';
        }
    }
    

    当然,jquery 在选择器中支持not() 语法,所以你可以只使用$(':not(script)') 吗?

    【讨论】:

    • 感谢 prodigitalson - 不过,我不确定这是否能实现我的目标。我的代码示例中可能有点含糊(只是对其进行了编辑) - 它的作用是遍历 CSS 选择器数组,并且对于每个匹配 DOM 节点,它只获取 innerText(IE)或 textContent(其他)该节点的属性。它实际上并没有遍历元素的子元素。但是,我认为后者可能是执行此操作的最佳方法 - 遍历匹配元素的所有后代,忽略
    • 再次感谢!这看起来是个不错的方法。好奇你为什么使用Array 构造函数而不是括号符号?
    • @bungle:个人喜好。
    • 酷,谢谢。我听说括号表示法是一种最佳实践,但我不知道有任何功能差异。再次感谢您的帮助。
    • 我也不知道功能差异。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多