share
Stack Overflowcontenteditable change events
[+499] [23] Jourkey
[2009-09-07 23:15:12]
[ javascript jquery html contenteditable ]
[ https://stackoverflow.com/questions/1391278/contenteditable-change-events ]

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!

(3) I usually do this: document.getElementById("editor").oninput = function() { ...}. - Basj
(1) There is an excellent answer for 2020 down the page: stackoverflow.com/a/49287032/4842857 - Jack Steam
[+432] [2009-09-11 14:39:53] Tim Down [ACCEPTED]

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 [1] 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/

[1] https://developer.mozilla.org/en-US/docs/Web/Reference/Events/input

(14) 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) - Tim Down
(6) You could use keyup, and you wont have a timing issue. - Blowsie
(3) @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. - Tim Down
(1) I'm doing keyup with debounce. - Aen Tan
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. - TechAurelian
@Jamrelian: Indeed. Have you checked IE 11 preview? - Tim Down
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. - Gullbyrd
@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. - Tim Down
Added a new solution here - Dan Philip Bejoy
Just to make this clear. The "input" solution does not detect copy, paste or cut. It does however detect delete. - Johann
(1) @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 - fishbone
Nothing is fired in the jsfiddle in my chrome ver 65. - Nathan G
(1) @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. - Tim Down
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 - Luke T O'Brien
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 - code-is-life
@code-is-life: You're absolutely right. I clearly read into the question what I was expecting rather than what was written. - Tim Down
1
[+221] [2011-06-07 09:54:30] balupton

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


(1) Worked perfect for me, thanks for both CoffeeScript and Javascript - jwilcox09
(7) 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 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) - Sebastien Lorber
@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 - user133408
Why do you need the "return $this;"? - Nathan G
@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. - balupton
Thanks. When you paste text, it doesn't trigger the change twice? - Nathan G
(1) @NadavB it shouldn't, as that is what "if ($this.data('before') !== $this.html()) {" is for - balupton
Note: You may want to use either blur paste input or blur paste keyup in order to prevent the event from triggering twice. - adelriosantiago
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. - Benedikt
2
[+69] [2012-12-26 17:24:42] jrullmann

Consider using MutationObserver [1]. These observers are designed to react to changes in the DOM, and as a performant replacement to Mutation Events [2].

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 [3] 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 [4] to compare using MutationObserers to handling a variety of events. I used balupton's code since his answer [5] has the most upvotes.
  • Mozilla [6] has an excellent page on the API
  • Take a look at the MutationSummary [7] library
[1] https://w3c.github.io/dom/#mutation-observers
[2] http://www.w3.org/TR/DOM-Level-3-Events/#interface-MutationEvent
[3] http://code.google.com/p/mutation-summary/
[4] http://jrullmann.github.com/contenteditable-onchange-approaches/
[5] https://stackoverflow.com/a/6263537/194957
[6] https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
[7] http://code.google.com/p/mutation-summary/

(2) 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. - Tim Down
(3) +1, but did you realize that Mutation Events do not report the effects of line feed in a contenteditable? Press enter in your snippet. - citykid
(5) @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. - Tim Down
Internet Explorer 11+ supports Mutation Observers. - Sampson
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) - mikermcneil
Snippet is missing. - Greggory Wiley
3
[+30] [2015-10-11 12:10:15] Developia

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

(5) what is this delete event? - James Cat
4
[+26] [2010-09-13 14:06:58] Dennkster

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

(1) Exactly what I needed. Removed keyup though as I need the alert to fire when element looses focus - code-is-life
5
[+25] [2018-03-14 20:23:22] Native Dev

Two options:

1) For modern (evergreen) browsers: The "input" event would act as an alternative "change" event.

https://developer.mozilla.org/en-US/docs/Web/Events/input

document.querySelector('div').addEventListener('input', (e) => {
    // Do something with the "change"-like event
});

or

<div oninput="someFunc(event)"></div>

or (with jQuery)

$('div').on('click', function(e) {
    // Do something with the "change"-like event
});

2) To account for IE11 and modern (evergreen) browsers: This watches for element changes and their contents inside the 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 });

(2) Just use the input event! If you need to support IE11, use a mutation observer. Love it. Short and to the point. - Jack Steam
(1) 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. - Auspex
6
[+12] [2018-11-07 10:29:14] superwf

const p = document.querySelector('p')
const result = document.querySelector('div')
const observer = new MutationObserver((mutationRecords) => {
  result.textContent = mutationRecords[0].target.data
  // result.textContent = p.textContent
})
observer.observe(p, {
  characterData: true,
  subtree: true,
})
<p contenteditable>abc</p>
<div />


(4) This looks interesting. Can someone provide some explanation? 😇 - ˈvɔlə
7
[+4] [2011-01-28 21:09:04] gregory whiteside

Here's what worked for me:

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

8
[+4] [2012-08-21 07:36:32] tracend

This thread was very helpful while I was investigating the subject.

I've modified some of the code available here into a jQuery plugin so it is in a re-usable form, primarily to satisfy my needs but others may appreciate a simpler interface to jumpstart using contenteditable tags.

https://gist.github.com/3410122

Update:

Due to its increasing popularity the plugin has been adopted by Makesites.org [1]

Development will continue from here:

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

[1] http://makesites.org

9
[+4] [2016-05-17 09:09:51] DrMcCleod

Non JQuery answer...

function makeEditable(elem){
    elem.setAttribute('contenteditable', 'true');
    elem.addEventListener('blur', function (evt) {
        elem.removeAttribute('contenteditable');
        elem.removeEventListener('blur', evt.target);
    });
    elem.focus();
}

