Memilih teks dalam suatu elemen (mirip dengan menyorot dengan mouse Anda)

424

Saya ingin pengguna mengklik tautan, lalu memilih teks HTML di elemen lain ( bukan input).

Dengan "pilih" maksud saya dengan cara yang sama Anda akan memilih teks dengan menyeret mouse Anda ke atasnya. Ini menjadi tantangan untuk penelitian karena semua orang berbicara tentang "pilih" atau "sorot" dalam istilah lain.

Apakah ini mungkin? Kode saya sejauh ini:

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

Apakah saya melewatkan sesuatu yang sangat jelas?

Jason
sumber

Jawaban:

613

Javascript biasa

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>

Ini demo yang berfungsi . Bagi Anda yang mencari plugin jQuery, saya juga membuat salah satunya .


jQuery (jawaban asli)

Saya telah menemukan solusi untuk ini di utas ini . Saya dapat mengubah info yang diberikan dan mencampurnya dengan sedikit jQuery untuk membuat fungsi yang benar-benar luar biasa untuk memilih teks dalam elemen apa pun, apa pun browsernya:

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);
    }
}
Jason
sumber
solusi jQuery memberi saya UnEught TypeError: Tidak dapat membaca properti 'msie' yang tidak terdefinisi
egmfrs
@egmfrs ya sejak jawaban ini diposting, jquery telah menghapus browsersniffer mereka .
Jason
127

Ini adalah versi tanpa sniffing browser dan tidak bergantung pada 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);
Tim Down
sumber
apakah ada cara untuk memilih teks div contentEditable='true'?
oldboy
@Rimrimomom: Kode ini akan berfungsi pada elemen yang dapat diedit tetapi Anda harus memastikan itu memiliki fokus terlebih dahulu.
Tim Down
18

Kode Jason tidak dapat digunakan untuk elemen di dalam iframe (karena ruang lingkup berbeda dari jendela dan dokumen). Saya memperbaiki masalah itu dan saya memodifikasinya agar dapat digunakan sebagai plugin jQuery lainnya (chainable):

Contoh 1: Pemilihan semua teks di dalam tag <code> dengan satu klik dan tambahkan kelas "terpilih":

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

Contoh 2: Klik tombol Pada, pilih elemen di dalam Iframe:

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

Catatan: ingat bahwa sumber iframe harus berada di domain yang sama untuk mencegah kesalahan keamanan.

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

Saya mengujinya di IE8, Firefox, Opera, Safari, Chrome (versi saat ini). Saya tidak yakin apakah itu berfungsi di versi IE yang lebih lama (dengan tulus saya tidak peduli).

lepe
sumber
3
$ .browser sekarang tidak digunakan lagi / dihapus - ini membutuhkan penulisan ulang
James McCormack
2
@JamesMcCormack: ya. Saya tidak yakin apakah menulis ulang itu akan sia-sia karena ada solusi lain yang diposting di sini yang tidak melibatkan $ .browser.
lepe
16

Utas ini berisi hal-hal yang sangat luar biasa. Tapi saya tidak dapat melakukannya dengan benar di halaman ini menggunakan FF 3.5b99 + FireBug karena "Kesalahan Keamanan".

Yipee !! Saya dapat memilih bilah sisi kanan dengan kode ini semoga membantu Anda:

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

PS: - Saya tidak dapat menggunakan objek yang dikembalikan oleh pemilih jquery seperti

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

   //this throws **security exception**

   r.selectNodeContents(w);
TheVillageIdiot
sumber
2
Anda perlu mendapatkan elemen dari jQuery, ketika Anda mencoba untuk memilih objek jQuery: var w = $ ("div.welovestackoverflow", $ ("div.sidebar")). Get (0);
Blixt
tidak berfungsi ... saya mendapatkan kesalahan "objek tidak mendukung metode ini" dan menyoroti baris pertama. saya melakukan beberapa penggalian dan menemukan bahwa ada "document.body.createTextRange ()" tapi kemudian "selectNodeContents" tidak bekerja .... dan ini adalah di IE
Jason
saya membaca utas yang Anda temukan ... luar biasa ... saya dapat membuat fungsi dari info yang bekerja di semua browser. Terima kasih banyak! Solusi saya diposting
Jason
6

Anda dapat menggunakan fungsi berikut untuk memilih konten dari elemen apa pun:

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

Fungsi ini dapat disebut sebagai berikut:

$('#selectme').selectText();
Wolfack
sumber
5

Saya mencari hal yang sama, solusi saya adalah ini:

$('#el-id').focus().select();
Auston
sumber
3
Anda tidak dapat menggunakan focus()pada non-input, yang merupakan pertanyaan ini.
Jason
4
tetapi Anda dapat menggunakannya pada elemen textarea - yang merupakan masalah saya googled untuk tiba di sini. Kesalahan saya karena tidak membaca pertanyaan sampai tuntas.
Auston
4

Saya menyukai jawaban lepe kecuali untuk beberapa hal:

  1. Peramban peramban, jQuery atau tidak, tidak optimal
  2. KERING
  3. Tidak berfungsi di IE8 jika orang tua obj tidak mendukung createTextRange
  4. Kemampuan Chrome untuk menggunakan setBaseAndExtent harus ditingkatkan (IMO)
  5. Tidak akan memilih rentang teks di beberapa elemen DOM (elemen dalam elemen "terpilih"). Dengan kata lain jika Anda memanggil selText pada div yang berisi beberapa elemen span, itu tidak akan memilih teks dari masing-masing elemen tersebut. Bagi saya, itu adalah pemecah kesepakatan, YMMV.

Inilah yang saya pikirkan, dengan anggukan ke jawaban lepe untuk inspirasi. Saya yakin saya akan diejek karena ini mungkin agak berat (dan sebenarnya bisa jadi lebih tapi saya ngelantur). Tapi itu berfungsi dan menghindari peramban browser dan itu intinya .

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

Itu dia. Beberapa yang Anda lihat adalah untuk keterbacaan dan / atau kenyamanan. Diuji pada Mac dalam versi terbaru Opera, Safari, Chrome, Firefox dan IE. Juga diuji di IE8. Juga saya biasanya hanya mendeklarasikan variabel jika / ketika diperlukan di dalam blok kode tetapi jslint menyarankan mereka semua dinyatakan di bagian atas. Ok jslint.

Sunting Saya lupa menyertakan cara mengikat ini ke kode op:

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

Bersulang

Pecah-pecah
sumber
Ya, itu terlihat berat bagi saya, meskipun tampaknya benar. Keluhan utama saya adalah bahwa menggunakan non-standar setBaseAndExtent()hanya karena ada tampaknya tidak ada gunanya bagi saya ketika Anda dapat menghapus cabang itu dan semuanya bekerja dengan baik dan dengan cara berbasis standar. Deteksi fitur bagus, tetapi saya akan lelah menguji segala sesuatu yang sangat cepat.
Tim Down
Nah @TimDown titik pengungkit setBaseAndExtentadalah itu jauh lebih efisien, dan bahkan dengan ifstatemnent yang ditambahkan masih jauh lebih daripada jika Anda "menghapus cabang itu". Saya tidak begitu mengerti komentar "Saya lelah .."? Tulis dan lupakan, satu-satunya yang harus Anda lakukan adalah memanggil fungsi, bukan menulisnya. :)
Madbreak
@ Madbreak Saya akan terkejut jika setBaseAndExtentsecara signifikan lebih banyak performan daripada addRange. Kenapa bisa begitu? Komentar saya yang lain terkait dengan etos pengujian fitur Anda yang diperluas ke semua interaksi DOM: ini adalah banyak sekali kode untuk menguji setiap metode dan properti DOM tunggal sebelum menggunakannya. Saya tidak setuju; Saya hanya senang menggambar garis dan membuat beberapa asumsi dalam kode saya.
Tim Down
4

Versi terbaru yang berfungsi di 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/

Kimtho6
sumber
3

Untuk setiap tag, seseorang dapat memilih semua teks di dalam tag itu dengan kode pendek dan sederhana ini. Ini akan menyorot seluruh area tag dengan warna kuning dan memilih teks di dalamnya dengan satu klik.

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);
};
knjhaveri
sumber
2

lepe - Itu sangat bagus untuk saya, terima kasih! Saya meletakkan kode Anda dalam file plugin, kemudian menggunakannya bersamaan dengan setiap pernyataan sehingga Anda dapat memiliki beberapa tag pra dan beberapa tautan "Pilih semua" pada satu halaman dan mengambil pra yang benar untuk disorot:

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

DaveR
sumber
1

Lihat objek Seleksi (mesin Gecko) dan objek TextRange (mesin Trisula.) Saya tidak tahu tentang kerangka kerja JavaScript yang memiliki dukungan lintas-browser untuk ini diterapkan, tapi saya belum pernah mencarinya, jadi mungkin saja jQuery memilikinya.

Blixt
sumber
0

Metode Tim berfungsi dengan baik untuk kasus saya - memilih teks dalam div untuk IE dan FF setelah saya mengganti pernyataan berikut:

range.moveToElementText(text);

dengan yang berikut ini:

range.moveToElementText(el);

Teks dalam div dipilih dengan mengkliknya dengan fungsi jQuery berikut:

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

di sini adalah solusi sederhana lain untuk mendapatkan teks yang dipilih dalam bentuk string, Anda dapat menggunakan string ini dengan mudah untuk menambahkan anak elemen div ke dalam kode Anda:

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();
Nadeem Yasin
sumber
Ini untuk mengembalikan teks yang disorot, bukan membuat highlight.
MKN Web Solutions
0

Kasus penggunaan khusus saya adalah memilih rentang teks di dalam elemen rentang yang dapat diedit, yang sejauh yang dapat saya lihat, tidak dijelaskan dalam salah satu jawaban di sini.

Perbedaan utama adalah bahwa Anda harus meneruskan simpul tipe Textke Rangeobjek, seperti yang dijelaskan dalam dokumentasi Range.setStart () :

Jika startNode adalah Node bertipe Teks, Komentar, atau CDATASection , maka startOffset adalah jumlah karakter dari awal startNode. Untuk tipe Node lainnya, startOffset adalah jumlah node anak antara awal startNode.

The Textnode node anak pertama dari elemen span, sehingga untuk mendapatkannya, akses childNodes[0]dari elemen span. Sisanya sama dengan di sebagian besar jawaban lainnya.

Berikut contoh kode:

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

Dokumentasi lain yang relevan:
Range
Selection
Document.createRange ()
Window.getSelection ()

Domysee
sumber
-1

Ditambahkan jQuery.browser.webkitke "else if" untuk Chrome. Tidak dapat mengaktifkan ini di Chrome 23.

Buat skrip ini di bawah ini untuk memilih konten dalam <pre>tag yang memiliki 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 );
        }
    } );
} );
Derk
sumber
-1

Menurut dokumentasi jQuery dari select():

Picu acara pilih dari setiap elemen yang cocok. Hal ini menyebabkan semua fungsi yang telah terikat dengan acara pilih untuk dieksekusi, dan memanggil tindakan pilih default browser pada elemen yang cocok.

Ada penjelasan Anda mengapa jQuery select()tidak akan berfungsi dalam kasus ini.

Kees de Kooter
sumber
4
Saya tidak mencoba menyorot teks dengan gaya css. Saya ingin teks dipilih.
Jason