【问题标题】:How do I get just the visible text with jQuery (or Javascript)?如何使用 jQuery(或 Javascript)获取可见文本?
【发布时间】:2010-12-23 04:51:51
【问题描述】:

我有网站可以转换Japanese Kanji into Romaji (roman letters):

输出显示和隐藏用户需要根据输入标准查看的内容。例如:

<div id="output"><span class="roman">watashi</span> <span class="english">I</span></div>

该界面允许用户根据他们想要看到的内容在watashiI 之间切换和输出。 CSS 使用 jQuery 和切换按钮隐藏其中之一。 (隐藏机制包括简单地向 body 添加一个类并让 CSS 完成它的工作)。

问题在于,当用户将文本复制/粘贴到 Word 中时,它会复制所有内容。所以我决定使用一个系统来复制粘贴文本,使用 JavaScript 和 jQuery,但问题再次出现:

$('#output').text() 输出watashi I,即使I 在页面本身而不是watashi 上是不可见的。有什么方法可以只获取可见文本?

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:

    使用:visible selector of jQuery

    在你的情况下,我认为你想做:

    $('#output').children(":visible").text() 
    

    【讨论】:

    • 顺便说一句,根据 OP 问题,只有一个 #output 的孩子是隐藏的,这样可行吗?当我测试$('#output:visible').text() 时,它仍然显示“Watashi I”,但 OP 只想要“Watashi”,不是吗?
    • @s.mark:你一定是对的。我编辑了我的答案,我认为它应该可以工作。如果不尝试 *:visible 或类似的东西。你也可以测试 .css("display")!="none"
    • 我认为这是错误的方法,因为它包括重新实现已经存在的浏览器功能(复制/粘贴)。
    • @smark:好!这显然不是最好的解决方案。最好的办法是只加载到所需语言的页面,但这会立即解决 OP 的问题
    • 嘿,Marcgg,这似乎工作得几乎完美。它确实复制了可见文本,但如果我通过“旋转”按钮更改可见文本,那么复制操作仍然只能获取原始可见文本 - 而不是更新后的文本(如果您访问实际站点和单击旋转图标以可视化文本的变化方式)。所以我想如果可见文本发生变化,它会变得有点复杂。 (顺便说一句,我想“提高”你的建议,但我是新来的,它不允许我!)
    【解决方案2】:

    不要隐藏 span,而是删除 span 元素并保留对它的引用。当用户单击切换按钮时,删除另一个并插入您保留引用的那个。用户将无法再选择不在 DOM 中的内容。

    【讨论】:

    • 嘿'风之声' :-) 我更愿意找到一个目前不涉及删除元素的解决方案,因为这需要对 coed 进行重大重写。
    • 我想你将不得不使用一些捉迷藏魔法然后:) 我会在绑定到切换按钮的界面元素的函数中使用 JS 动态删除它们,但如果那也是很多工作我没有其他建议:(
    【解决方案3】:

    其他解决方案没有给我我需要的东西。

    简答

    我的答案是:

    $('#output *:not(:has(*)):visible').text()
    

    plunkr

    TL;DR

    marcgg解决方案的问题

    你不应该询问某个根元素下所有元素的文本..

    为什么? - 它将重复输出并忽略隐藏标志

    让我们看一个简单的例子

    <div id="output" class="my-root">
        <div class="some-div">
             <span class="first" style="display:none"> hidden text </span>
             <span class="second" > visible text </span>
        </div>
    <div>
    

    现在如果我这样做$('#output').children(":visible").text()

    我会得到.some-div.second.. 事实上.some-div 对我来说并不重要..

    当我在这些元素上请求 text() 时,.some-div 也会返回隐藏文本..

    所以从技术上讲,marcgg 的解决方案是错误的恕我直言......

    我回答的原因

    现在,为了正确回答这个问题,我们必须做出一个假设。对我来说,这似乎足够合理。

    假设是文本只出现在叶元素中..

    所以我们不会看到这样的:

    <div id="output" class="my-root">
        <div class="some-div">
             <span class="first" style="display:none"> hidden text </span>
             <span class="second" > visible text </span>
        </div>
    
        some text here.. 
    
    <div>
    

    为什么这个假设对我来说似乎是合理的?两个原因:

    • 因为很难维护以这种方式构建的页面 - 并且随着时间的推移,有经验的人会学习并避免它。
    • 很容易将您的 html 转换为这样的结构。只需用跨度包装父母的文字。所以即使这个假设现在不存在,也很容易实现。

    有了这个假设,你要做的是请求所有的叶子元素(没有子元素的元素),过滤掉可见的元素,然后请求它们的文本..

    $('#output *:not(:has(*)):visible').text()
    

    这应该会产生正确的结果。

    必须在叶子元素之外有文字吗?

    cmets 建议有时您只需要在叶子元素之外添加文本

    <div> This is some <strong style="display:none"> text </strong>  </div>
    

    如您所见,您有&lt;strong&gt; 作为叶子,并且在此示例中通常在其外部放置文本。

    您可以使用我上面建议的解决方法来解决它。但是如果您不能呢?

    您可以克隆 dom,然后删除所有隐藏的元素。 这里的问题是,为了让:visible 选择器或:hidden 选择器工作,我必须在文档上有 dom 元素(这意味着用户实际上可以看到)。 所以,这种方法有一些副作用,所以要小心。

    这是一个例子

    对于这个 html

     <div id="output" class="my-root">
         <span>
             some text <strong style="display:none">here.. </strong>
         </span>
    </div>
    

    这个javascript有效

    $(function(){
         var outputClone = $('#output').clone();
        $('#output :hidden').remove(); 
        console.log($('#output').text()); // only visible text
        $('#output').replaceWith(outputClone);
        console.log($('#output').text()); // show original state achieved. 
    })
    

    plunker here

    如前所述 - 副作用可能看起来像瞬间闪烁,或一些应该运行的初始化脚本。有些可能会通过一些原始想法来避免(大小为 1px/1px 的 div 包含原始内容旁边的克隆?)取决于你的场景。

    【讨论】:

    • 文本在叶节点中的假设似乎有问题 - 例如如果您有 文本或 文本,那么这些部分将位于叶节点中,而不是周围非粗体或非强调文本的其余部分。
    • 但我展示了如何轻松绕过它。还有另一种方法.. 您可以克隆整个 HTML,然后简单地删除隐藏的部分,然后对所有内容执行“getText”。
    • @DaveHilditch 添加了一个也可以解决您的情况的示例。
    • 我在您发布的最终 JS 函数中出现了一些奇怪的行为,您确定正确保存所有 DOM 元素上的所有 JS 事件吗?
    • @Dave13s 不确定我是否遵循了这个问题。我发布了一个plunker。如果您遇到问题 - 您能在 plunker 上重现它吗?它会更容易解决。
    【解决方案4】:

    小伙答对了。

    但是,我正在处理一个“this”对象,因此要让他的答案起作用,您需要使用以下语法...

    $('*:not(:has(*)):visible', this).text()
    

    【讨论】:

      【解决方案5】:
      var lookup = function(element, text) {
          //DFS Recursive way of finding text on each level
          //Visible only works on elements that take up space(i.e. not fixed position elements)
          var results = element.children(':visible');
      
          //Look at the text at each level with the children removed
          var newText = '';
          results.each(function(index, value) {
              newText += $(value).clone()
                  .children()
                  .remove()
                  .end()
                  .text();
          });
      
          var moreResultText = '';
          results.each(function(index, value) {
              moreResultText += lookup($(value), text);
          })
      
          if (results.length > 0) {
              return text + newText + moreResultText;
          } else {
              return text;
          }
      };
      
      lookup($('#output'), ''));
      

      大多数其他功能在页面的大部分区域运行时都会崩溃,这应该是一种更准确的方法来确定实际向用户显示的内容,不会损坏页面,也不会返回不可见的文本用户。

      当然要小心,这不会保留任何格式感,并且元素之间的输出间距可能不正确。此外,它可能没有正确排序返回的文本,在这些方面它的使用将受到限制。另一个考虑因素是可见的真正定义对nail down 来说有点困难,但对于这个例子,我接受 ":visible" 适用于大多数常见情况。

      我用它来检查页面是否包含可见文本(只需在 body 元素上运行它),但它可能也适用于这个示例。

      【讨论】:

      • 我发现这段代码是一个有用的开始。但是你不想替换 moreResultText += lookup($(value), text); with moreResultText += lookup($(value), '');如果不是,您将重复原始文本值。
      • 我很高兴你发现它很有用:),我觉得这个答案提供了一个更好/更完整的方法来完成所请求的功能,而不是其他更高投票的答案。至于代码,我没有观察到这种行为,它是 DFS,所以它应该将每个较低级别的文本添加到字符串中,最终将其传递给父调用者,直到你有一个包含所有文本的大字符串。您是否有一个不起作用的示例,可能是JSFiddle
      【解决方案6】:

      在现代浏览器中试试这个(这里的“元素”是一个非 JQuery DOM 对象):

      function getVisibleText(element) {
          window.getSelection().removeAllRanges();
      
          let range = document.createRange();
          range.selectNode(element);
          window.getSelection().addRange(range);
      
          let visibleText = window.getSelection().toString().trim();
          window.getSelection().removeAllRanges();
      
          return visibleText;
      }
      

      然后:

      getVisibleText(document.getElementById('output'));
      

      【讨论】:

      • $('#output *:not(:has(*)):visible').text() jQuery 答案由于某种原因错过了一些文本。这个拾取了所有可见的内容(除了 ::before 伪元素中生成的内容,我不太担心)。
      • 验证您的输入!如果您查询输入元素,它可能是 null ? 很好的答案。
      猜你喜欢
      • 2013-07-19
      • 1970-01-01
      • 2011-08-15
      • 2014-10-01
      • 2014-03-16
      • 1970-01-01
      • 1970-01-01
      • 2011-01-22
      • 1970-01-01
      相关资源
      最近更新 更多