perubahan konten yang dapat diedit

359

Saya ingin menjalankan fungsi ketika pengguna mengedit konten divdengan contenteditableatribut. Apa yang setara dengan suatu onchangeacara?

Saya menggunakan jQuery sehingga solusi apa pun yang menggunakan jQuery lebih disukai. Terima kasih!

Jourkey
sumber
Saya biasanya melakukan ini: document.getElementById("editor").oninput = function() { ...}.
Basj

Jawaban:

311

Saya sarankan melampirkan pendengar ke acara utama yang dipecat oleh elemen yang dapat diedit, meskipun Anda harus menyadari itu keydowndan keypressacara dipecat sebelum kontennya diubah. Ini tidak akan mencakup segala cara yang mungkin untuk mengubah konten: pengguna juga dapat menggunakan memotong, menyalin, dan menempel dari menu browser Edit atau konteks, sehingga Anda mungkin ingin menangani cut copydan pasteacara juga. Selain itu, pengguna dapat menjatuhkan teks atau konten lain, sehingga ada lebih banyak acara di sana ( mouseup, misalnya). Anda mungkin ingin mensurvei konten elemen sebagai fallback.

PEMBARUAN 29 Oktober 2014

The HTML5 inputacara adalah jawaban dalam jangka panjang. Pada saat penulisan, ini didukung untuk contenteditableelemen dalam Mozilla saat ini (dari Firefox 14) dan browser WebKit / Blink, tetapi tidak untuk IE.

Demo:

