使用 jQuery 从包装集中收集所有文本节点,并用空格分隔

发布于 2024-09-01 18:17:33 字数 1288 浏览 1 评论 0原文

我正在寻找一种方法来收集 jQuery 包装集中的所有文本,但我需要在兄弟节点之间创建空格,这些节点之间没有文本节点。

例如,考虑这个 HTML:

<div>
  <ul>
    <li>List item #1.</li><li>List item #2.</li><li>List item #3.</li>
  </ul>
</div>

如果我简单地使用 jQuery 的 text() 方法来收集

的文本内容,如下所示:

var $div = $('div'), text = $div.text().trim();

alert(text);

将生成以下文本:

列表项 #1.列表项 #2.列表项 #3。

因为每个

  • 元素之间没有空格。我实际上正在寻找的是这个(注意每个句子之间的单个空格):
  • 列出第 1 项。列出项目#3。列出项目 #3。

    这表明我需要遍历包装集中的 DOM 节点,将每个节点的文本附加到一个字符串,后跟一个空格。我尝试了以下代码:

    var $div = $('div'), text = '';
    
    $div.find('*').each(function() {
      text += $(this).text().trim() + ' ';
    });
    
    alert(text);
    

    但这产生了以下文本:

    这是列表项#1。这是列表项#2。这是列表项#3。这是列表项#1。这是列表项#2。这是列表项#3。

    我认为这是因为我正在迭代

    的每个后代并附加文本,所以我在两个
      中获取文本节点及其每个
    • 子元素,导致文本重复。

    我想我可能会找到/编写一个简单的 JavaScript 函数来递归地遍历包装集的 DOM,收集和附加文本节点 - 但是有没有更简单的方法使用 jQuery 来做到这一点?跨浏览器的一致性非常重要。

    感谢您的帮助!

    I'm looking for a way to gather all of the text in a jQuery wrapped set, but I need to create spaces between sibling nodes that have no text nodes between them.

    For example, consider this HTML:

    <div>
      <ul>
        <li>List item #1.</li><li>List item #2.</li><li>List item #3.</li>
      </ul>
    </div>
    

    If I simply use jQuery's text() method to gather the text content of the <div>, like such:

    var $div = $('div'), text = $div.text().trim();
    
    alert(text);
    

    that produces the following text:

    List item #1.List item #2.List item #3.

    because there is no whitespace between each <li> element. What I'm actually looking for is this (note the single space between each sentence):

    List item #1. List item #3. List item #3.

    This suggest to me that I need to traverse the DOM nodes in the wrapped set, appending the text for each to a string, followed by a space. I tried the following code:

    var $div = $('div'), text = '';
    
    $div.find('*').each(function() {
      text += $(this).text().trim() + ' ';
    });
    
    alert(text);
    

    but this produced the following text:

    This is list item #1.This is list item #2.This is list item #3. This is list item #1. This is list item #2. This is list item #3.

    I assume this is because I'm iterating through every descendant of <div> and appending the text, so I'm getting the text nodes within both <ul> and each of its <li> children, leading to duplicated text.

    I think I could probably find/write a plain JavaScript function to recursively walk the DOM of the wrapped set, gathering and appending text nodes - but is there a simpler way to do this using jQuery? Cross-browser consistency is very important.

    Thanks for any help!

    如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

    扫码二维码加入Web技术交流群

    发布评论

    需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

    评论(4

    病毒体 2024-09-08 18:17:33

    jQuery 主要处理元素,它的文本节点能力相对较弱。您可以使用 contents() 获取所有子节点的列表,但您仍然需要遍历它检查类型,因此这实际上与仅使用纯 DOM childNodes 没有什么不同。没有方法可以递归获取文本节点,因此您必须自己编写一些内容,例如。像这样的东西:

    function collectTextNodes(element, texts) {
        for (var child= element.firstChild; child!==null; child= child.nextSibling) {
            if (child.nodeType===3)
                texts.push(child);
            else if (child.nodeType===1)
                collectTextNodes(child, texts);
        }
    }
    function getTextWithSpaces(element) {
        var texts= [];
        collectTextNodes(element, texts);
        for (var i= texts.length; i-->0;)
            texts[i]= texts[i].data;
        return texts.join(' ');
    }
    

    jQuery deals mostly with elements, its text-node powers are relatively weak. You can get a list of all children with contents(), but you'd still have to walk it checking types, so that's really no different from just using plain DOM childNodes. There is no method to recursively get text nodes so you would have to write something yourself, eg. something like:

    function collectTextNodes(element, texts) {
        for (var child= element.firstChild; child!==null; child= child.nextSibling) {
            if (child.nodeType===3)
                texts.push(child);
            else if (child.nodeType===1)
                collectTextNodes(child, texts);
        }
    }
    function getTextWithSpaces(element) {
        var texts= [];
        collectTextNodes(element, texts);
        for (var i= texts.length; i-->0;)
            texts[i]= texts[i].data;
        return texts.join(' ');
    }
    
    熟人话多 2024-09-08 18:17:33

    这是我能想到的最简单的解决方案:

    $("body").find("*").contents().filter(function(){return this.nodeType!==1;});
    

    This is the simplest solution I could think of:

    $("body").find("*").contents().filter(function(){return this.nodeType!==1;});
    
    七分※倦醒 2024-09-08 18:17:33

    可以使用 jQuery contents() 方法获取所有节点(包括文本节点),然后向下过滤您设置为仅文本节点。

    $("body").find("*").contents().filter(function(){return this.nodeType!==1;});
    

    从那里您可以创建您需要的任何结构。

    You can use the jQuery contents() method to get all nodes (including text nodes), then filter down your set to only the text nodes.

    $("body").find("*").contents().filter(function(){return this.nodeType!==1;});
    

    From there you can create whatever structure you need.

    吾性傲以野 2024-09-08 18:17:33

    我以 @bobince 的出色答案为基础,制作了搜索工具,该工具将搜索表的所有列并过滤行以仅显示与所有用户搜索词(以任何顺序提供)匹配(不区分大小写)的行。

    以下是屏幕截图示例:

    在此处输入图像描述

    这是我的 javascript/jQuery 代码:

    $(function orderFilter() {
      // recursively collect all text from child elements (returns void)
      function collectTextNodes(element, texts) {
        for (
          let child = element.firstChild;
          child !== null;
          child = child.nextSibling
        ) {
          if (child.nodeType === Node.TEXT_NODE) {
            texts.push(child);
          } else if (child.nodeType === Node.ELEMENT_NODE) {
            collectTextNodes(child, texts);
          }
        }
      }
    
      // separate all text from all children with single space
      function getAllText(element) {
        const texts = [];
        collectTextNodes(element, texts);
    
        for (let i = texts.length; i-- > 0; ) texts[i] = texts[i].data;
    
        return texts.join(' ').replace(/\s\s+/g, ' ');
      }
    
      // check to see if the search value appears anywhere in child text nodes
      function textMatchesFilter(tbody, searchVal) {
        const tbodyText = getAllText(tbody).toLowerCase();
        const terms = searchVal.toLowerCase().replace(/\s\s+/g, ' ').split(' ');
    
        return terms.every(searchTerm => tbodyText.includes(searchTerm));
      }
    
      // filter orders to only show those matching certain fields
      $(document).on('keyup search', 'input.js-filter-orders', evt => {
        const searchVal = $(evt.target).val();
        const $ordersTable = $('table.js-filterable-table');
        $ordersTable.find('tbody[hidden]').removeAttr('hidden');
    
        if (searchVal.length <= 1) return;
    
        // Auto-click the "Show more orders" button and reveal any collapsed rows
        $ordersTable
          .find('tfoot a.show-hide-link.collapsed, tbody.rotate-chevron.collapsed')
          .each((_idx, clickToShowMore) => {
            clickToShowMore.click();
          });
    
        // Set all tbodies to be hidden, then unhide those that match
        $ordersTable
          .find('tbody')
          .attr('hidden', '')
          .filter((_idx, tbody) => textMatchesFilter(tbody, searchVal))
          .removeAttr('hidden');
      });
    });
    

    就我们的目的而言,它工作得非常完美!希望这对其他人有帮助!

    I built on @bobince's terrific answer to make search tool that would search all columns of a table and filter the rows to show only those that matched (case-insensitively) all of a user's search terms (provided in any order).

    Here is a screenshot example:

    enter image description here

    And here is my javascript/jQuery code:

    $(function orderFilter() {
      // recursively collect all text from child elements (returns void)
      function collectTextNodes(element, texts) {
        for (
          let child = element.firstChild;
          child !== null;
          child = child.nextSibling
        ) {
          if (child.nodeType === Node.TEXT_NODE) {
            texts.push(child);
          } else if (child.nodeType === Node.ELEMENT_NODE) {
            collectTextNodes(child, texts);
          }
        }
      }
    
      // separate all text from all children with single space
      function getAllText(element) {
        const texts = [];
        collectTextNodes(element, texts);
    
        for (let i = texts.length; i-- > 0; ) texts[i] = texts[i].data;
    
        return texts.join(' ').replace(/\s\s+/g, ' ');
      }
    
      // check to see if the search value appears anywhere in child text nodes
      function textMatchesFilter(tbody, searchVal) {
        const tbodyText = getAllText(tbody).toLowerCase();
        const terms = searchVal.toLowerCase().replace(/\s\s+/g, ' ').split(' ');
    
        return terms.every(searchTerm => tbodyText.includes(searchTerm));
      }
    
      // filter orders to only show those matching certain fields
      $(document).on('keyup search', 'input.js-filter-orders', evt => {
        const searchVal = $(evt.target).val();
        const $ordersTable = $('table.js-filterable-table');
        $ordersTable.find('tbody[hidden]').removeAttr('hidden');
    
        if (searchVal.length <= 1) return;
    
        // Auto-click the "Show more orders" button and reveal any collapsed rows
        $ordersTable
          .find('tfoot a.show-hide-link.collapsed, tbody.rotate-chevron.collapsed')
          .each((_idx, clickToShowMore) => {
            clickToShowMore.click();
          });
    
        // Set all tbodies to be hidden, then unhide those that match
        $ordersTable
          .find('tbody')
          .attr('hidden', '')
          .filter((_idx, tbody) => textMatchesFilter(tbody, searchVal))
          .removeAttr('hidden');
      });
    });
    

    For our purposes, it works perfectly! Hope this helps others!

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文