Я хочу запустить функцию, когда пользователь редактирует содержимое атрибута div
с атрибутом contenteditable
. Что эквивалентно событию onchange
?
Я использую jQuery, поэтому любые решения, использующие jQuery, являются предпочтительными. Спасибо!
Я бы предложил подключить слушателей к ключевым событиям, запускаемым редактируемым элементом, хотя вам нужно знать, что события 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>
Вот более эффективный вариант, который использует 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 и в качестве замены исполнителей на Mutation Events.
Плюсы:
Минусы:
Выучить больше:
input
HTML5 (которое поддерживается для contenteditable
во всех браузерах WebKit и Mozilla, которые поддерживают наблюдатели мутаций), поскольку мутация DOM может происходить как через сценарий, так и через пользовательский ввод, но это жизнеспособное решение для этих браузеров , Я полагаю, что это может повредить производительности больше, чем событие input
, но у меня нет веских доказательств этого.
не 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);
});
Я изменил закон, чтобы ответить так, и это работает для меня. Я использую событие 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')});
Эта тема была очень полезна, когда я изучал этот вопрос.
Я изменил часть кода, доступного здесь, в плагин jQuery, поэтому он находится в повторно используемой форме, в первую очередь для удовлетворения моих потребностей, но другие могут оценить более простой интерфейс для перехода с использованием контентных тегов.
https://gist.github.com/3410122
Из-за его растущей популярности плагин был принят Makesites.org
Развитие будет продолжаться отсюда:
Вот что сработало для меня:
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);
}
});
Вот решение, которое я использовал, и работает сказочно. Я использую $(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!');
}
});
Два варианта:
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 });
Простой ответ в 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
}
});
$("div [contenteditable=true]")
выберет все дочерние элементы div, прямо или косвенно, которые являются contenteditable.
Я построил плагин 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();
Вы также можете удалить/добавить события, если хотите
innerHTML
стоит дорого). Я бы порекомендовал использовать событие input
там, где оно существует, и вернуться к чему-то подобному, но с каким-то опровержением.
Использование 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());
})
DOMCharacterDataModified
не будет срабатывать, когда пользователь изменяет существующий текст, например, применяя жирный шрифт или курсив. DOMSubtreeModified
более подходит в этом случае. Кроме того, люди должны помнить, что устаревшие браузеры не поддерживают эти события.
Проверьте эту идею. http://pastie.org/1096892
Я думаю, что это близко. HTML 5 действительно нужно добавить событие изменения в спецификацию. Единственная проблема заключается в том, что функция обратного вызова вычисляет if (до == $(this).html()) до фактического обновления содержимого в $(this).html(). setTimeout не работает, и это печально. Дайте мне знать, что вы думаете.
Чтобы избежать таймеров и кнопок "сохранить", вы можете использовать событие размытия, которое срабатывает, когда элемент теряет фокус. но чтобы убедиться, что элемент был фактически изменен (а не просто сфокусирован и расфокусирован), его контент следует сравнить с его последней версией. или используйте событие keydown для установки некоторого "грязного" флага в этом элементе.
Событие onchange не срабатывает, когда элемент с атрибутом contentEditable изменен, предлагаемым подходом может быть добавление кнопки, чтобы "сохранить" издание.
Проверьте этот плагин, который обрабатывает проблему таким образом:
На основании ответа @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>
Не ответ 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'))
Теперь этот элемент будет редактироваться пользователем до тех пор, пока он не потеряет фокус.
cut
,copy
иpaste
в браузерах, которые их поддерживают (IE 5 +, Firefox 3.0+, Safari 3+, Chrome)