contenteditable change events
Solution 1
2022 update
As pointed out in the comments, this doesn't answer the question asked, which wanted the equivalent of the change
event rather than the input
event. However, I'll leave it here as is.
Original answer
I'd suggest attaching listeners to key events fired by the editable element, though you need to be aware that keydown
and keypress
events are fired before the content itself is changed. This won't cover every possible means of changing the content: the user can also use cut, copy and paste from the Edit or context browser menus, so you may want to handle the cut
copy
and paste
events too. Also, the user can drop text or other content, so there are more events there (mouseup
, for example). You may want to poll the element's contents as a fallback.
UPDATE 29 October 2014
The HTML5 input
event is the answer in the long term. At the time of writing, it is supported for contenteditable
elements in current Mozilla (from Firefox 14) and WebKit/Blink browsers, but not IE.
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/
Solution 2
Here is a more efficient version which uses on
for all contenteditables. It's based off the top answers here.
$('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');
}
});
The project is here: https://github.com/balupton/html5edit
Solution 3
Consider using MutationObserver. These observers are designed to react to changes in the DOM, and as a performant replacement to Mutation Events.
Pros:
- Fires when any change occurs, which is difficult to achieve by listening to key events as suggested by other answers. For example, all of these work well: drag & drop, italicizing, copy/cut/paste through context menu.
- Designed with performance in mind.
- Simple, straightforward code. It's a lot easier to understand and debug code that listens to one event rather than code that listens to 10 events.
- Google has an excellent mutation summary library which makes using MutationObservers very easy.
Cons:
- Requires a very recent version of Firefox (14.0+), Chrome (18+), or IE (11+).
- New API to understand
- Not a lot of information available yet on best practices or case studies
Learn more:
- I wrote a little snippet to compare using MutationObserers to handling a variety of events. I used balupton's code since his answer has the most upvotes.
- Mozilla has an excellent page on the API
- Take a look at the MutationSummary library
Solution 4
non jQuery quick and dirty answer:
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);
});
Solution 5
I have modified lawwantsin 's answer like so and this works for me. I use the keyup event instead of keypress which works great.
$('#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')});
Jourkey
Updated on October 18, 2021Comments
-
Jourkey over 2 years
I want to run a function when a user edits the content of a
div
withcontenteditable
attribute. What's the equivalent of anonchange
event?I'm using jQuery so any solutions that uses jQuery is preferred. Thanks!
-
Tim Down about 14 yearsI should also add that it's possible to change an editable element by using the browser's edit menu or context menu to cut, copy or paste content, so you'll need to handle
cut
,copy
andpaste
events in browsers that support them (IE 5+, Firefox 3.0+, Safari 3+, Chrome) -
Blowsie about 13 yearsYou could use keyup, and you wont have a timing issue.
-
Tim Down about 13 years@Blowsie: Yes, but you could potentially end up with lots of changes happening from an auto-repeated keypress before the event fires. There are also other ways of changing editable text not considered by this answer, such as drag and drop and edits via the Edit and context menus. Long-term, this should all be covered by the HTML5
input
event. -
Andy E about 12 years+1 -
DOMCharacterDataModified
won't fire when the user modifies existing text, for example applying bold or italics.DOMSubtreeModified
is more appropriate in this case. Also, people should remember that legacy browsers don't support these events. -
jwilcox09 over 11 yearsWorked perfect for me, thanks for both CoffeeScript and Javascript
-
jrullmann over 11 yearsThere are some changes that won't be caught by this code until the contenteditable is blurred. For example: dragging & dropping, cutting from the context menu, and pasting from the browser window. I've setup an example page that uses this code which you can interact with here.
-
jrullmann over 11 yearsJust a note that Mutation Events are depreciated by the w3c because of performance problems. More can be found in this Stack Overflow question.
-
Tim Down over 11 yearsIt's not quite the same as the HTML5
input
event (which is supported forcontenteditable
in all the WebKit and Mozilla browsers that support mutation observers), since DOM mutation could occur via script as well as user input, but it's a viable solution for those browsers. I imagine it could harm performance more than theinput
event too, but I have no hard evidence for this. -
Aen Tan almost 11 yearsI'm doing keyup with debounce.
-
Aen Tan almost 11 yearsTry keyup instead of keypress.
-
Tim Down almost 11 yearsThat will get slow and laggy as the editable content gets longer (
innerHTML
is expensive). I would recommend using theinput
event where it exists and falling back to something like this but with some kind of debouncing. -
citykid over 10 years+1, but did you realize that Mutation Events do not report the effects of line feed in a contenteditable? Press enter in your snippet.
-
Tim Down over 10 years@citykid: That's because the snippet is only watching for changes to character data. It's possible to observe DOM structural changes too. See jsfiddle.net/4n2Gz/1, for example.
-
TechAurelian over 10 yearsUpdate Oct 2013: Unfortunately it still does not work in IE, not even in IE10! It's a pity, because it works fine in Chrome and Firefox, and indeed is the best solution.
-
Tim Down over 10 years@Jamrelian: Indeed. Have you checked IE 11 preview?
-
Sebastien Lorber about 10 years@jrullmann Yes I had problems with this code when drag&dropping images because it doesn't listen to the image load (it was a problem to handle a resize operation in my case)
-
Sampson about 10 yearsInternet Explorer 11+ supports Mutation Observers.
-
Admin almost 10 years@jrullmann Very nice, this works even then changing value with javascript, try in console:
document.getElementById('editor_input').innerHTML = 'ssss'
. Drawback is that it requires IE11 -
Gullbyrd about 9 yearsUnfortunately, key events are not sufficient. You can also change the contents by cut and paste with the mouse or drag and drop (both of which could be monitored with mouse events, I guess). But HTM5 input is not an answer, because the question was about contenteditable divs.
-
Tim Down about 9 years@Gullbyrd: Yes. I should have mentioned cut/copy/paste/drag-and-drop; I have done so in similar answers (stackoverflow.com/a/8694125/96100, for example). However, I'm not wrong about the HTML5
input
event. The HTML Editing spec seems to have stalled but theinput
event is part of it (w3.org/Bugs/Public/show_bug.cgi?id=13118) and most browsers implement it forcontenteditable
. -
mikermcneil almost 9 yearsI was able to verify (at least in Chrome 43.0.2357.130) that the HTML5
input
event fires in each of these situations (specifically drag & drop, italicizing, copy/cut/paste through context menu). I also tested bolding w/ cmd/ctrl+b and got the expected results. I also checked and was able to verify that theinput
event does not fire on programmatic changes (which seems obvious, but is arguably contrary to some confusing language on the relevant MDN page; see the bottom of the page here to see what I mean: developer.mozilla.org/en-US/docs/Web/Events/input) -
Bruno Ferreira over 7 yearsNote that
$("div [contenteditable=true]")
will select all childrens of a div, directly or indirectly, that are contenteditable. -
Dan Philip about 7 yearsAdded a new solution here
-
Johann almost 7 yearsJust to make this clear. The "input" solution does not detect copy, paste or cut. It does however detect delete.
-
fishbone over 6 years@AndroidDev I testet Chrome, and the input event is also fired on copy paste and cut as required by the specs: w3c.github.io/uievents/#events-inputevents
-
Mike Willis over 6 yearsUgh, why the downvote with no explanation? This does a disservice to both the person who wrote the answer, and everyone who reads the answer later.
-
James Cat over 6 yearswhat is this delete event?
-
Nathan B almost 6 yearsNothing is fired in the jsfiddle in my chrome ver 65.
-
Nathan B almost 6 yearsWhy do you need the "return $this;"?
-
Tim Down almost 6 years@NadavB: Something has changed in jsFiddle that made it not work any more. I've replaced the fiddle with a code snippet and it works fine now.
-
balupton almost 6 years@NadavB result from the coffeescript conversion, in coffeescript you must always return things as it has implicit returns, and with jquery a return false can mean prevent default action. I've removed the coffeescript code (no one uses it anymore) and updated the javascript code.
-
Nathan B almost 6 yearsThanks. When you paste text, it doesn't trigger the change twice?
-
balupton almost 6 years@NadavB it shouldn't, as that is what "if ($this.data('before') !== $this.html()) {" is for
-
ˈvɔlə about 5 yearsThis looks interesting. Can someone provide some explanation? 😇
-
revelt almost 5 yearsfor posterity, ten years forward and there's no need for a "save" button, check the accepted answer's jsfiddle
-
adelriosantiago over 4 yearsNote: You may want to use either
blur paste input
orblur paste keyup
in order to prevent the event from triggering twice. -
Luke T O'Brien almost 4 yearsThe issue I have is that
input
is fired on every keypress, I want an event that fires at the end, I'm not sure if the change event works forcontenteditable
-
Jack Steam almost 4 yearsRemember, if you down vote an answer, you are responsible to leave a comment. Explain why the answer is unsatisfactory (unless someone has already left a comment, and you just agree with them).
-
Jack Steam almost 4 yearsThis code makes an element
contenteditable
until it loses focus. I don't think this answers the question. The question is "How can I run a function when acontenteditable
element changes?" -
Jack Steam almost 4 yearsJust use the
input
event! If you need to support IE11, use a mutation observer. Love it. Short and to the point. -
Benedikt over 3 yearsThis is still the best solution in 2021, I just removed 'keyup' and 'input' from the event list to get REAL 'change' event behaviour, which only fires after losing focus on the element and not while user is still doing changes to the string.
-
Automagisch over 2 yearsjQuery is never an appropriate answer @JackSteam
-
Auspex over 2 yearsNot really. The point is that the OP wants to do something on the final change (what a
change
event would do), whileinput
is firing as you type. I'm firing an AJAX request -- I don't want to do that on every keypress. Many of the other answers explain how to handle that. -
code-is-life about 2 yearsExactly what I needed. Removed keyup though as I need the alert to fire when element looses focus
-
code-is-life about 2 yearsAm I missing something here? The author specifically asked for something similar to the onchange event but this fires after every key input? The answer from Dennkster solved my problem but with a slight modification
-
Tim Down about 2 years@code-is-life: You're absolutely right. I clearly read into the question what I was expecting rather than what was written.