document.getElementById("editor").addEventListener("input", function() {
    console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>

Demo: http://jsfiddle.net/ch6yn/2691/

Tim Down
sumber
11
Saya juga harus menambahkan bahwa dimungkinkan untuk mengubah elemen yang dapat diedit dengan menggunakan menu edit browser atau menu konteks untuk memotong, menyalin atau menempelkan konten, jadi Anda harus menangani cut, copydan pasteacara di browser yang mendukungnya (IE 5+, Firefox 3.0+, Safari 3+, Chrome)
Tim Down
4
Anda dapat menggunakan keyup, dan Anda tidak akan memiliki masalah waktu.
Blowsie
2
@Blowsie: Ya, tetapi Anda berpotensi berakhir dengan banyak perubahan yang terjadi dari penekanan tombol yang diulang otomatis sebelum acara dimulai. Ada juga cara lain untuk mengubah teks yang dapat diedit yang tidak dipertimbangkan oleh jawaban ini, seperti seret dan lepas dan edit melalui menu Edit dan konteks. Jangka panjang, ini semua harus dicakup oleh inputacara HTML5 .
Tim Down
1
Saya melakukan keyup dengan debounce.
Aen Tan
1
@AndroidDev I testet Chrome, dan acara masukan juga diaktifkan pada salin dan potong sesuai kebutuhan oleh spesifikasi: w3c.github.io/uievents/#events-inputevents
fishbone
188

Ini adalah versi yang lebih efisien yang digunakan onuntuk semua konten yang dapat diedit. Ini didasarkan dari jawaban teratas di sini.

$('body').on('focus', '[contenteditable]', function() {
    const $this = $(this);
    $this.data('before', $this.html());
}).on('blur keyup paste input', '[contenteditable]', function() {
    const $this = $(this);
    if ($this.data('before') !== $this.html()) {
        $this.data('before', $this.html());
        $this.trigger('change');
    }
});

Proyeknya ada di sini: https://github.com/balupton/html5edit

balupton
sumber
Bekerja sempurna untuk saya, terima kasih untuk CoffeeScript dan Javascript
jwilcox09
7
Ada beberapa perubahan yang tidak akan ditangkap oleh kode ini sampai konten yang dapat diedit kabur. Misalnya: menyeret & menjatuhkan, memotong dari menu konteks, dan menempel dari jendela browser. Saya telah menyiapkan halaman contoh yang menggunakan kode ini yang dapat Anda berinteraksi dengan di sini .
jrullmann
@ jrullmann Ya saya punya masalah dengan kode ini ketika menyeret & menjatuhkan gambar karena tidak mendengarkan beban gambar (itu adalah masalah untuk menangani operasi mengubah ukuran dalam kasus saya)
Sebastien Lorber
@jrullmann sangat bagus, karya ini bahkan kemudian mengubah nilai dengan javascript, coba di konsol: document.getElementById('editor_input').innerHTML = 'ssss'. Kekurangannya adalah itu membutuhkan
1
@NadavB tidak seharusnya, karena itulah "jika ($ this.data ('before')! == $ this.html ()) {" adalah untuk
balupton
52

Pertimbangkan menggunakan MutationObserver . Pengamat ini dirancang untuk bereaksi terhadap perubahan DOM, dan sebagai pengganti pemain untuk Acara Mutasi .

Pro:

  • Kebakaran ketika setiap perubahan terjadi, yang sulit dicapai dengan mendengarkan peristiwa penting seperti yang disarankan oleh jawaban lainnya. Misalnya, semua ini berfungsi dengan baik: seret & lepas, cetak miring, salin / potong / tempel melalui menu konteks.
  • Dirancang dengan kinerja dalam pikiran.
  • Kode sederhana dan mudah. Jauh lebih mudah untuk memahami dan men-debug kode yang mendengarkan satu peristiwa daripada kode yang mendengarkan 10 peristiwa.
  • Google memiliki pustaka ringkasan mutasi yang sangat baik yang membuat penggunaan MutationObservers sangat mudah.

Cons:

  • Memerlukan versi terbaru dari Firefox (14.0+), Chrome (18+), atau IE (11+).
  • API baru untuk dipahami
  • Belum banyak informasi tersedia tentang praktik terbaik atau studi kasus

Belajarlah lagi:

  • Saya menulis cuplikan kecil untuk dibandingkan menggunakan MutationObserers untuk menangani berbagai acara. Saya menggunakan kode balupton karena jawabannya memiliki paling tinggi.
  • Mozilla memiliki halaman yang luar biasa di API
  • Lihatlah perpustakaan MutationSummary
Jrullmann
sumber
Ini tidak sama dengan inputacara HTML5 (yang didukung untuk contenteditablesemua browser WebKit dan Mozilla yang mendukung pengamat mutasi), karena mutasi DOM dapat terjadi melalui skrip serta input pengguna, tetapi ini merupakan solusi yang layak untuk browser tersebut. Saya membayangkan itu bisa merusak kinerja lebih dari inputperistiwa itu juga, tetapi saya tidak punya bukti kuat untuk ini.
Tim Down
2
+1, tetapi apakah Anda menyadari bahwa Acara Mutasi tidak melaporkan efek umpan baris dalam konten yang dapat diedit? Tekan enter di cuplikan Anda.
citykid
4
@citykid: Itu karena cuplikan hanya menonton untuk perubahan data karakter. Dimungkinkan untuk mengamati perubahan struktural DOM juga. Lihat jsfiddle.net/4n2Gz/1 , misalnya.
Tim Down
Internet Explorer 11+ mendukung Pengamat Mutasi .
Sampson
Saya dapat memverifikasi (setidaknya di Chrome 43.0.2357.130) bahwa inputacara HTML5 menyala di setiap situasi ini (khusus seret & jatuhkan, miringkan, salin / potong / tempel melalui menu konteks). Saya juga menguji huruf tebal w / cmd / ctrl + b dan mendapatkan hasil yang diharapkan. Saya juga memeriksa dan dapat memverifikasi bahwa inputacara tersebut tidak memicu perubahan terprogram (yang tampak jelas, tetapi bisa dibilang bertentangan dengan beberapa bahasa membingungkan pada halaman MDN yang relevan; lihat bagian bawah halaman di sini untuk melihat apa yang saya maksud: pengembang .mozilla.org / en-US / docs / Web / Acara / input )
mikermcneil
22

Saya telah mengubah jawaban lawwantsin seperti itu dan ini bekerja untuk saya. Saya menggunakan acara keyup bukan penekanan tombol yang berfungsi dengan baik.

$('#editor').on('focus', function() {
  before = $(this).html();
}).on('blur keyup paste', function() { 
  if (before != $(this).html()) { $(this).trigger('change'); }
});

$('#editor').on('change', function() {alert('changed')});
Dennkster
sumber
22

non jQuery jawaban cepat dan kotor :

function setChangeListener (div, listener) {

    div.addEventListener("blur", listener);
    div.addEventListener("keyup", listener);
    div.addEventListener("paste", listener);
    div.addEventListener("copy", listener);
    div.addEventListener("cut", listener);
    div.addEventListener("delete", listener);
    div.addEventListener("mouseup", listener);

}

var div = document.querySelector("someDiv");

setChangeListener(div, function(event){
    console.log(event);
});
Developia
sumber
3
apa acara penghapusan ini?
James Cat
7

Dua pilihan:

1) Untuk browser modern (evergreen): Acara "input" akan bertindak sebagai acara "perubahan" alternatif.

https://developer.mozilla.org/en-US/docs/Web/Events/input

document.querySelector('div').addEventListener('input', (e) => {
    // Do something with the "change"-like event
});

atau

<div oninput="someFunc(event)"></div>

atau (dengan jQuery)

$('div').on('click', function(e) {
    // Do something with the "change"-like event
});

2) Untuk menjelaskan browser IE11 dan modern (evergreen): Ini mengawasi perubahan elemen dan isinya di dalam div.

