Selezione del testo in un elemento (simile all'evidenziazione con il mouse)


424

Vorrei che gli utenti facessero clic su un collegamento, quindi seleziona il testo HTML in un altro elemento ( non un input).

Con "seleziona" intendo allo stesso modo in cui selezioneresti il ​​testo trascinandolo con il mouse. Questo è stato un orso per la ricerca perché tutti parlano di "selezionare" o "evidenziare" in altri termini.

È possibile? Il mio codice finora:

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

Mi sto perdendo qualcosa di palesemente ovvio?


Risposte:


613

Javascript semplice

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>

Ecco una demo funzionante . Per quelli di voi che cercano un plugin jQuery, ne ho creato uno anche io .


jQuery (risposta originale)

Ho trovato una soluzione per questo in questo thread . Sono stato in grado di modificare le informazioni fornite e mescolarle con un po 'di jQuery per creare una funzione assolutamente fantastica per selezionare il testo in qualsiasi elemento, indipendentemente dal browser:

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

La soluzione jQuery mi dà Uncaught TypeError: Impossibile leggere la proprietà 'msie' di undefined
egmfrs

@egmfrs sì da quando è stata pubblicata questa risposta, jquery ha rimosso il suo browsersniffer.
Jason

127

Ecco una versione senza sniffing del browser e nessuna dipendenza da 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);

c'è un modo per selezionare il testo di un div contentEditable='true'?
oldboy

@PrimitiveNom: questo codice funzionerà su un elemento contenteditable ma dovrai prima assicurarti che abbia il focus.
Tim Down

18

Il codice di Jason non può essere utilizzato per elementi all'interno di un iframe (poiché l'ambito differisce da finestra e documento). Ho risolto il problema e l'ho modificato per essere usato come qualsiasi altro plugin jQuery (concatenabile):

Esempio 1: selezione di tutto il testo all'interno dei tag <code> con un solo clic e aggiunta della classe "selezionata":

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

Esempio 2: facendo clic sul pulsante, selezionare un elemento all'interno di un Iframe:

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

Nota: ricordare che l'origine iframe deve risiedere nello stesso dominio per evitare errori di sicurezza.

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

L'ho testato su IE8, Firefox, Opera, Safari, Chrome (versioni correnti). Non sono sicuro che funzioni con le versioni precedenti di IE (sinceramente non mi interessa).


3
$ .browser ora è deprecato / rimosso - questo richiede una riscrittura
James McCormack

2
@JamesMcCormack: sì. Non sono sicuro che riscriverà la pena riscriverlo poiché ci sono altre soluzioni pubblicate qui che non comportano $ .browser.
lepe

16

Questa discussione contiene cose davvero meravigliose. Ma non sono in grado di farlo direttamente su questa pagina usando FF 3.5b99 + FireBug a causa di "Errore di sicurezza".

Yipee !! Sono stato in grado di selezionare l'intera barra laterale destra con questo codice spero che ti aiuti:

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

PS: - Non sono stato in grado di utilizzare oggetti restituiti da selettori jquery come

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

   //this throws **security exception**

   r.selectNodeContents(w);

2
Devi ottenere l'elemento da jQuery, mentre stai cercando di selezionare un oggetto jQuery: var w = $ ("div.welovestackoverflow", $ ("div.sidebar")). Get (0);
Blixt,

non funziona ... viene visualizzato l'errore "L'oggetto non supporta questo metodo" ed evidenzia la prima riga. ho fatto qualche ricerca e ho scoperto che c'è un "document.body.createTextRange ()" ma poi "selectNodeContents" non funziona .... e questo è in IE
Jason,

ho letto quel thread che hai trovato ... fantastico ... sono stato in grado di creare una funzione da quelle informazioni che funziona su tutti i browser. Grazie mille! La mia soluzione è pubblicata
Jason,

6

È possibile utilizzare la seguente funzione per selezionare il contenuto di qualsiasi 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);
    }
};

Questa funzione può essere chiamata come segue:

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

5

Stavo cercando la stessa cosa, la mia soluzione era questa:

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

3
non puoi usarlo focus()su un non input, che è di cosa tratta questa domanda.
Jason,

4
ma puoi usarlo su un elemento textarea - che era il problema con cui ho cercato su Google di arrivare qui. Colpa mia per non aver letto la domanda fino in fondo.
Auston,

4

Mi è piaciuta la risposta di lepe ad eccezione di alcune cose:

  1. L'annusamento del browser, jQuery o no non è ottimale
  2. ASCIUTTO
  3. Non funziona in IE8 se il genitore di obj non supporta createTextRange
  4. La capacità di Chrome di utilizzare setBaseAndExtent dovrebbe essere sfruttata (IMO)
  5. Non selezionerà lo spanning del testo su più elementi DOM (elementi all'interno dell'elemento "selezionato"). In altre parole, se chiami selText su un div contenente più elementi di span, non selezionerà il testo di ciascuno di quegli elementi. Questo è stato un fallimento per me, YMMV.

