Pilih teks secara terprogram dalam elemen HTML yang dapat diedit konten?

119

Dalam JavaScript, dimungkinkan untuk memilih teks secara terprogram dalam elemen inputatau textarea. Anda dapat memfokuskan input dengan ipt.focus(), lalu memilih kontennya dengan ipt.select(). Anda bahkan dapat memilih rentang tertentu dengan ipt.setSelectionRange(from,to).

Pertanyaan saya adalah: apakah ada cara untuk melakukan ini dalam sebuah contenteditableelemen juga?

Saya menemukan bahwa saya bisa melakukannya elem.focus(), untuk meletakkan tanda sisipan di contenteditableelemen, tetapi kemudian berjalan elem.select()tidak berfungsi (dan juga tidak setSelectionRange). Saya tidak dapat menemukan apa pun di web tentang hal itu, tetapi mungkin saya mencari hal yang salah ...

Omong-omong, jika ada bedanya, saya hanya membutuhkannya untuk berfungsi di Google Chrome, karena ini untuk ekstensi Chrome.

callum
sumber

Jawaban:

170

Jika Anda ingin memilih semua konten elemen (konten dapat diedit atau tidak) di Chrome, berikut caranya. Ini juga akan berfungsi di Firefox, Safari 3+, Opera 9+ (kemungkinan versi sebelumnya juga) dan IE 9. Anda juga dapat membuat pilihan hingga ke level karakter. API yang Anda butuhkan adalah DOM Range (spesifikasi saat ini DOM Level 2 , lihat juga MDN ) dan Seleksi, yang ditetapkan sebagai bagian dari spesifikasi Rentang baru ( dokumen MDN ).