https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

var div = document.querySelector('div');
var divMO = new window.MutationObserver(function(e) {
    // Do something on change
});
divMO.observe(div, { childList: true, subtree: true, characterData: true });
sedikit kurang
sumber
7

const p = document.querySelector('p')
const result = document.querySelector('div')
const observer = new MutationObserver((mutationRecords) => {
  result.textContent = mutationRecords[0].target.data
  // result.textContent = p.textContent
})
observer.observe(p, {
  characterData: true,
  subtree: true,
})
<p contenteditable>abc</p>
<div />

superwf
sumber
2
Ini terlihat menarik. Bisakah seseorang memberikan penjelasan? 😇
WoIIe
3

Inilah yang bekerja untuk saya:

   var clicked = {} 
   $("[contenteditable='true']").each(function(){       
        var id = $(this).attr("id");
        $(this).bind('focus', function() {
            // store the original value of element first time it gets focus
            if(!(id in clicked)){
                clicked[id] = $(this).html()
            }
        });
   });

   // then once the user clicks on save
   $("#save").click(function(){
            for(var id in clicked){
                var original = clicked[id];
                var current = $("#"+id).html();
                // check if value changed
                if(original != current) save(id,current);
            }
   });
sisi putih gregory
sumber
3

Utas ini sangat membantu saat saya menyelidiki masalah ini.

Saya telah memodifikasi beberapa kode yang tersedia di sini menjadi plugin jQuery sehingga itu dalam bentuk yang dapat digunakan kembali, terutama untuk memenuhi kebutuhan saya tetapi yang lain mungkin menghargai antarmuka yang lebih sederhana untuk memulai dengan menggunakan tag yang dapat diedit.

https://gist.github.com/3410122

Memperbarui:

Karena popularitasnya yang semakin meningkat, plugin ini telah diadopsi oleh Makesites.org

Pengembangan akan berlanjut dari sini:

https://github.com/makesites/jquery-contenteditable

tracend
sumber
2

Inilah solusi yang akhirnya saya gunakan dan bekerja dengan luar biasa. Saya menggunakan $ (this) .text () sebagai gantinya karena saya hanya menggunakan satu baris div yang dapat diedit konten. Tetapi Anda juga dapat menggunakan .html () dengan cara ini Anda tidak perlu khawatir tentang ruang lingkup variabel global / non-global dan sebelumnya benar-benar dilampirkan ke editor editor.

$('body').delegate('#editor', 'focus', function(){
    $(this).data('before', $(this).html());
});
$('#client_tasks').delegate('.task_text', 'blur', function(){
    if($(this).data('before') != $(this).html()){
        /* do your stuff here - like ajax save */
        alert('I promise, I have changed!');
    }
});
pengguna740433
sumber
1

Untuk menghindari penghitung waktu dan tombol "simpan", Anda dapat menggunakan acara blur yang menyala ketika elemen kehilangan fokus. tetapi untuk memastikan bahwa elemen tersebut benar-benar diubah (tidak hanya fokus dan tidak fokus), isinya harus dibandingkan dengan versi terakhirnya. atau gunakan acara keydown untuk mengatur beberapa bendera "kotor" pada elemen ini.

joox
sumber
1

Sebuah jawaban sederhana di JQuery, saya baru saja membuat kode ini dan berpikir itu akan bermanfaat bagi orang lain juga

    var cont;

    $("div [contenteditable=true]").focus(function() {
        cont=$(this).html();
    });

    $("div [contenteditable=true]").blur(function() {
        if ($(this).html()!=cont) {
           //Here you can write the code to run when the content change
        }           
    });
sarath
sumber
Catatan yang $("div [contenteditable=true]")akan memilih semua anak dari div, secara langsung atau tidak langsung, yang dapat diedit.
Bruno Ferreira
1

Bukan jawaban JQuery ...

function makeEditable(elem){
    elem.setAttribute('contenteditable', 'true');
    elem.addEventListener('blur', function (evt) {
        elem.removeAttribute('contenteditable');
        elem.removeEventListener('blur', evt.target);
    });
    elem.focus();
}

