在元素中选择文本(类似于用鼠标突出显示)

490

我希望用户点击一个链接,然后选择另一个元素中的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();
}

我错过了什么非常明显的东西吗?


16个回答

681

纯JavaScript

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

    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>

这里有一个可工作的演示。如果你正在寻找一个 jQuery 插件,我也制作了其中之一


jQuery (原始回答)

这个贴子中,我找到了一个解决方案。我能够修改给出的信息并结合一些 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);
    }
}

2
jQuery的解决方案给我返回了未捕获的TypeError错误:无法读取未定义的“msie”属性。 - egmfrs
1
自从发布这个答案以来,jQuery已经移除了他们的“browser”嗅探器。 - Jason
2
jQuery 的解决方案起源于 VillageIdiot 的回答。这个精彩的线程现在是一个失效的链接。比特腐败 :-( - Roland
我正在选择一个表格,但是当我按下CTRL + C复制所选区域时,它总是复制所选区域开头和结尾的换行符,有没有什么建议可以不复制它们? - undefined

135

这是一个没有浏览器嗅探,也不依赖于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);

1
有没有办法选择一个 div contentEditable='true' 的文本内容? - oldboy
@PrimitiveNom:这段代码可以在contenteditable元素上工作,但您必须先确保它具有焦点。 - Tim Down

24

这个帖子已失效链接)包含非常棒的内容。但是我无法在此页面使用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); 

注意:我无法使用由jQuery选择器返回的对象,例如

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

   r.selectNodeContents(w);

2
你需要从jQuery中获取元素,因为你正在尝试选择一个jQuery对象:var w=$("div.welovestackoverflow",$("div.sidebar")).get(0); - Blixt
无法正常工作...我收到了一个错误信息“对象不支持此方法”,并且它突出显示了第一行。我进行了一些调查,发现有一个“document.body.createTextRange()”,但是“selectNodeContents”无法正常工作...而且这是在IE中。 - Jason

18

Jason的代码无法用于iframe中的元素,因为范围与window和document不同。我解决了这个问题,并进行了修改,以便像任何其他jQuery插件一样使用(可链式调用):

示例1:单击选择<code>标签内的所有文本并添加类“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中有效(老实说我不关心)。


3
$.browser已经被弃用/移除 - 这需要重写。 - James McCormack
2
@JamesMcCormack:是的。我不确定重写它是否值得,因为这里发布了其他解决方案,不涉及$ .browser。 - lepe

9
您可以使用以下函数选择任何元素的内容:
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();

5
这个问题涉及到在非输入元素上使用focus(),是不被允许的。请注意不要改变原意,用通俗易懂的语言进行翻译。 - Jason
4
但是你可以在文本域(textarea)元素上使用它——这也是我搜索到这里的问题所在。我的错因为我没有完整地阅读问题。 - Auston
@Jason 是正确的,这并不回答 OP 的问题,但它确实解决了我的输入元素问题。谷歌把我带到了这个问题,而谷歌并不遵循 SO 的规则,但它知道我想要什么。+1 - Roland

5

我喜欢lepe的答案,但有几点需要注意:

  1. 无论使用jQuery与否,浏览器嗅探都不是最佳选择
  2. 避免重复代码
  3. 如果obj的父元素不支持createTextRange,则在IE8中无法正常工作
  4. 应该利用Chrome的setBaseAndExtent功能(个人意见)
  5. 不能选择跨越多个DOM元素的文本(位于“selected”元素内的元素)。换句话说,如果在包含多个span元素的div上调用selText,它将无法选择每个元素的文本。这对我来说是致命的问题,可能会因人而异。

以下是我的解决方案,受到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;
}

就是这样。你看到的一些东西只是为了易读性和/或方便起见。在最新版本的Opera、Safari、Chrome、Firefox和IE中测试过。也在IE8中进行了测试。此外,我通常只在代码块内需要时声明变量,但jslint建议将它们全部声明在顶部。好吧,jslint。

编辑 我忘记如何将其与op的代码联系起来:

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

干杯


是的,对我来说看起来很笨重,尽管它似乎是正确的。我的主要抱怨是,仅仅因为存在非标准的 setBaseAndExtent() 就使用它似乎毫无意义,当你可以简单地删除那个分支,一切都能够正常工作,并且符合标准。特性检测很好,但我很快就会厌倦彻底测试所有东西。 - Tim Down
嗯,@TimDown,利用setBaseAndExtent的重点在于它更加高效,即使添加了if语句,仍然比“删除该分支”要高效得多。我不太理解“我会感到疲倦”的评论?编写它并忘记它,你唯一需要做的就是调用函数,而不是编写它。 :) - Madbreaks
@Madbreaks 如果 setBaseAndExtent 的性能表现明显优于 addRange,我会感到惊讶。它为什么会这样呢?我的另一个评论与您的功能测试主张相关,该主张扩展到所有DOM交互:在使用每个单独的DOM方法和属性之前测试所有这些代码是非常多的。我并不反对;我只是很高兴在我的代码中划清界限并做出更多的假设。 - Tim Down

5

适用于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/


4
任何标签都可以通过这段简短的代码选择其中所有文本。它会用黄色突出显示整个标签区域,并在单击时选择其中的文本。
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);
};

2

lepe - 非常适合我,谢谢! 我将你的代码放在插件文件中,然后与each语句一起使用,这样您就可以在一个页面上拥有多个“预格式化”标记和多个“选择全部”链接,并选择正确的预格式化标记进行突出显示:

<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;               
                    });
        });
  });


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接