contenteditable change events

JavascriptJqueryHtmlContenteditable

Javascript Problem Overview


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!

Javascript Solutions


Solution 1 - Javascript

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 - Javascript

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 - Javascript

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 - Javascript

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 - Javascript

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

Solution 6 - Javascript

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

Solution 7 - Javascript

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 />

Solution 8 - Javascript

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

Solution 9 - Javascript

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

Development will continue from here:

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

Solution 10 - Javascript

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


Solution 11 - Javascript

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.

Solution 12 - Javascript

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

Solution 13 - Javascript

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.

Solution 14 - Javascript

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

Solution 15 - Javascript

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:

Solution 16 - Javascript

Using DOMCharacterDataModified under MutationEvents 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

Solution 17 - Javascript

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

Solution 18 - Javascript

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

Solution 19 - Javascript

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

Solution 20 - Javascript

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.

Solution 21 - Javascript

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>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJourkeyView Question on Stackoverflow
Solution 1 - JavascriptTim DownView Answer on Stackoverflow
Solution 2 - JavascriptbaluptonView Answer on Stackoverflow
Solution 3 - JavascriptjrullmannView Answer on Stackoverflow
Solution 4 - JavascriptDevelopiaView Answer on Stackoverflow
Solution 5 - JavascriptDennksterView Answer on Stackoverflow
Solution 6 - JavascriptModularView Answer on Stackoverflow
Solution 7 - JavascriptsuperwfView Answer on Stackoverflow
Solution 8 - Javascriptgregory whitesideView Answer on Stackoverflow
Solution 9 - JavascripttracendView Answer on Stackoverflow
Solution 10 - JavascriptDávid KonkolyView Answer on Stackoverflow
Solution 11 - JavascriptjooxView Answer on Stackoverflow
Solution 12 - Javascriptuser740433View Answer on Stackoverflow
Solution 13 - JavascriptDrMcCleodView Answer on Stackoverflow
Solution 14 - JavascriptMD SHAYONView Answer on Stackoverflow
Solution 15 - JavascriptChristian C. SalvadóView Answer on Stackoverflow
Solution 16 - JavascriptjanfabianView Answer on Stackoverflow
Solution 17 - JavascriptBlowsieView Answer on Stackoverflow
Solution 18 - JavascriptsarathView Answer on Stackoverflow
Solution 19 - JavascriptCarsonView Answer on Stackoverflow
Solution 20 - JavascriptLawrence WhitesideView Answer on Stackoverflow
Solution 21 - JavascriptPhillip SennView Answer on Stackoverflow