Selecionando texto em um elemento (semelhante ao destaque com o mouse)


424

Gostaria que os usuários cliquem em um link e, em seguida, ele seleciona o texto HTML em outro elemento ( não uma entrada).

Por "selecionar", quero dizer da mesma maneira que você selecionaria o texto arrastando o mouse sobre ele. Isso tem sido motivo de pesquisa, porque todo mundo fala sobre "selecionar" ou "destacar" em outros termos.

Isso é possível? Meu código até agora:

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

Estou perdendo algo flagrantemente óbvio?


Respostas:


613

Javascript simples

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>

Aqui está uma demonstração de trabalho . Para aqueles que procuram um plugin jQuery, eu também fiz um .


jQuery (resposta original)

Eu encontrei uma solução para isso neste tópico . Consegui modificar as informações fornecidas e misturá-las com um pouco de jQuery para criar uma função totalmente impressionante para selecionar o texto em qualquer elemento, independentemente do navegador:

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

solução jQuery me dá TypeError não capturado: Não é possível ler a propriedade 'msie' de indefinido
egmfrs

@egmfrs sim, desde que esta resposta foi publicada, o jquery removeu o browsersniffer.
Jason

127

Aqui está uma versão sem farejador de navegador e sem dependência do 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);

existe alguma maneira de selecionar o texto de a div contentEditable='true'?
Oldboy

@PrimitiveNom: este código funcionará em um elemento editável por conteúdo, mas você precisará garantir que ele se concentre primeiro.
Tim Baixo

18

O código de Jason não pode ser usado para elementos dentro de um iframe (como o escopo difere da janela e do documento). Corrigi o problema e o modifiquei para ser usado como qualquer outro plugin do jQuery (encadeado):

Exemplo 1: Seleção de todo o texto dentro das tags <code> com um único clique e adicione a classe "selecionada":

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

Exemplo 2: Ao clicar no botão, selecione um elemento dentro de um Iframe:

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

Nota: lembre-se de que a origem do iframe deve residir no mesmo domínio para evitar erros de segurança.

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

Eu testei no IE8, Firefox, Opera, Safari, Chrome (versões atuais). Não tenho certeza se funciona em versões mais antigas do IE (sinceramente, não me importo).


3
$ .browser agora está obsoleta / removido - este precisa de uma reescrita
James McCormack

2
@JamesMcCormack: sim. Não tenho certeza se reescrever valerá a pena, pois existem outras soluções postadas aqui que não envolvem $ .browser.
Lepe

16

Esta discussão contém coisas realmente maravilhosas. Mas não consigo fazer isso direito nesta página usando o FF 3.5b99 + FireBug devido a "Erro de segurança".

Yipee !! Consegui selecionar toda a barra lateral direita com este código, espero que ajude você:

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

PS: - Não pude usar objetos retornados por seletores jquery como

   var w=$("div.welovestackoverflow",$("div.sidebar"));

   //this throws **security exception**

   r.selectNodeContents(w);

2
Você precisa obter o elemento do jQuery, enquanto tenta selecionar um objeto jQuery: var w = $ ("div.welovestackoverflow", $ ("div.sidebar")). Get (0);
Blixt

não funciona ... recebo um erro "objeto não suporta este método" e destaca a primeira linha. Eu fiz algumas escavações e descobri que existe um "document.body.createTextRange ()", mas depois "selectNodeContents" não funciona .... e isso está no IE
Jason

Eu li o tópico que você encontrou ... incrível ... eu era capaz de criar uma função a partir dessa informação que funciona em todos os navegadores. Muito obrigado! Minha solução está publicada
Jason

6

Você pode usar a seguinte função para selecionar o conteúdo de qualquer elemento:

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

Esta função pode ser chamada da seguinte maneira:

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

5

Eu estava procurando a mesma coisa, minha solução foi a seguinte:

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

3
você não pode usar focus()em uma não entrada, que é a questão desta pergunta.
21411 Jason

4
mas você pode usá-lo em um elemento textarea - que foi o problema que pesquisei ao chegar aqui. Minha culpa por não ter lido a pergunta até o fim.
Auston # 28/04

4

Gostei da resposta de lepe, exceto por algumas coisas:

  1. Detectar o navegador, jQuery ou não, não é o ideal
  2. SECO
  3. Não funciona no IE8 se o pai do obj não suportar createTextRange
  4. Capacidade do Chrome de usar setBaseAndExtent deve ser aproveitada (IMO)
  5. Não seleciona texto que se estende por vários elementos DOM (elementos dentro do elemento "selecionado"). Em outras palavras, se você chamar selText em uma div contendo vários elementos de amplitude, ele não selecionará o texto de cada um desses elementos. Isso foi um negócio para mim, YMMV.

