события изменения contenteditable

Я хочу, чтобы запустить функцию, когда пользователь редактирует содержимое div с

17 ответов


Я бы предложил прикрепить слушателей к ключевым событиям, вызванным редактируемым элементом, хотя вам нужно знать, что keydown и keypress события запускаются до изменения самого содержимого. Это не будет охватывать все возможные способы изменения содержимого: пользователь также может использовать вырезать, копировать и вставлять из меню редактирования или контекстного браузера, поэтому вы можете обработать cut copy и paste слишком событий. Кроме того, пользователь может удалить текст или другой контент, поэтому там больше событий (mouseup, например). Возможно, вы захотите опросить содержимое элемента в качестве резервного.

обновление 29 октября 2014

на в HTML5 input событие ответ в долгосрочной перспективе. На момент написания статьи он поддерживается для contenteditable элементы в текущих браузерах Mozilla (из Firefox 14) и WebKit/Blink, но не IE.

демо:

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

демо: http://jsfiddle.net/ch6yn/2691/


вот более эффективная версия, которая использует on для всех contenteditables. Это основано на лучших ответах здесь.

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

проект здесь:https://github.com/balupton/html5edit


рассмотрите возможность использования mutationobserver'а. Эти наблюдатели предназначены для реагирования на изменения в DOM и в качестве замены исполнителя на Событий Мутации.

плюсы:

  • пожары любой изменение происходит, что трудно достичь, слушая ключевые события, как это предлагается другими ответами. Например, все это хорошо работает: drag & drop, курсив, копирование/вырезание / вставка через контекст меню.
  • разработанный с учетом производительности.
  • простой, понятный код. Гораздо проще понять и отладить код, который слушает одно событие, а не код, который слушает 10 событий.
  • Google имеет отличный библиотека резюме мутаций что делает использование MutationObservers очень простым.

плюсы:

  • требуется последняя версия Firefox (14.0+), Chrome (18+) или IE (11+).
  • новый API для понимания
  • не много информации о лучших практик и кейсов

подробнее:

  • Я написал немного фрагмент для сравнения использования MutationObserers для обработки различных событий. Я использовал код balupton с его ответ имеет большинство upvotes.
  • Mozilla имеет отличную страницу на API
  • взгляните на the MutationSummary библиотека

non jQuery быстрая и грязный ответ:

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

Я изменил ответ lawwantsin так, и это работает для меня. Я использую событие keyup вместо нажатия клавиши, которое отлично работает.

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

вот что сработало для меня:

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

эта тема была очень полезна, пока я исследовал эту тему.

Я изменил часть кода, доступного здесь, в плагин jQuery, поэтому он находится в повторно используемой форме, в первую очередь для удовлетворения моих потребностей, но другие могут оценить более простой интерфейс для запуска с использованием contenteditable тегов.

https://gist.github.com/3410122

обновление:

из-за его растущей популярности плагин был принят Makesites.org

развитие будет продолжаться здесь:

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


вот решение, которое я использовал и работает сказочно. Я использую $(this).text () вместо этого, потому что я просто использую одну строку div, которая является редактируемой. Но вы также можете использовать .html () таким образом, вам не нужно беспокоиться о области глобальной/неглубокой переменной, а предыдущая фактически прикреплена к редактору div.

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

событие onchange не срабатывает при изменении элемента с атрибутом contentEditable, предлагаемым подходом может быть добавление кнопки в "сохранить" издание.

проверьте этот плагин, который обрабатывает проблему таким образом:


чтобы избежать таймеров и кнопок" сохранить", вы можете использовать событие размытия, которое срабатывает, когда элемент теряет фокус. но чтобы убедиться, что элемент действительно был изменен (а не просто сфокусирован и расфокусирован), его содержимое следует сравнить с его последней версией. или используйте событие keydown для установки некоторого" грязного " флага на этом элементе.


С помощью DOMCharacterDataModified под MutationEvents приведет к тому же. Тайм-аут настроен для предотвращения отправки неправильных значений (например, в Chrome у меня были некоторые проблемы с пробелом)

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


Я построил плагин jQuery для этого.

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

Вы можете просто позвонить $('.wysiwyg').wysiwygEvt();

вы также можете удалить / добавить события, если хотите


простой ответ в JQuery, я только что создал этот код и подумал, что это будет полезно и для других

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

два варианта:

1) Для современных (вечнозеленые) браузеры: Событие " input "будет действовать как альтернативное событие" change".

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

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

или

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

или (с jQuery)

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

2) для учета IE11 и современных (вечнозеленых) браузеров: Это наблюдение за изменениями элементов и их содержимым внутри элемент 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 });

проверьте эту идею. http://pastie.org/1096892

Я думаю, это близко. HTML 5 действительно нужно добавить событие изменения в спецификацию. Единственная проблема заключается в том, что функция обратного вызова вычисляет if (before == $(this).html ()) перед тем, как содержимое будет фактически Обновлено в $(this).формат HTML.)( setTimeout не работает, и это грустно. Дай мне знать, что ты думаешь.


Non jQuery ответ...

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

чтобы использовать его, вызовите (скажем) элемент заголовка с id="myHeader"

makeEditable(document.getElementById('myHeader'))

этот элемент теперь будет редактироваться пользователем, пока он не потеряет фокус.


на основе ответа @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>