如何使用 jQuery(或 Javascript)获取可见文本?

Posted

技术标签:

【中文标题】如何使用 jQuery(或 Javascript)获取可见文本?【英文标题】:How do I get just the visible text with jQuery (or 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 上是不可见的。有什么方法可以只获取可见文本?

【问题讨论】:

【参考方案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 ? 很好的答案。

以上是关于如何使用 jQuery(或 Javascript)获取可见文本?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JavaScript 或 jQuery 更改数组内对象的值?

如何使用 jquery 或 javascript 删除索引处的行? [复制]

如何使用 jQuery 或 JavaScript 设置底边距

如何使用 PHP 或 JavaScript/jQuery 禁用地址栏?

如何防止使用 jQuery 或 Javascript 进行双重提交?

当您使用 javascript 或 jquery 将鼠标悬停时,如何使图像或按钮发光?