6

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

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

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

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

但是,我需要忽略<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 + ' ';
    }
  }
}

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

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

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

提前感谢您的帮助!

4

2 回答 2

9
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;
于 2010-03-28T10:06:16.923 回答
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)')吗?

于 2010-03-28T06:40:27.687 回答