To use it, call on (say) a header element with id="myHeader"

makeEditable(document.getElementById('myHeader'))

That element will now be editable by the user until it loses focus.


(7) 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. - Mike Willis
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
10
[+4] [2019-11-17 17:15:02] Dávid Konkoly

In Angular 2+

<div contentEditable (input)="type($event)">
   Value
</div>

@Component({
  ...
})
export class ContentEditableComponent {

 ...

 type(event) {
   console.log(event.data) // <-- The pressed key
   console.log(event.path[0].innerHTML) // <-- The content of the div 
 }
}



11
[+3] [2010-05-09 06:19:14] joox

To avoid timers and "save" buttons, you may use blur event wich fires when the element loses focus. but to be sure that the element was actually changed (not just focused and defocused), its content should be compared against its last version. or use keydown event to set some "dirty" flag on this element.


12
[+3] [2011-05-05 17:22:51] user740433

Here is the solution I ended up using and works fabulously. I use $(this).text() instead because I am just using a one line div that is content editable. But you may also use .html() this way you dont have to worry about the scope of a global/non-global variable and the before is actually attached to the editor 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!');
    }
});

13
[+3] [2021-04-01 01:36:50] MD SHAYON

You need to use input event type

Demo

HTML

<div id="editor" contenteditable="true" >Some text here</div>

JS

const input = document.getElementById('editor');


input.addEventListener('input', updateValue);

function updateValue(e) {
  console.log(e.target);
}

know more [1]

[1] https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event

14
[+2] [2021-10-18 07:41:13] Carson

For me, I want to check the input is valid or not.

If valid, then update, Otherwise show an error message and keep the value as same as before.

Skill: When you edit done, usually, it will trigger the blur [1] event.

Example

<span contenteditable="true">try input somethings.</span>
<script>
  const elem = document.querySelector(`span`)
  let oldValue = elem.innerText
  elem.onkeydown = (keyboardEvent) => {
    if (keyboardEvent.key === "Enter") {
      elem.blur() // set focusout
    }
  }
  elem.onblur = (e) => {
    const curValue = elem.innerText
    if (curValue === oldValue) {
      return
    }
    if (curValue.length <= 50) { // 👈 Input your conditions.
      // 👇 fail
      elem.innerText = oldValue
      
      // (Optional) Add error message
      elem.insertAdjacentHTML("beforeend", `<span style="margin-left:5px;color:red">error length=${curValue.length}. Must greater than 50. undo to the previous value.</span>`)
      const errMsg = elem.querySelector(`span`)
      setTimeout(() => errMsg.remove(), 3500) // wait 3.5 second, and then remove it.
      return
    }
    // 👇 OK, update
    oldValue = curValue
  }
</script>

[1] https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event

15
[+1] [2012-02-28 14:33:57] janfabian

Using DOMCharacterDataModified under MutationEvents [1] will lead to the same. The timeout is setup to prevent sending incorrect values (e.g. in Chrome I had some issues with space key)

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 example [2]

[1] http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MutationEvent
[2] http://jsfiddle.net/vgnQg/

(1) +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. - Andy E
(3) Just a note that Mutation Events are depreciated by the w3c because of performance problems. More can be found in this Stack Overflow question. - jrullmann
16
[0] [2009-09-07 23:56:10] Christian C. Salvadó

The onchange event doesn't fires when an element with the contentEditable attribute is changed, a suggested approach could be to add a button, to "save" the edition.

Check this plugin which handles the issue in that way:

[1] http://www.west-wind.com/Weblog/posts/778165.aspx

for posterity, ten years forward and there's no need for a "save" button, check the accepted answer's jsfiddle - revelt
17
[0] [2013-06-04 16:22:53] Blowsie

I built a jQuery plugin to do this.

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

You can simply call $('.wysiwyg').wysiwygEvt();

You can also remove / add events if you wish


(2) 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. - Tim Down
18
[0] [2016-03-29 09:44:53] sarath

A simple answer in JQuery, I just created this code and thought it will be helpful for others too

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

Note that $("div [contenteditable=true]") will select all childrens of a div, directly or indirectly, that are contenteditable. - Bruno Ferreira
19
[0] [2023-08-08 19:48:57] Nim
  1. Set input event trigger on the contenteditable-element
document.getElementById("contenteditableElement").addEventListener("input", function() {
            elementEdited();
        }, false);
  1. Save value once contenteditable-element looses focus
    jQuery("#contenteditableElement").blur(function(){
        //once element looses focus, you can save the new value etc..
        });

20
[0] [2024-02-14 06:35:20] Ronnie Royston

Focus / Blur events. Use the blur (unfocus) event to record the changes.

document.querySelector("p").addEventListener("focus",function(e){
  console.log("focus :" + e.target.textContent);
})
document.querySelector("p").addEventListener("blur",function(e){
  console.log("blur :" + e.target.textContent);
})
<p contenteditable>This is a paragraph element</p>


21
[-1] [2010-08-17 03:14:52] Lawrence Whiteside

Check this idea out. http://pastie.org/1096892

I think it's close. HTML 5 really needs to add the change event to the spec. The only problem is that the callback function evaluates if (before == $(this).html()) before the content is actually updated in $(this).html(). setTimeout don't work, and it's sad. Let me know what you think.


Try keyup instead of keypress. - Aen Tan
22
[-2] [2017-08-30 18:25:34] Phillip Senn

Based on @balupton's answer:

$(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>


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
jQuery is never an appropriate answer @JackSteam - Automagisch
23