contenteditable change events

303,614

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')});
Share:
303,614
Jourkey
Author by

Jourkey

Updated on October 18, 2021

Comments

  • Jourkey
    Jourkey over 2 years

    I want to run a function when a user edits the content of a div with contenteditable attribute. What's the equivalent of an onchange event?

    I'm using jQuery so any solutions that uses jQuery is preferred. Thanks!

  • Tim Down
    Tim Down about 14 years
    I 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 and paste events in browsers that support them (IE 5+, Firefox 3.0+, Safari 3+, Chrome)
  • Blowsie
    Blowsie about 13 years
    You could use keyup, and you wont have a timing issue.
  • Tim Down
    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
    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
    jwilcox09 over 11 years
    Worked perfect for me, thanks for both CoffeeScript and Javascript
  • jrullmann
    jrullmann over 11 years
    There 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
    jrullmann over 11 years
    Just 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
    Tim Down over 11 years
    It's not quite the same as the HTML5 input event (which is supported for contenteditable 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 the input event too, but I have no hard evidence for this.
  • Aen Tan
    Aen Tan almost 11 years
    I'm doing keyup with debounce.
  • Aen Tan
    Aen Tan almost 11 years
    Try keyup instead of keypress.
  • Tim Down
    Tim Down almost 11 years
    That will get slow and laggy as the editable content gets longer (innerHTML is expensive). I would recommend using the input event where it exists and falling back to something like this but with some kind of debouncing.
  • citykid
    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
    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
    TechAurelian over 10 years
    Update 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
    Tim Down over 10 years
    @Jamrelian: Indeed. Have you checked IE 11 preview?
  • Sebastien Lorber
    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
    Sampson about 10 years
    Internet Explorer 11+ supports Mutation Observers.
  • Admin
    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
    Gullbyrd about 9 years
    Unfortunately, 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
    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 the input event is part of it (w3.org/Bugs/Public/show_bug.cgi?id=13118) and most browsers implement it for contenteditable.
  • mikermcneil
    mikermcneil almost 9 years
    I 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 the input 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
    Bruno Ferreira over 7 years
    Note that $("div [contenteditable=true]") will select all childrens of a div, directly or indirectly, that are contenteditable.
  • Dan Philip
    Dan Philip about 7 years
    Added a new solution here
  • Johann
    Johann almost 7 years
    Just to make this clear. The "input" solution does not detect copy, paste or cut. It does however detect delete.
  • fishbone
    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
    Mike Willis over 6 years
    Ugh, 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
    James Cat over 6 years
    what is this delete event?
  • Nathan B
    Nathan B almost 6 years
    Nothing is fired in the jsfiddle in my chrome ver 65.
  • Nathan B
    Nathan B almost 6 years
    Why do you need the "return $this;"?
  • Tim Down
    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
    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
    Nathan B almost 6 years
    Thanks. When you paste text, it doesn't trigger the change twice?
  • balupton
    balupton almost 6 years
    @NadavB it shouldn't, as that is what "if ($this.data('before') !== $this.html()) {" is for
  • ˈvɔlə
    ˈvɔlə about 5 years
    This looks interesting. Can someone provide some explanation? 😇
  • revelt
    revelt almost 5 years
    for posterity, ten years forward and there's no need for a "save" button, check the accepted answer's jsfiddle
  • adelriosantiago
    adelriosantiago over 4 years
    Note: You may want to use either blur paste input or blur paste keyup in order to prevent the event from triggering twice.
  • Luke T O'Brien
    Luke T O'Brien almost 4 years
    The 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 for contenteditable
  • Jack Steam
    Jack Steam almost 4 years
    Remember, 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
    Jack Steam almost 4 years
    This 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 a contenteditable element changes?"
  • Jack Steam
    Jack Steam almost 4 years
    Just use the input event! If you need to support IE11, use a mutation observer. Love it. Short and to the point.
  • Benedikt
    Benedikt over 3 years
    This 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
    Automagisch over 2 years
    jQuery is never an appropriate answer @JackSteam
  • Auspex
    Auspex over 2 years
    Not really. The point is that the OP wants to do something on the final change (what a change event would do), while input 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
    code-is-life about 2 years
    Exactly what I needed. Removed keyup though as I need the alert to fire when element looses focus
  • code-is-life
    code-is-life about 2 years
    Am 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
    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.