Untuk menggunakannya, panggil (katakanlah) elemen tajuk dengan id = "myHeader"

makeEditable(document.getElementById('myHeader'))

Elemen itu sekarang akan dapat diedit oleh pengguna sampai kehilangan fokus.

DrMcCleod
sumber
5
Ugh, mengapa downvote tanpa penjelasan? Ini merugikan bagi orang yang menulis jawaban, dan semua orang yang membaca jawabannya nanti.
Mike Willis
0

Acara onchange tidak menyala ketika elemen dengan atribut contentEditable diubah, pendekatan yang disarankan bisa dengan menambahkan tombol, untuk "menyimpan" edisi.

Periksa plugin ini yang menangani masalah dengan cara itu:

CMS
sumber
untuk anak cucu, sepuluh tahun ke depan dan tidak perlu tombol "simpan", periksa jsfiddle jawaban yang diterima
kembalilah
0

Menggunakan DOMCharacterDataModified di bawah MutationEvents akan mengarah ke hal yang sama. Batas waktu disetel untuk mencegah pengiriman nilai yang salah (mis. Di Chrome saya mengalami beberapa masalah dengan tombol spasi)

var timeoutID;
$('[contenteditable]').bind('DOMCharacterDataModified', function() {
    clearTimeout(timeoutID);
    $that = $(this);
    timeoutID = setTimeout(function() {
        $that.trigger('change')
    }, 50)
});
$('[contentEditable]').bind('change', function() {
    console.log($(this).text());
})

Contoh JSFIDDLE

janfabian
sumber
1
+1 - DOMCharacterDataModifiedtidak akan diaktifkan ketika pengguna memodifikasi teks yang ada, misalnya menerapkan cetak tebal atau miring. DOMSubtreeModifiedlebih tepat dalam hal ini. Selain itu, orang harus ingat bahwa browser lama tidak mendukung acara ini.
Andy E
3
Hanya catatan bahwa Acara Mutasi disusutkan oleh w3c karena masalah kinerja. Lebih banyak dapat ditemukan dalam pertanyaan Stack Overflow ini .
jrullmann
0

Saya membangun plugin jQuery untuk melakukan ini.

(function ($) {
    $.fn.wysiwygEvt = function () {
        return this.each(function () {
            var $this = $(this);
            var htmlold = $this.html();
            $this.bind('blur keyup paste copy cut mouseup', function () {
                var htmlnew = $this.html();
                if (htmlold !== htmlnew) {
                    $this.trigger('change')
                }
            })
        })
    }
})(jQuery);

Anda cukup menelepon $('.wysiwyg').wysiwygEvt();

Anda juga dapat menghapus / menambah acara jika diinginkan

Blowsie
sumber
2
Itu akan menjadi lambat dan lambat karena konten yang dapat diedit menjadi lebih lama ( innerHTMLmahal). Saya akan merekomendasikan menggunakan inputacara di mana itu ada dan jatuh kembali ke sesuatu seperti ini tetapi dengan semacam debouncing.
Tim Down
0

Dalam Angular 2+

<div contentEditable (input)="type($event)">
   Value
</div>

@Component({
  ...
})
export class ContentEditableComponent {

 ...

 type(event) {
   console.log(event.data) // <-- The pressed key
   console.log(event.path[0].innerHTML) // <-- The content of the div 
 }
}

Dávid Konkoly
sumber
0

Silakan ikuti solusi sederhana berikut ini

Dalam .ts:

getContent(innerText){
  this.content = innerText;
}

dalam .html:

<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>
Sahil Ralkar
sumber
-1

Lihat ide ini. http://pastie.org/1096892

Saya pikir sudah dekat. HTML 5 benar-benar perlu menambahkan acara perubahan ke spec. Satu-satunya masalah adalah bahwa fungsi callback mengevaluasi if (before == $ (this) .html ()) sebelum konten benar-benar diperbarui dalam $ (this) .html (). setTimeout tidak berfungsi, dan itu menyedihkan. Biarkan aku tahu apa yang Anda pikirkan.

Lawrence Whiteside
sumber
Coba keyup alih-alih penekanan tombol.
Aen Tan
-2

Berdasarkan jawaban @ balupton:

$(document).on('focus', '[contenteditable]', e => {
	const self = $(e.target)
	self.data('before', self.html())
})
$(document).on('blur', '[contenteditable]', e => {
	const self = $(e.target)
	if (self.data('before') !== self.html()) {
	  self.trigger('change')
	}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Phillip Senn
sumber