代码之家  ›  专栏  ›  技术社区  ›  Bungle

javascript:如何从元素的所有子代获取文本,而不考虑脚本?

  •  6
  • Bungle  · 技术社区  · 15 年前

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

    例如,当提供选择器时 #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>
    

    我的脚本将返回(在清理一点空白之后):

    这是一些文本。var test=true;这是更多的文本。

    但是,我需要忽略出现在 <script> 元素。

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

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

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

    我假设我需要以某种方式循环遍历元素的所有子元素,这些子元素与选择器匹配,并累积所有文本节点,而不是 <脚本& GT; 元素;它看起来没有任何方法来识别javascript,因为它已经被卷进了所有文本节点累积的字符串中。

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

    提前感谢您的帮助!

    2 回复  |  直到 15 年前
        1
  •  8
  •   bobince    15 年前
    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以删除 <script> 元素(通常不会有明显的副作用),更快:

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

    编辑 :

    首先,让我说我不太熟悉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)') ?

    推荐文章