function selectElementContents(el) {
    var range = document.createRange();
    range.selectNodeContents(el);
    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

var el = document.getElementById("foo");
selectElementContents(el);
Tim Down
sumber
15
Untuk kompatibilitas ekstra, Anda harus memanggil selectElementContents()a setTimeout()atau requestAnimationFrame()jika dipanggil dari onfocus. Lihat jsfiddle.net/rudiedirkx/MgASG/1/show
Rudie
@Dylan: Saya tidak yakin: pertanyaan menyebutkan bahwa OP sudah menggunakan focus().
Tim Down
4
@Rudie Kompatibilitas untuk aplikasi mana?
yckart
Berfungsi dengan baik di desktop. Di browser seluler, tidak berfungsi. Tidak ada pilihan yang dibuat. Mencoba Safari dan Chrome di iPhone iOS 11.
campbell
1
@campbell: Ini berfungsi di Safari setidaknya di iOS, asalkan Anda sudah memiliki pilihan . Jika tidak, tidak, browser tidak mengizinkan JavaScript untuk menampilkan pilihan, mungkin karena alasan pengalaman pengguna.
Tim Down
34

Selain jawaban Tim Downs , saya membuat solusi yang berfungsi bahkan di IE lama:

var selectText = function() {
  var range, selection;
  if (document.body.createTextRange) {
    range = document.body.createTextRange();
    range.moveToElementText(this);
    range.select();
  } else if (window.getSelection) {
    selection = window.getSelection();
    range = document.createRange();
    range.selectNodeContents(this);
    selection.removeAllRanges();
    selection.addRange(range);
  }
};

document.getElementById('foo').ondblclick = selectText;​

Diuji di IE 8+, Firefox 3+, Opera 9+, & Chrome 2+. Bahkan saya telah menyiapkannya menjadi plugin jQuery:

jQuery.fn.selectText = function() {
  var range, selection;
  return this.each(function() {
    if (document.body.createTextRange) {
      range = document.body.createTextRange();
      range.moveToElementText(this);
      range.select();
    } else if (window.getSelection) {
      selection = window.getSelection();
      range = document.createRange();
      range.selectNodeContents(this);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  });
};

$('#foo').on('dblclick', function() {
  $(this).selectText();
});

... dan siapa yang tertarik, ini sama untuk semua pecandu kopi:

jQuery.fn.selectText = ->
  @each ->
    if document.body.createTextRange
      range = document.body.createTextRange()
      range.moveToElementText @
      range.select()
    else if window.getSelection
      selection = window.getSelection()
      range = document.createRange()
      range.selectNodeContents @
      selection.removeAllRanges()
      selection.addRange range
    return

Memperbarui:

Jika Anda ingin memilih seluruh halaman atau konten dari wilayah yang dapat diedit (ditandai dengan contentEditable), Anda dapat melakukannya lebih sederhana dengan beralih ke designModedan menggunakan document.execCommand:

Ada titik awal yang baik di MDN dan dokumentasi yang sedikit .

var selectText = function () {
  document.execCommand('selectAll', false, null);
};

(bekerja dengan baik di IE6 +, Opera 9+, Firefoy 3+, Chrome 2+) http://caniuse.com/#search=execCommand

yckart
sumber
10

Karena semua jawaban yang ada berhubungan dengan divelemen, saya akan menjelaskan bagaimana melakukannya dengan spans.

Ada perbedaan halus saat memilih rentang teks dalam a span. Untuk dapat meneruskan indeks awal dan akhir teks, Anda harus menggunakan Textnode, seperti yang dijelaskan di sini :

Jika startNode adalah Node berjenis Teks, Komentar, atau CDATASection, maka startOffset adalah jumlah karakter dari awal startNode. Untuk jenis Node lainnya, startOffset adalah jumlah node turunan di antara awal startNode.

var e = document.getElementById("id of the span element you want to select text in");
var textNode = e.childNodes[0]; //text node is the first child node of a span

var r = document.createRange();
var startIndex = 0;
var endIndex = textNode.textContent.length;
r.setStart(textNode, startIndex);
r.setEnd(textNode, endIndex);

var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);
Domysee
sumber
Benar-benar harus: r.setStart(e.firstChild,0); r.setEnd(e.lastChild,e.lastChild.textContent.length); Tentu saja Anda harus memeriksa bahwa e.firstChild sebenarnya bukan null.
Yorick
2
Tidak ada perbedaan antara membuat pilihan di a <div>dan <span>elemen. Setidaknya, tidak seperti yang Anda gambarkan.
Tim Down
Ada perbedaan antara div dan span, dalam beberapa kasus solusi untuk div tidak berfungsi dengan benar dalam span. Misalnya jika Anda memilih teks secara terprogram dengan solusi div lalu menempelkan konten baru, itu tidak akan menggantikan teks seluruhnya, hanya sebagian dan ada perbedaan antara chrome dan firefox
neosonne
6

Rangy memungkinkan Anda melakukan lintas browser ini dengan kode yang sama. Rangy adalah implementasi lintas-browser dari metode DOM untuk pemilihan. Ini telah diuji dengan baik dan membuat ini jauh lebih tidak menyakitkan. Saya menolak untuk menyentuh konten yang dapat diedit tanpanya.

Anda dapat menemukan rangy di sini:

http://code.google.com/p/rangy/

Dengan rangy dalam proyek Anda, Anda selalu dapat menulis ini, meskipun browsernya IE 8 atau sebelumnya dan memiliki API asli yang sama sekali berbeda untuk pilihan:

var range = rangy.createRange();
range.selectNodeContents(contentEditableNode);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);

Di mana "contentEditableNode" adalah node DOM yang memiliki atribut contenteditable. Anda mungkin mengambilnya seperti ini:

var contentEditable = document.getElementById('my-editable-thing');

Atau jika jQuery sudah menjadi bagian dari proyek Anda dan Anda merasa nyaman:

var contentEditable = $('.some-selector')[0];
Tom Boutell
sumber
Proyek Rangy dipindahkan ke Github sekarang: github.com/timdown/rangy
tanius
4

Cara modern dalam melakukan sesuatu adalah seperti ini. Rincian lebih lanjut tentang MDN

document.addEventListener('dblclick', (event) => {
  window.getSelection().selectAllChildren(event.target)
})
<div contenteditable="true">Some text</div>


sumber
Terima kasih, ini berfungsi dengan baik! Fwiw, halaman MDN itu menandai teknologi ini sebagai eksperimental. Tetapi ini berfungsi di versi Chrome dan FF saat ini pada Juni 2020.
JohnK
2

[Diperbarui untuk memperbaiki kesalahan]

Berikut adalah contoh yang diadaptasi dari jawaban ini yang tampaknya berfungsi dengan baik di Chrome - Pilih rentang di div konten yang dapat diedit

var elm = document.getElementById("myText"),
    fc = elm.firstChild,
    ec = elm.lastChild,
    range = document.createRange(),
    sel;
elm.focus();
range.setStart(fc,1);
range.setEnd(ec,3);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

HTML adalah:

<div id="myText" contenteditable>test</div>
patorjk.dll
sumber