Ecco cosa mi è venuto in mente, con un cenno alla risposta di lepe per l'ispirazione. Sono sicuro che sarò ridicolizzato perché questo è forse un po 'pesante (e in realtà potrebbe essere moreso ma sto divagando). Ma funziona ed evita l'annusamento del browser e questo è il punto .

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

Questo è tutto. Parte di ciò che vedi è per leggibilità e / o convenienza. Testato su Mac nelle ultime versioni di Opera, Safari, Chrome, Firefox e IE. Testato anche su IE8. Inoltre, in genere dichiaro variabili solo se / quando necessario all'interno di blocchi di codice, ma jslint ha suggerito che fossero tutte dichiarate al top. Ok jslint.

Modifica Ho dimenticato di includere come legare questo al codice dell'operazione:

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

Saluti


Sì, mi sembra molto pesante, anche se sembra corretto. La mia lamentela principale è che usare il non standard setBaseAndExtent()solo perché esiste mi sembra inutile quando puoi semplicemente rimuovere quel ramo e tutto funziona altrettanto bene e in modo standardizzato. Il rilevamento delle funzionalità è carino, ma mi stancherei di testare tutto molto rapidamente.
Tim Down

Bene @TimDown il punto di leva setBaseAndExtentè che è significativamente più efficiente, e anche con lo stato aggiunto ifè ancora molto più che se "rimuovi quel ramo". Non capisco davvero il commento di "Mi stancherei .."? Scrivilo e dimenticalo, l'unica cosa che devi fare è chiamare la funzione, non scriverla. :)
Madbreaks

@Madbreaks Sarei sorpreso se setBaseAndExtentfosse significativamente più performante di addRange. Perché dovrebbe essere? Un altro mio commento era legato al tuo ethos di testing delle funzionalità esteso a tutte le interazioni DOM: è un sacco di codice per testare ogni singolo metodo e proprietà DOM prima di usarlo. Non disapprovo; Sono solo felice di tracciare la linea e fare alcune ipotesi nel mio codice.
Tim Down

4

Una versione aggiornata che funziona in 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

Per ogni tag si può selezionare tutto il testo all'interno di quel tag con questo codice breve e semplice. Evidenzierà l'intera area del tag con il colore giallo e selezionerà il testo al suo interno con un solo clic.

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 - Per me funziona benissimo grazie! Ho inserito il tuo codice in un file plug-in, quindi l'ho usato insieme a ciascuna istruzione in modo da poter avere più pre-tag e più collegamenti "Seleziona tutto" su una pagina e seleziona il pre corretto per evidenziare:

<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

Dai un'occhiata all'oggetto Selection (motore Gecko) e all'oggetto TextRange (motore Trident.) Non conosco alcun framework JavaScript che abbia il supporto cross-browser per questo implementato, ma non l'ho mai nemmeno cercato, quindi è possibile che anche jQuery ce l'abbia.


0

Il metodo di Tim funziona perfettamente per il mio caso: selezionare il testo in un div sia per IE che per FF dopo aver sostituito la seguente dichiarazione:

range.moveToElementText(text);

con il seguente:

range.moveToElementText(el);

Il testo nel div viene selezionato facendo clic su di esso con la seguente funzione jQuery:

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

0

ecco un'altra semplice soluzione per ottenere il testo selezionato sotto forma di stringa, puoi usare questa stringa facilmente per aggiungere un elemento figlio div al tuo codice:

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

Questo serve per restituire il testo evidenziato, non per creare un momento saliente.
MKN Web Solutions,

0

Il mio particolare caso d'uso era selezionare un intervallo di testo all'interno di un elemento span modificabile, che, per quanto ho potuto vedere, non è descritto in nessuna delle risposte qui.

La differenza principale è che bisogna passare un nodo di tipo Textal Rangeoggetto, come descritto nella documentazione di Range.setStart () :

Se startNode è un nodo di tipo Text, Comment o CDATASection , startOffset è il numero di caratteri dall'inizio di startNode. Per altri tipi di nodo, startOffset è il numero di nodi figlio tra l'inizio di startNode.

Il Textnodo è il primo nodo figlio di un elemento span, quindi per ottenerlo, l'accesso childNodes[0]all'elemento span. Il resto è lo stesso della maggior parte delle altre risposte.

Ecco un esempio di codice:

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

Altra documentazione pertinente:
Range
Selection
Document.createRange ()
Window.getSelection ()


-1

aggiunto jQuery.browser.webkit a "else if" per Chrome. Impossibile farlo funzionare in Chrome 23.

Creato questo script di seguito per selezionare il contenuto in un <pre>tag che ha il 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

Secondo la documentazione jQuery di select():

Attiva l'evento select di ciascun elemento abbinato. Ciò provoca l'esecuzione di tutte le funzioni associate a quell'evento di selezione e chiama l'azione di selezione predefinita del browser sugli elementi corrispondenti.

Ecco la tua spiegazione del perché jQuery select()non funzionerà in questo caso.


4
non sto cercando di evidenziare il testo con uno stile CSS. voglio che il testo sia selezionato.
Jason,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.