tartışmalı değişiklik etkinlikleri


359

Bir kullanıcı bir içeriğini düzenler zaman bir işlevi çalıştırmak istediğiniz divile contenteditableöznitelik. Bir onchangeetkinliğin karşılığı nedir ?

JQuery kullanan herhangi bir çözüm tercih edilir böylece jQuery kullanıyorum. Teşekkürler!


Bunu genelde: document.getElementById("editor").oninput = function() { ...}.
19:45, Basj

Yanıtlar:


313

Anahtar olaylar düzenlenebilir öğe tarafından ateş etmek o farkında olması gerekir gerçi ben takılarak dinleyicileri öneririm keydownve keypressiçeriğin kendisi değiştirilir önce olay harekete. Bu, içeriği değiştirmek için her türlü aracı kapsamaz: kullanıcı, Düzenle veya bağlam tarayıcı menülerinden kes, kopyala ve yapıştır da kullanabilir, böylece cut copyve pasteolaylarını da işlemek isteyebilirsiniz . Ayrıca, kullanıcı metin veya başka içerik bırakabilir, bu nedenle orada daha fazla etkinlik vardır ( mouseupörneğin). Öğenin içeriğini yedek olarak yoklamak isteyebilirsiniz.

GÜNCELLEME 29 Ekim 2014

HTML5 inputolay uzun vadede cevaptır. Yazma sırasında, contenteditablegeçerli Mozilla'daki (Firefox 14'ten) ve WebKit / Blink tarayıcılarındaki öğeler için desteklenir , ancak IE için desteklenmez.

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/


11
Ayrıca, bu tarayıcının düzenleme menüsünü veya kesim için bağlam menüsünü kullanarak düzenlenebilir elemanın değiştirilmesi mümkün olduğunu eklemek kopyalamak veya sap gerekir, böylece içeriği yapıştırmak gerekir cut, copyve pastetarayıcılarda olaylar onları destekleyen (IE 5+, Firefox 3.0+, Safari 3+, Chrome)
Tim Down

4
Keyup kullanabilirsiniz ve bir zamanlama sorunu olmayacak.
Blowsie

2
@Blowsie: Evet, ancak etkinlik tetiklenmeden önce otomatik olarak tekrarlanan bir tuşa basıldığında birçok değişiklik olabilir. Bu cevap tarafından dikkate alınmayan, sürükle bırak ve Düzenleme ve bağlam menüleri aracılığıyla düzenlemeler gibi düzenlenebilir metinleri değiştirmenin başka yolları da vardır. Uzun vadede, bunların tümü HTML5 inputetkinliği tarafından kapsanmalıdır .
Tim Down

1
Ben debounce ile keyup yapıyorum.
Aen Tan

1
@AndroidDev ben Chrome'u testet ve özellikleri gereği giriş olay da kopyasını yapıştırın ve kesim ateşlenir: w3c.github.io/uievents/#events-inputevents
balık kılçığı

188

İşte ontüm içerikler için kullanılan daha verimli bir sürüm . Buradaki en iyi cevaplara dayanıyor.

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

Proje burada: https://github.com/balupton/html5edit


Benim için mükemmel çalıştı, hem CoffeeScript hem de Javascript için teşekkürler
jwilcox09

7
İçerik kodu bulanıklaşana kadar bu kod tarafından yakalanmayacak bazı değişiklikler var. Örneğin: sürükleme ve bırakma, içerik menüsünden kesme ve tarayıcı penceresinden yapıştırma. Burada etkileşime girebileceğiniz bu kodu kullanan bir örnek sayfa ayarladım .
jrullmann

@jrullmann Evet Görüntü yükünü dinlemediğinden görüntüleri sürükleyip bırakarak bu kodla ilgili sorunlar yaşadım (benim durumumda yeniden boyutlandırma işlemini işlemek bir sorundu)
Sebastien Lorber

@jrullmann Çok güzel, o zaman bile javascript ile değerini değiştirerek bu eserler, konsolda deneyin: document.getElementById('editor_input').innerHTML = 'ssss'. Dezavantajı IE11 gerektirir

1
@NadavB olmamalı, çünkü "if ($ this.data ('before')! == $ this.html ()) {" bunun için
balupton

52

MutationObserver kullanmayı düşünün . Bu gözlemciler, DOM'daki değişikliklere tepki vermek ve Mutasyon Olaylarının yerine geçmek için tasarlanmıştır .