Aqui está o que eu criei, com um aceno de cabeça para a resposta de Lepe em busca de inspiração. Tenho certeza de que serei ridicularizado, pois isso talvez seja um pouco pesado (e na verdade poderia ser mais, mas eu discordo). Mas funciona e evita farejar o navegador, e esse é o ponto .

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

É isso aí. Parte do que você vê é de legibilidade e / ou conveniência. Testado no Mac nas versões mais recentes do Opera, Safari, Chrome, Firefox e IE. Também testado no IE8. Também normalmente declaro apenas variáveis ​​se / quando necessárias dentro de blocos de código, mas o jslint sugeriu que todas elas fossem declaradas no topo. Ok jslint.

Editar Eu esqueci de incluir como vincular isso ao código da operação:

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

Felicidades


Sim, isso parece pesado para mim, embora pareça correto. Minha queixa principal é que usar o não-padrão setBaseAndExtent()apenas porque existe parece inútil para mim quando você pode simplesmente remover esse ramo e tudo funciona tão bem quanto de maneira baseada em padrões. A detecção de recursos é boa, mas eu me cansaria de testar tudo rapidamente.
Tim Baixo

Bem, o @TimDown, o ponto de alavancagem setBaseAndExtenté que é significativamente mais eficiente, e mesmo com a ifdeclaração adicional ainda é muito mais do que se você "remover esse ramo". Eu realmente não entendo o comentário de "Eu me cansaria .."? Escreva e esqueça, a única coisa que você precisa fazer é chamar a função, não escrevê-la. :)
Madbreaks

@ Madbreaks Eu ficaria surpreso se setBaseAndExtenttivesse um desempenho significativamente maior que o addRange. Por que seria? Meu outro comentário foi relacionado ao ethos de teste de recursos estendido a todas as interações do DOM: é muito código para testar todos os métodos e propriedades do DOM antes de usá-lo. Eu não desaprovo; Estou feliz em traçar a linha e fazer mais algumas suposições no meu código.
Tim Down

4

Uma versão atualizada que funciona no 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/


3

Para qualquer tag, é possível selecionar todo o texto dentro dessa tag por este código curto e simples. Ele destacará toda a área da etiqueta com a cor amarela e selecionará o texto dentro dela com um único clique.

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 - Isso funciona muito bem para mim, obrigado! Coloquei seu código em um arquivo de plug-in e, em seguida, usei-o em conjunto com cada instrução, para que você possa ter várias pré tags e vários links "Selecionar tudo" em uma página e escolher a pré correta para destacar:

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


1

Dê uma olhada no objeto Selection (mecanismo Gecko) e no objeto TextRange (mecanismo Trident.) Não conheço nenhuma estrutura JavaScript que tenha suporte entre navegadores para isso implementado, mas também nunca o procurei. é possível que até o jQuery tenha.


0

O método de Tim funciona perfeitamente para o meu caso - selecionando o texto em uma div para o IE e o FF após a substituição da seguinte declaração:

range.moveToElementText(text);

com o seguinte:

range.moveToElementText(el);

O texto na div é selecionado clicando nele com a seguinte função jQuery:

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

0

aqui está outra solução simples para selecionar o texto na forma de string, você pode usar essa string facilmente para acrescentar um filho do elemento div ao seu código:

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

Isto é para retornar o texto destacado, não criando um destaque.
MKN Web Solutions

0

Meu caso de uso específico foi selecionar um intervalo de texto dentro de um elemento de extensão editável que, até onde pude ver, não está descrito em nenhuma das respostas aqui.

A principal diferença é que você precisa passar um nó do tipo Textpara o Rangeobjeto, conforme descrito na documentação de Range.setStart () :

Se o startNode for um Nó do tipo Text, Comment ou CDATASection , startOffset é o número de caracteres desde o início do startNode. Para outros tipos de nós, startOffset é o número de nós filhos entre o início do startNode.

O Textnó é o primeiro nó filho de um elemento de extensão; portanto, para obtê-lo, acesso childNodes[0]ao elemento de extensão. O resto é o mesmo que na maioria das outras respostas.

Aqui está um exemplo de código:

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

Outra documentação relevante: Seleção de
intervalo Document.createRange () Window.getSelection ()



-1

Adicionado jQuery.browser.webkitao "else if" no Chrome. Não foi possível fazer isso funcionar no Chrome 23.

Crie este script abaixo para selecionar o conteúdo em uma <pre>tag que possui o class="code".

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

-1

De acordo com a documentação do jQuery de select():

Acione o evento de seleção de cada elemento correspondente. Isso faz com que todas as funções vinculadas a esse evento de seleção sejam executadas e chama a ação de seleção padrão do navegador no (s) elemento (s) correspondente (s).

A sua explicação é por que o jQuery select()não funcionará neste caso.


4
Não estou tentando destacar o texto com um estilo CSS. Eu quero que o texto seja selecionado.
1212 Jason
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.