konten dapat diedit, atur tanda sisipan di akhir teks (lintas-browser)

111

keluaran di Chrome :

<div id="content" contenteditable="true" style="border:1px solid #000;width:500px;height:40px;">
    hey
    <div>what's up?</div>
<div>
<button id="insert_caret"></button>

Saya percaya FF akan terlihat seperti ini:

hey
<br />
what's up?

dan di IE :

hey
<p>what's up?</p>

sayangnya, tidak ada cara yang baik untuk membuatnya sehingga setiap browser memasukkan <br />bukan div- atau p-tag, atau setidaknya saya tidak dapat menemukan apa pun secara online.


TETAPI, yang saya coba lakukan sekarang adalah, ketika saya menekan tombol , saya ingin tanda sisipan disetel di akhir teks, jadi akan terlihat seperti ini:

hey
what's up?|

cara apa pun untuk melakukan ini agar berfungsi di semua browser ?

contoh:

$(document).ready(function()
{
    $('#insert_caret').click(function()
    {
        var ele = $('#content');
        var length = ele.html().length;

        ele.focus();

        //set caret -> end pos
     }
 }
Josh Crozier
sumber
1
Ini bekerja paling baik untuk saya
Илья Зеленько

Jawaban:

282

Fungsi berikut akan melakukannya di semua browser utama:

function placeCaretAtEnd(el) {
    el.focus();
    if (typeof window.getSelection != "undefined"
            && typeof document.createRange != "undefined") {
        var range = document.createRange();
        range.selectNodeContents(el);
        range.collapse(false);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (typeof document.body.createTextRange != "undefined") {
        var textRange = document.body.createTextRange();
        textRange.moveToElementText(el);
        textRange.collapse(false);
        textRange.select();
    }
}

placeCaretAtEnd( document.querySelector('p') );
p{ padding:.5em; border:1px solid black; }
<p contentEditable>foo bar </p>

Menempatkan tanda sisipan di awal hampir sama: hanya perlu mengubah Boolean yang diteruskan menjadi panggilan ke collapse(). Berikut adalah contoh yang membuat fungsi untuk menempatkan tanda sisipan di awal dan di akhir:

function createCaretPlacer(atStart) {
    return function(el) {
        el.focus();
        if (typeof window.getSelection != "undefined"
                && typeof document.createRange != "undefined") {
            var range = document.createRange();
            range.selectNodeContents(el);
            range.collapse(atStart);
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (typeof document.body.createTextRange != "undefined") {
            var textRange = document.body.createTextRange();
            textRange.moveToElementText(el);
            textRange.collapse(atStart);
            textRange.select();
        }
    };
}

var placeCaretAtStart = createCaretPlacer(true);
var placeCaretAtEnd = createCaretPlacer(false);
Tim Down
sumber
Tidak berfungsi dengan Chrome karena createTextRange bukan fungsi standar. Lihat stackoverflow.com/a/41743191/700206 .
whitneyland
@Lee: Ini berfungsi dengan baik di Chrome, yang mendukung window.getSelectiondan document.createRange. The createTextRangecabang untuk versi lama dari Internet Explorer.
Tim Down
pada saat penulisan window.getSelectiontidak didukung oleh 0,29% dari semua browser (IE> 8). lihat: caniuse.com/#search=window.getSelection
w.stoettinger
@TimDown ini berhasil. +1. Tapi bisakah Anda juga menjelaskan kode dengan menambahkan komentar? Akan lebih bagus
shinobi
Ada yang tahu cara menyetel tanda sisipan sampai akhir (gunakan kode ini) saat mencoba menargetkan elemen body iframe>? Elemen tubuh memiliki editablecontent="true"...? placeCaretAtEnd(this);tidak akan berhasil untuk saya.
RobbTe
6

Sayangnya, jawaban Tim yang luar biasa bekerja untuk saya hanya untuk menempatkan di akhir, untuk menempatkan di awal saya harus sedikit memodifikasinya.

function setCaret(target, isStart) {
  const range = document.createRange();
  const sel = window.getSelection();
  if (isStart){
    const newText = document.createTextNode('');
    target.appendChild(newText);
    range.setStart(target.childNodes[0], 0);
  }
  else {
    range.selectNodeContents(target);
  }
  range.collapse(isStart);
  sel.removeAllRanges();
  sel.addRange(range);
  target.focus();
  target.select();
}

Namun tidak yakin apakah focus()dan select()benar-benar dibutuhkan.

remang-remang
sumber
Anda juga memperkenalkan perpustakaan eksternal. Jika menurut Anda fokus dan pemilihan tidak diperlukan, mengapa tidak mengomentari baris dan mengujinya?
dotnethaggis
Terima kasih, jquery dihapus. Saya ingat saya mendapat beberapa hasil yang bertentangan, saya akan mencoba mengisolasi mereka lagi.
redup
1

Contoh (langsung) ini menunjukkan fungsi sederhana yang singkat setCaretAtStartEnd, yang membutuhkan dua argumen; Node (dapat diedit) untuk menempatkan tanda sisipan di & Boolean yang menunjukkan di mana harus meletakkannya (awal atau akhir node)

const editableElm = document.querySelector('[contenteditable]');

document.querySelectorAll('button').forEach((elm, idx) => 
  elm.addEventListener('click', () => {
    editableElm.focus()
    setCaretAtStartEnd(editableElm, idx) 
  })
)

function setCaretAtStartEnd( node, atEnd ){
  const sel = document.getSelection();
  node = node.firstChild;

  if( sel.rangeCount ){
      ['Start', 'End'].forEach(pos =>
        sel.getRangeAt(0)["set" + pos](node, atEnd ? node.length : 0)
      )
  }
}
[contenteditable]{ padding:5px; border:1px solid; }
<h1 contenteditable>Place the caret anywhere</h1>
<br>
<button>Move caret to start</button>
<button>Move caret to end</button>

vsync
sumber
-1

Jika Anda menggunakan kompiler penutupan google, Anda dapat melakukan hal berikut (agak disederhanakan dari jawaban Tim):

function placeCaretAtEnd(el) {
    el.focus();
    range = goog.dom.Range.createFromNodeContents(el);
    range.collapse(false);
    range.select();
}

Inilah hal yang sama di ClojureScript:

(defn place-caret-at-end [el] 
   (.focus el)
   (doto (.createFromNodeContents goog.dom.Range el)
         (.collapse false)
         .select))

Saya telah menguji ini di Chrome, Safari dan FireFox, tidak yakin tentang IE ...

Matt WD
sumber
1
range = goog.dom.Range.createFromNodeContents (el); apa goog.dom?
Anh Tú
Ini berhasil untuk saya. @ AnhTú: goog.dom adalah namespace yang disediakan oleh closure-library.
David Beneš