Artıları:

  • Herhangi bir değişiklik meydana geldiğinde tetiklenir , bu da diğer cevapların önerdiği gibi önemli olayları dinleyerek başarılması zordur. Örneğin, bunların hepsi iyi çalışır: sürükle ve bırak, italikleştirme, içerik menüsü aracılığıyla kopyalama / kesme / yapıştırma.
  • Performans göz önünde bulundurularak tasarlanmıştır.
  • Basit, anlaşılır kod. 10 olayı dinleyen kod yerine bir olayı dinleyen kodu anlamak ve hata ayıklamak çok daha kolaydır.
  • Google, MutationObservers kullanımını çok kolaylaştıran mükemmel bir mutasyon özeti kütüphanesine sahiptir.

Eksileri:

  • Firefox (14.0+), Chrome (18+) veya IE (11+) 'nin çok yeni bir sürümünü gerektirir.
  • Anlamak için yeni API
  • En iyi uygulamalar veya vaka çalışmaları hakkında henüz çok fazla bilgi mevcut değil

Daha fazla bilgi edin:

  • MutationObserers kullanarak çeşitli olayları işlemek için karşılaştırmak için küçük bir snippet yazdım . Onun beri balupton kodunu kullanılan cevabı en upvotes vardır.
  • Mozilla , API'da mükemmel bir sayfaya sahip
  • MutationSummary kütüphanesine bir göz atın

DOM mutasyonu komut dosyası ve kullanıcı girdisi aracılığıyla gerçekleşebileceğinden , HTML5 inputolayıyla ( contenteditablemutasyon gözlemcilerini destekleyen tüm WebKit ve Mozilla tarayıcılarında desteklenir) aynı değildir, ancak bu tarayıcılar için geçerli bir çözümdür. Performansa inputolaydan daha fazla zarar verebileceğini düşünüyorum, ancak bunun için sağlam bir kanıtım yok.
Tim Down

2
+1, ancak Mutasyon Olaylarının satır beslemenin etkilerini tartışılabilir bir içerikte bildirmediğini fark ettiniz mi? Snippet'inizde enter tuşuna basın.
citykid

4
@citykid: Parçacık yalnızca karakter verilerindeki değişiklikleri izliyor. DOM yapısal değişikliklerini de gözlemlemek mümkündür. Örneğin, bkz. Jsfiddle.net/4n2Gz/1 .
Tim Down

Internet Explorer 11+, Mutasyon Gözlemcilerini destekler .
Sampson

inputBu durumların her birinde HTML5 olayının tetiklendiğini (en azından Chrome 43.0.2357.130'da) (özellikle sürükle ve bırak, italikleştirme, kopyala / kes / yapıştır bağlam menüsünden) doğrulayabildim . Ayrıca kalınlık w / cmd / ctrl + b'yi test ettim ve beklenen sonuçları aldım. Ben de kontrol edilmeli ve doğrulamak başardı inputolay değil bariz görünüyor (programatik değişikliklere üzerine ateş, ancak tartışmasız ilgili MDN sayfada bazı kafa karıştırıcı dilin aykırı olduğu; ne demek istediğimi görmek için buraya sayfanın sonuna bakınız: geliştirici .mozilla.org / tr-
TR

22

Lawwantsin'ın cevabını bu şekilde değiştirdim ve bu benim için işe yarıyor. Harika çalışan keypress yerine keyup olayını kullanıyorum.

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

22

olmayan jQuery hızlı ve kirli cevap:

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

3
bu silme olayı nedir?
James Cat

7

İki seçenek:

1) Modern (herdem yeşil) tarayıcılar için: "input" olayı alternatif bir "change" olayı olarak işlev görür.

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

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

veya

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

veya (jQuery ile)

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

2) IE11 ve modern (herdem yeşil) tarayıcıları hesaba katmak için: Bu, div içindeki element değişikliklerini ve içeriklerini izler.

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

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 />


2
Bu ilginç görünüyor. Birisi açıklama yapabilir mi? 😇
WoIIe

3

İşte benim için işe yarayan:

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

3

Konuyu araştırırken bu konu çok yardımcı oldu.

Burada mevcut kodun bir kısmını jQuery eklentisine dönüştürdüm, bu yüzden öncelikle ihtiyaçlarımı karşılamak için yeniden kullanılabilir bir formda, ancak diğerleri içerikli etiketleri kullanarak atlamak için daha basit bir arayüzü takdir edebilir.

