оспариваемые события изменения

276

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

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

Теги:
contenteditable

17 ответов

244
Лучший ответ

Я бы предложил подключить слушателей к ключевым событиям, запускаемым редактируемым элементом, хотя вам нужно знать, что события 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>

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

  • 11
    Я также должен добавить, что можно изменять редактируемый элемент, используя меню редактирования или контекстное меню браузера, чтобы вырезать, копировать или вставлять содержимое, поэтому вам нужно обрабатывать события cut , copy и paste в браузерах, которые их поддерживают (IE 5 +, Firefox 3.0+, Safari 3+, Chrome)
  • 4
    Вы можете использовать keyup, и у вас не будет проблем с синхронизацией.
Показать ещё 11 комментариев
179

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

  • 0
    Работал отлично для меня, спасибо за CoffeeScript и Javascript
  • 7
    Существуют некоторые изменения, которые не будут отслеживаться этим кодом, пока содержимое не будет размыто. Например: перетаскивание, вырезание из контекстного меню и вставка из окна браузера. Я установил пример страницы, которая использует этот код, с которым вы можете взаимодействовать здесь .
Показать ещё 6 комментариев
47

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

Плюсы:

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

Минусы:

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

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

  • Я написал небольшой фрагмент, чтобы сравнить использование MutationObserers для обработки различных событий. Я использовал балуптон-код, так как его ответ имеет самые высокие показатели.
  • Mozilla имеет отличную страницу в API
  • Взгляните на библиотеку MutationSummary
  • 0
    Это не совсем то же самое, что событие input HTML5 (которое поддерживается для contenteditable во всех браузерах WebKit и Mozilla, которые поддерживают наблюдатели мутаций), поскольку мутация DOM может происходить как через сценарий, так и через пользовательский ввод, но это жизнеспособное решение для этих браузеров , Я полагаю, что это может повредить производительности больше, чем событие input , но у меня нет веских доказательств этого.
  • 2
    +1, но вы поняли, что Mutation Events не сообщают о эффектах перевода строки в contenteditable? Нажмите ввод в вашем фрагменте.
Показать ещё 3 комментария
19

не 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);
});
  • 1
    что это за событие удаления?
19

Я изменил закон, чтобы ответить так, и это работает для меня. Я использую событие 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')});
3

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

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

https://gist.github.com/3410122

Обновление:

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

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

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

3

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

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

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

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

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

Простой ответ в 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
        }           
    });
  • 0
    Обратите внимание, что $("div [contenteditable=true]") выберет все дочерние элементы div, прямо или косвенно, которые являются contenteditable.
0

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

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

  • 2
    Это будет медленным и медленным, поскольку редактируемый контент становится длиннее ( innerHTML стоит дорого). Я бы порекомендовал использовать событие input там, где оно существует, и вернуться к чему-то подобному, но с каким-то опровержением.
0

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

  • 1
    +1 - DOMCharacterDataModified не будет срабатывать, когда пользователь изменяет существующий текст, например, применяя жирный шрифт или курсив. DOMSubtreeModified более подходит в этом случае. Кроме того, люди должны помнить, что устаревшие браузеры не поддерживают эти события.
  • 3
    Просто обратите внимание, что Mutation Events не рекомендуется w3c из-за проблем с производительностью. Больше можно найти в этом вопросе переполнения стека .
0

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

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

  • 0
    Попробуйте keyup вместо нажатия клавиши.
0

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

0

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

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

-2

На основании ответа @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="/jquery.min.js"></script>
-3

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

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

  • 4
    Тьфу, почему downvote без объяснения причин? Это оказывает плохую услугу как человеку, который написал ответ, так и всем, кто читает ответ позже.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню