Контекстные события изменения

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

Я использую jQuery, поэтому любые решения, использующие jQuery, являются предпочтительными. Спасибо!

Ответ 1

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

ОБНОВЛЕНИЕ 29 октября 2014 года

Событие input HTML5 является ответом в долгосрочной перспективе. На момент написания этой статьи он поддерживается для 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>

Ответ 2

Вот более эффективный вариант, который использует 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

Ответ 3

Рассмотрим использование MutationObserver. Эти наблюдатели предназначены для реагирования на изменения в DOM и в качестве замены исполнителей на Mutation Events.

Плюсы:

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

Минусы:

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

Выучить больше:

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

https://gist.github.com/3410122

Обновление:

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

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

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

Ответ 8

Вот решение, которое я использовал, и работает сказочно. Я использую $(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!');
    }
});

Ответ 9

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

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

Ответ 10

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

Ответ 11

Использование 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

Ответ 12

Я построил плагин 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();

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

Ответ 13

Простой ответ в 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
        }           
    });

Ответ 14

Два варианта:

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

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

Ответ 15

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

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

Ответ 16

Не ответ 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'))

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

Ответ 17

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