【问题标题】:Selecting text in an element (akin to highlighting with your mouse)选择元素中的文本(类似于用鼠标突出显示)
【发布时间】:2010-11-02 09:14:55
【问题描述】:

我想让用户点击一个链接,然后它选择另一个元素中的 HTML 文本(不是输入)。

“选择”是指通过将鼠标拖到文本上来选择文本的相同方式。这一直是一项研究工作,因为每个人都在用其他术语谈论“选择”或“突出显示”。

这可能吗?到目前为止我的代码:

HTML:

<a href="javascript:" onclick="SelectText('xhtml-code')">Select Code</a>
<code id="xhtml-code">Some Code here </code>

JS:

function SelectText(element) {
    $("#" + element).select();
}

我是否遗漏了一些明显的东西?

【问题讨论】:

标签: javascript jquery


【解决方案1】:

纯 Javascript

function selectText(node) {
    node = document.getElementById(node);

    if (document.body.createTextRange) {
        const range = document.body.createTextRange();
        range.moveToElementText(node);
        range.select();
    } else if (window.getSelection) {
        const selection = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(node);
        selection.removeAllRanges();
        selection.addRange(range);
    } else {
        console.warn("Could not select text in node: Unsupported browser.");
    }
}

const clickable = document.querySelector('.click-me');
clickable.addEventListener('click', () => selectText('target'));
<div id="target"><p>Some text goes here!</p><p>Moar text!</p></div>
<p class="click-me">Click me!</p>

这是working demo。对于那些正在寻找 jQuery 插件的人,我创建了one of those too


jQuery(原始答案)

我在this thread 中找到了解决方案。我能够修改给定的信息并将其与一些 jQuery 混合,以创建一个非常棒的函数来选择任何元素中的文本,而不管浏览器如何:

function SelectText(element) {
    var text = document.getElementById(element);
    if ($.browser.msie) {
        var range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = window.getSelection();
        var range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = window.getSelection();
        selection.setBaseAndExtent(text, 0, text, 1);
    }
}

