检测文本中哪个单词被点击

IT技术 javascript html parsing
2021-02-08 00:28:46

我正在构建一个 JS 脚本,它在某个时候能够在给定的页面上允许用户单击任何单词并将该单词存储在变量中。

我有一个非常难看的解决方案,它涉及使用 jQuery 进行类解析:我首先解析整个 html,拆分每个空间上的所有内容" ",然后重新附加包裹在 a 中的所有内容<span class="word">word</span>,然后我添加一个带有 jQ​​ 的事件来检测点击这样一个类,并使用 $(this).innerHTML 我得到点击的词。

这在很多方面都是缓慢而丑陋的,我希望有人知道实现这一目标的另一种方法。

PS:我可能会考虑将它作为浏览器扩展来运行,所以如果仅使用 JS 听起来不可能,并且如果您知道允许这样做的浏览器 API,请随时提及它!

一个可能的 owrkaround 是让用户突出显示这个词而不是点击它,但我真的很想只需点击一下就可以实现同样的事情!

6个回答

这是一个无需向文档添加大量跨度即可工作的解决方案(适用于 Webkit 和 Mozilla 以及 IE9+):

https://jsfiddle.net/Vap7C/15/

    $(".clickable").click(function(e){
         s = window.getSelection();
         var range = s.getRangeAt(0);
         var node = s.anchorNode;
         
         // Find starting point
         while(range.toString().indexOf(' ') != 0) {                 
            range.setStart(node,(range.startOffset -1));
         }
         range.setStart(node, range.startOffset +1);
         
         // Find ending point
         do{
           range.setEnd(node,range.endOffset + 1);

        }while(range.toString().indexOf(' ') == -1 && range.toString().trim() != '');
        
        // Alert result
        var str = range.toString().trim();
        alert(str);
       });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="clickable">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rutrum ante nunc. Proin sit amet sem purus. Aliquam malesuada egestas metus, vel ornare purus sollicitudin at. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer porta turpis ut mi pharetra rhoncus. Ut accumsan, leo quis hendrerit luctus, purus nunc suscipit libero, sit amet lacinia turpis neque gravida sapien. Nulla facilisis neque sit amet lacus ornare consectetur non ac massa. In purus quam, imperdiet eget tempor eu, consectetur eget turpis. Curabitur mauris neque, venenatis a sollicitudin consectetur, hendrerit in arcu.
</p>

在 IE8 中,由于 getSelection,它有问题。此链接(是否有 getSelection() 的跨浏览器解决方案?)可能有助于解决这些问题。我还没有在 Opera 上测试过。

从类似的问题中使用https://jsfiddle.net/Vap7C/1/作为起点。它使用了Selection.modify函数:

s.modify('extend','forward','word');
s.modify('extend','backward','word');

不幸的是,他们并不总能得到完整的信息。作为一种解决方法,我获得了选择范围并添加了两个循环来查找单词边界。第一个不断向单词添加字符,直到它到达一个空格。第二个循环到单词的末尾,直到它到达一个空格。

这也将抓住单词末尾的任何标点符号,因此请确保在需要时将其修剪掉。

一位匿名用户建议进行此编辑:一个改进的解决方案,它总是得到正确的词,更简单,并且适用于 IE 4+:jsfiddle.net/Vap7C/80
2021-03-13 00:28:46
在上面的第一个代码部分中,range.setStart(node, (range.startOffset - 1));在“节点”中的第一个单词上运行时会崩溃,因为它试图设置range为负值。我尝试添加逻辑来防止这种情况发生,但随后range.setStart(node, range.startOffset + 1);返回除第一个单词的第一个字母之外的所有内容。 此外,当单词由换行符分隔时,除了单击的单词外,还返回上一行的最后一个单词。所以,这需要一些工作。
2021-03-21 00:28:46
我不为第一个词工作。至少在 Chromium/Linux 中
2021-03-23 00:28:46
我实际上必须阅读 Mozilla 上的 DOM 文档才能弄清楚这一点。
2021-03-25 00:28:46
非常可爱...我已将其修改为仅在按下 ctrl 时才触发。不过,它似乎不想获取<a>元素的文本怎么来的?
2021-04-09 00:28:46

据我所知,span为每个单词添加一个是唯一的方法。

您可以考虑使用Lettering.js,它会为您处理拆分尽管这不会真正影响性能,除非您的“拆分代码”效率低下。

然后,与绑定.click()到 every 相比span,将 single 绑定.click()spans的容器并检查event.target哪个span已被单击会更有效

我刚刚发现这个浏览 SO:jsfiddle.net/niklasvh/rD2uE它不如“span”hack 准确,这是一个问题,但似乎有效......我现在必须进行基准测试(并尝试了解什么代码实际上是)
2021-03-13 00:28:46
@thirtydot 但是为每个单词的聊天添加拼写检查是正确的吗?如果一个单词被标记不正确并且需要更正,我该如何保留它,如果我转换为跨度,它会删除它的标记
2021-03-26 00:28:46

以下是对已接受答案的改进:

$(".clickable").click(function (e) {
    var selection = window.getSelection();
    if (!selection || selection.rangeCount < 1) return true;
    var range = selection.getRangeAt(0);
    var node = selection.anchorNode;
    var word_regexp = /^\w*$/;

    // Extend the range backward until it matches word beginning
    while ((range.startOffset > 0) && range.toString().match(word_regexp)) {
      range.setStart(node, (range.startOffset - 1));
    }
    // Restore the valid word match after overshooting
    if (!range.toString().match(word_regexp)) {
      range.setStart(node, range.startOffset + 1);
    }

    // Extend the range forward until it matches word ending
    while ((range.endOffset < node.length) && range.toString().match(word_regexp)) {
      range.setEnd(node, range.endOffset + 1);
    }
    // Restore the valid word match after overshooting
    if (!range.toString().match(word_regexp)) {
      range.setEnd(node, range.endOffset - 1);
    }

    var word = range.toString();
});​

另一个对@stevendaniel 的回答的看法:

$('.clickable').click(function(){
   var sel=window.getSelection();
   var str=sel.anchorNode.nodeValue,len=str.length, a=b=sel.anchorOffset;
   while(str[a]!=' '&&a--){}; if (str[a]==' ') a++; // start of word
   while(str[b]!=' '&&b++<len){};                   // end of word+1
   console.log(str.substring(a,b));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p class="clickable">The objective can also be achieved by simply analysing the
string you get from <code>sel=window.getSelection()</code>. Two simple searches for
the next blank before and after the word, pointed to by the current position
(<code>sel.anchorOffset</code>) and the work is done:</p>

<p>This second paragraph is <em>not</em> clickable. I tested this on Chrome and Internet explorer (IE11)</p>

我所知道的唯一跨浏览器(IE < 8)方式是包装在span元素中。这很丑陋,但并不是那么慢。

这个例子直接来自 jQuery .css() 函数文档,但有一大块文本要预处理:

http://jsfiddle.net/kMvYy/

这是在不需要换行的同一文本块上的另一种方法(此处给出:jquery 捕获单词 valuespanhttp://jsfiddle.net/Vap7C/1

好的,那么我已经看到了这种技术,尽管它高效且非常便携,但似乎受到了许多人的监督;不幸的是,它不准确(单击单词的第一个字母通常会返回前一个单词)。
2021-04-04 00:28:46
抱歉,忘记点击保存
2021-04-08 00:28:46