https://gist.github.com/3410122

Güncelleme:

Artan popülaritesi nedeniyle eklenti Makesites.org tarafından benimsenmiştir.

Gelişim buradan devam edecek:

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


2

İşte sonunda kullandım ve harika çalışıyor çözüm. Bunun yerine $ (this) .text () kullanıyorum çünkü içeriği düzenlenebilir tek satırlık bir div kullanıyorum. Ancak .html () yöntemini de kullanabilirsiniz.

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

1

Zamanlayıcıları ve "kaydet" düğmelerini önlemek için, öğe odağı kaybettiğinde patlayan bulanıklık olayını kullanabilirsiniz. ancak öğenin gerçekten değiştirildiğinden emin olmak için (sadece odaklanmış ve odaklanmamış değil), içeriği son sürümüyle karşılaştırılmalıdır. veya bu öğede bazı "kirli" bayrak ayarlamak için keydown olayını kullanın.


1

JQuery basit bir cevap, ben sadece bu kodu oluşturdu ve diğerleri için de yararlı olacağını düşündüm

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

$("div [contenteditable=true]")Bir div'in doğrudan veya dolaylı olarak tartışılabilir tüm çocuklarını seçeceğini unutmayın .
Bruno Ferreira

1

JQuery olmayan yanıt ...

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

Kullanmak için id = "myHeader" olan bir başlık öğesini çağırın (diyelim)

makeEditable(document.getElementById('myHeader'))

Bu öğe, artık odağını kaybedene kadar kullanıcı tarafından düzenlenebilir.


5
Ugh, neden açıklamasız downvote? Bu hem cevabı yazan kişiye hem de cevabı daha sonra okuyan herkese bir kötülük yapar.
Mike Willis

0

Onchange olayı, contentEditable özniteliğine sahip bir öğe değiştirildiğinde tetiklenmez, önerilen bir yaklaşım , baskıyı "kaydetmek" için bir düğme eklemek olabilir .

Sorunu bu şekilde ele alan bu eklentiyi kontrol edin:


gelecek nesiller için, on yıl ileri ve "kaydet" düğmesine gerek yoktur, kabul edilen yanıtın jsfiddle kodunu kontrol edin
9'da revelt

0

Kullanılması MutationEvents altında DOMCharacterDataModified aynı yol açacaktır. Zaman aşımı yanlış değerler göndermeyi önlemek için ayarlandı (örneğin Chrome'da boşluk tuşu ile ilgili bazı sorunlar yaşadım)

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

JSFIDDLE örneği


1
+1 - DOMCharacterDataModified kullanıcı varolan metni değiştirdiğinde (örneğin kalın veya italik uygulamak) tetiklenmez. DOMSubtreeModifiedbu durumda daha uygundur. Ayrıca, insanlar eski tarayıcıların bu etkinlikleri desteklemediğini hatırlamalıdır.
Andy E

3
Mutasyon Olaylarının performans sorunları nedeniyle w3c tarafından amortismana uğradığına dikkat edin. Daha bulunabilir bu yığın taşması soruya .
jrullmann

0

Bunu yapmak için bir jQuery eklentisi oluşturdum.

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

Sadece arayabilirsiniz $('.wysiwyg').wysiwygEvt();

İsterseniz etkinlikleri kaldırabilir / ekleyebilirsiniz


2
Düzenlenebilir içerik uzadıkça ( innerHTMLpahalı) bu yavaş ve gecikmeli olur . inputVar olan olayı kullanmayı ve böyle bir şeye düşmeyi ancak bir tür zıplatma ile tavsiye ederim.
Tim Down

0

Açısal 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 
 }
}


0

Lütfen aşağıdaki basit çözümü izleyin

.Ts içinde:

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

.html biçiminde:

<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>

-1

Bu fikri kontrol edin. http://pastie.org/1096892

Sanırım yakın. HTML 5'in gerçekten change olayını spesifikasyona eklemesi gerekiyor. Tek sorun, içeriğin $ (this) .html () içinde gerçekten güncellenmeden önce (== $ (this) .html ()) önce geri arama işlevinin değerlendirmesidir. setTimeout çalışmıyor ve üzücü. Ne düşündüğü söyle.


Tuşa basma yerine tuşlamayı deneyin.
Aen Tan

-2

@ Balupton'un cevabına dayanarak:

$(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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.