JavaScript: Как получить текст от всех потомков элемента, не обращая внимания на сценарии? - PullRequest
6 голосов
/ 28 марта 2010

Мой текущий проект включает сбор текстового содержимого элемента и всех его потомков на основе предоставленного селектора.

Например, если указан селектор #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 + ' ';
    }
  }
}

Это немного слишком упрощенно в том смысле, что он просто возвращает все текстовые узлы в элементе (и его потомках), которые соответствуют предоставленному селектору. Решение, которое я ищу, будет возвращать все текстовые узлы, кроме тех, которые попадают в элементы <script>. Он не должен быть особенно высокопроизводительным, но мне нужно, чтобы он в конечном итоге был совместим с различными браузерами.

Я предполагаю, что мне нужно каким-то образом пройти через все дочерние элементы элемента, который соответствует селектору, и накапливать все текстовые узлы, кроме узлов в элементах <script>; не похоже, что есть какой-либо способ идентифицировать JavaScript, когда он уже свернут в строку, собранную из всех текстовых узлов.

Я не могу использовать jQuery (по соображениям производительности / пропускной способности), хотя вы, возможно, заметили, что я использую его механизм выбора Sizzle, поэтому доступна логика выбора jQuery.

Заранее спасибо за любую помощь!

Ответы [ 2 ]

8 голосов
/ 28 марта 2010
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 голосов
/ 28 марта 2010

EDIT

Ну, во-первых, позвольте мне сказать, что я не слишком знаком с Sizzle на его одиночестве, но в библиотеках, которые его используют ... Тем не менее ..

если бы мне пришлось это сделать, я бы сделал что-то вроде:

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

ofcourse jquery поддерживает синтаксис not() в селекторах, так что вы можете просто сделать $(':not(script)')?

...