【讨论】:

  • jQuery 解决方案给了我 Uncaught TypeError: Cannot read property 'msie' of undefined
  • @egmfrs 是的,因为发布了这个答案,jquery 已经删除了他们的browser 嗅探器。
  • jquery 的解决方案来源于下面VillageIdiot 的回答。精彩的线程现在是一个死链接。位腐烂:-(
【解决方案2】:

这是一个没有浏览器嗅探且不依赖 jQuery 的版本:

function selectElementText(el, win) {
    win = win || window;
    var doc = win.document, sel, range;
    if (win.getSelection && doc.createRange) {
        sel = win.getSelection();
        range = doc.createRange();
        range.selectNodeContents(el);
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (doc.body.createTextRange) {
        range = doc.body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
}

selectElementText(document.getElementById("someElement"));
selectElementText(elementInIframe, iframe.contentWindow);

【讨论】:

  • 有什么方法可以选择div contentEditable='true'的文本吗?
  • @PrimitiveNom:此代码适用于 contenteditable 元素,但您必须先确保它具有焦点。
【解决方案3】:

这个thread(现在已经死了)包含了非常棒的东西。但由于“安全错误”,我无法使用 FF 3.5b99 + FireBug 在此页面上正确执行此操作。

耶!!我可以使用此代码选择整个右侧边栏,希望对您有所帮助:

    var r = document.createRange();
    var w=document.getElementById("sidebar");  
    r.selectNodeContents(w);  
    var sel=window.getSelection(); 
    sel.removeAllRanges(); 
    sel.addRange(r); 

PS:- 我无法使用 jquery 选择器返回的对象,例如

   var w=$("div.welovestackoverflow",$("div.sidebar"));
   
   //this throws **security exception**

   r.selectNodeContents(w);

【讨论】:

  • 你需要从 jQuery 中获取元素,因为你试图选择一个 jQuery 对象: var w=$("div.welovestackoverflow",$("div.sidebar")).get (0);
  • 不起作用...我收到错误消息“对象不支持此方法”,它突出显示了第一行。我做了一些挖掘,发现有一个“document.body.createTextRange()”但是“selectNodeContents”不起作用....这是在 IE 中
【解决方案4】:

Jason 的代码不能用于 iframe 内的元素(因为范围不同于窗口和文档)。我解决了这个问题并对其进行了修改,以便用作任何其他 jQuery 插件(可链接):

示例1:单击选择标签内的所有文本并添加类“selected”:

$(function() {
    $("code").click(function() {
        $(this).selText().addClass("selected");
    });
});

示例 2:单击按钮时,选择 Iframe 内的元素:

$(function() {
    $("button").click(function() {
        $("iframe").contents().find("#selectme").selText();
    });
});

注意:请记住,iframe 源应位于同一域中以防止安全错误。

jQuery 插件:

jQuery.fn.selText = function() {
    var obj = this[0];
    if ($.browser.msie) {
        var range = obj.offsetParent.createTextRange();
        range.moveToElementText(obj);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        var range = obj.ownerDocument.createRange();
        range.selectNodeContents(obj);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        selection.setBaseAndExtent(obj, 0, obj, 1);
    }
    return this;
}

我在 IE8、Firefox、Opera、Safari、Chrome(当前版本)中对其进行了测试。我不确定它是否适用于较旧的 IE 版本(我不在乎)。

【讨论】:

  • $.browser 现在已弃用/删除 - 这需要重写
  • @JamesMcCormack:是的。我不确定重写它是否值得,因为这里发布了其他不涉及 $.browser 的解决方案。
【解决方案5】:

您可以使用以下函数来选择任何元素的内容:

jQuery.fn.selectText = function(){
    this.find('input').each(function() {
        if($(this).prev().length == 0 || !$(this).prev().hasClass('p_copy')) { 
            $('<p class="p_copy" style="position: absolute; z-index: -1;"></p>').insertBefore($(this));
        }
        $(this).prev().html($(this).val());
    });
    var doc = document;
    var element = this[0];
    console.log(this, element);
    if (doc.body.createTextRange) {
        var range = document.body.createTextRange();
        range.moveToElementText(element);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();        
        var range = document.createRange();
        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);
    }
};

这个函数可以如下调用:

$('#selectme').selectText();

【讨论】:

    【解决方案6】:

    我也在寻找同样的东西,我的解决方案是这样的:

    $('#el-id').focus().select();
    

    【讨论】:

    • 你不能在非输入时使用focus(),这就是这个问题的意义所在。
    • 但是你可以在 textarea 元素上使用它——这是我用谷歌搜索到这里的问题。没有通读这个问题是我的错。
    • @Jason 是对的,这不能回答 OP,但它确实解决了我在输入元素上的问题。谷歌向我发送了这个问题,谷歌不遵守 SO 规则,但它确实知道我想要什么。 +1
    【解决方案7】:

    我喜欢 lepe 的回答,除了几件事:

    1. 浏览器嗅探、jQuery 或否都不是最佳选择
    2. 干燥
    3. 如果 obj 的父级不支持 createTextRange,则在 IE8 中不起作用
    4. 应该利用 Chrome 使用 setBaseAndExtent 的能力 (IMO)
    5. 不会选择跨越多个 DOM 元素(“选定”元素中的元素)的文本。换句话说,如果您在包含多个 span 元素的 div 上调用 selText,它将不会选择每个元素的文本。 YMMV,这对我来说是个大问题。

    这是我想出的,向 lepe 的答案点头以获得灵感。我敢肯定我会被嘲笑,因为这可能有点笨拙(实际上可能更多,但我离题了)。但它可以工作并避免浏览器嗅探,这就是重点

    selectText:function(){
    
        var range,
            selection,
            obj = this[0],
            type = {
                func:'function',
                obj:'object'
            },
            // Convenience
            is = function(type, o){
                return typeof o === type;
            };
    
        if(is(type.obj, obj.ownerDocument)
            && is(type.obj, obj.ownerDocument.defaultView)
            && is(type.func, obj.ownerDocument.defaultView.getSelection)){
    
            selection = obj.ownerDocument.defaultView.getSelection();
    
            if(is(type.func, selection.setBaseAndExtent)){
                // Chrome, Safari - nice and easy
                selection.setBaseAndExtent(obj, 0, obj, $(obj).contents().size());
            }
            else if(is(type.func, obj.ownerDocument.createRange)){
    
                range = obj.ownerDocument.createRange();
    
                if(is(type.func, range.selectNodeContents)
                    && is(type.func, selection.removeAllRanges)
                    && is(type.func, selection.addRange)){
                    // Mozilla
                    range.selectNodeContents(obj);
                    selection.removeAllRanges();
                    selection.addRange(range);
                }
            }
        }
        else if(is(type.obj, document.body) && is(type.obj, document.body.createTextRange)) {
    
            range = document.body.createTextRange();
    
            if(is(type.obj, range.moveToElementText) && is(type.obj, range.select)){
                // IE most likely
                range.moveToElementText(obj);
                range.select();
            }
        }
    
        // Chainable
        return this;
    }
    

    就是这样。您看到的一些内容是为了便于阅读和/或方便。在 Mac 上测试最新版本的 Opera、Safari、Chrome、Firefox 和 IE。也在IE8中测试过。此外,我通常只在代码块内需要时/当需要时声明变量,但 jslint 建议将它们都声明在顶部。好的 jslint。

    编辑 我忘了包括如何将它与操作代码联系起来:

    function SelectText(element) {
        $("#" + element).selectText();
    }
    

    干杯

    【讨论】:

    • 是的,这对我来说看起来很严厉,尽管它看起来是正确的。我的主要抱怨是,仅仅因为它存在而使用非标准setBaseAndExtent() 对我来说似乎毫无意义,因为您可以简单地删除该分支并且一切都以基于标准的方式正常工作。特征检测很好,但我会厌倦快速彻底地测试所有内容。
    • 好吧@TimDown 利用setBaseAndExtent 的要点在于它的效率要高得多,即使添加了if 声明仍然比“删除该分支”要有效得多。我真的不明白“我会累..”的评论?写了就忘了,你唯一要做的就是调用函数,而不是写它。 :)
    • @Madbreaks 如果setBaseAndExtent 的性能明显高于addRange,我会感到惊讶。为什么会这样?我的另一条评论与您扩展到所有 DOM 交互的特性测试精神有关:在使用它之前测试每个 DOM 方法和属性是非常多的代码。我不反对;我很高兴在我的代码中划清界限并做出更多假设。
    【解决方案8】:

    适用于 chrome 的更新版本:

    function SelectText(element) {
        var doc = document;
        var text = doc.getElementById(element);    
        if (doc.body.createTextRange) { // ms
            var range = doc.body.createTextRange();
            range.moveToElementText(text);
            range.select();
        } else if (window.getSelection) {
            var selection = window.getSelection();
            var range = doc.createRange();
            range.selectNodeContents(text);
            selection.removeAllRanges();
            selection.addRange(range);
    
        }
    }
    
    $(function() {
        $('p').click(function() {
            SelectText("selectme");
    
        });
    });
    

    http://jsfiddle.net/KcX6A/326/

    【讨论】:

      【解决方案9】:

      对于任何标签,可以通过这个简短的代码选择该标签内的所有文本。它将以黄色突出显示整个标签区域,并单击选择其中的文本。

      document.onclick = function(event) {
          var range, selection;
      event.target.style.backgroundColor = 'yellow';
              selection = window.getSelection();
              range = document.createRange();
              range.selectNodeContents(event.target);
              selection.removeAllRanges();
              selection.addRange(range);
      };
      

      【讨论】:

        【解决方案10】:

        看看Selection object(Gecko 引擎)和TextRange object(Trident 引擎。)我不知道有任何 JavaScript 框架为此实现了跨浏览器支持,但我从未看过对于它,所以它可能甚至连 jQuery 都有它。

        【讨论】:

          【解决方案11】:

          lepe - 这对我很有用,谢谢! 我将您的代码放在一个插件文件中,然后将它与每个语句一起使用,这样您就可以在一个页面上拥有多个 pre 标记和多个“全选”链接,它会挑选出正确的 pre 来突出显示:

          <script type="text/javascript" src="../js/jquery.selecttext.js"></script>
          <script type="text/javascript">
            $(document).ready(function() { 
                  $(".selectText").each(function(indx) {
                          $(this).click(function() {                 
                              $('pre').eq(indx).selText().addClass("selected");
                                  return false;               
                              });
                  });
            });
          

          【讨论】:

            【解决方案12】:

            Tim 的方法非常适合我的情况 - 在我替换以下语句后,为 IE 和 FF 选择 div 中的文本:

            range.moveToElementText(text);
            

            以下内容:

            range.moveToElementText(el);
            

            使用以下 jQuery 函数单击 div 中的文本来选择它:

            $(function () {
                $("#divFoo").click(function () {
                    selectElementText(document.getElementById("divFoo"));
                })
            });
            

            【讨论】:

              【解决方案13】:

              这是另一个以字符串形式获取所选文本的简单解决方案,您可以轻松使用此字符串将 div 元素子元素附加到代码中:

              var text = '';
              
              if (window.getSelection) {
                  text = window.getSelection();
              
              } else if (document.getSelection) {
                  text = document.getSelection();
              
              } else if (document.selection) {
                  text = document.selection.createRange().text;
              }
              
              text = text.toString();
              

              【讨论】:

                【解决方案14】:

                我的特定用例是在可编辑的 span 元素内选择一个文本范围,据我所知,这里的任何答案都没有描述。

                主要区别在于您必须将Text 类型的节点传递给Range 对象,如in the documentation of Range.setStart() 所述:

                如果 startNode 是 Text、Comment 或 CDATASection 类型的节点, 那么 startOffset 是从开始的字符数 开始节点。对于其他节点类型,startOffset 是子节点的数量 startNode 开始之间的节点。

                Text 节点是 span 元素的第一个子节点,所以要获取它,请访问 span 元素的 childNodes[0]。其余部分与大多数其他答案相同。

                这里是一个代码示例:

                var startIndex = 1;
                var endIndex = 5;
                var element = document.getElementById("spanId");
                var textNode = element.childNodes[0];
                
                var range = document.createRange();
                range.setStart(textNode, startIndex);
                range.setEnd(textNode, endIndex);
                
                var selection = window.getSelection();
                selection.removeAllRanges();
                selection.addRange(range);
                

                其他相关文档:
                Range
                Selection
                Document.createRange()
                Window.getSelection()

                【讨论】:

                  【解决方案15】:

                  在 Chrome 的“else if”中添加了jQuery.browser.webkit。无法在 Chrome 23 中使用此功能。

                  在下面创建此脚本,用于选择具有class="code"&lt;pre&gt; 标记中的内容。

                  jQuery( document ).ready(function() {
                      jQuery('pre.code').attr('title', 'Click to select all');
                      jQuery( '#divFoo' ).click( function() {
                          var refNode = jQuery( this )[0];
                          if ( jQuery.browser.msie ) {
                              var range = document.body.createTextRange();
                              range.moveToElementText( refNode );
                              range.select();
                          } else if ( jQuery.browser.mozilla || jQuery.browser.opera  || jQuery.browser.webkit ) {
                              var selection = refNode.ownerDocument.defaultView.getSelection();
                              console.log(selection);
                              var range = refNode.ownerDocument.createRange();
                              range.selectNodeContents( refNode );
                              selection.removeAllRanges();
                              selection.addRange( range );
                          } else if ( jQuery.browser.safari ) {
                              var selection = refNode.ownerDocument.defaultView.getSelection();
                              selection.setBaseAndExtent( refNode, 0, refNode, 1 );
                          }
                      } );
                  } );
                  

                  【讨论】:

                    【解决方案16】:

                    根据select()的jQuery文档:

                    触发每个匹配元素的选择事件。这会导致所有绑定到该 select 事件的函数都被执行,并在匹配元素上调用浏览器的默认选择操作。

                    您解释了为什么 jQuery select() 在这种情况下不起作用。

                    【讨论】:

                    • 我不想用 css 样式突出显示文本。我希望选择文本。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-08-08
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多