48

Code like:

...text <span contenteditable="true" onChange="someFunction()">blah blah</span> text...

The onChange event doesn't work. (at least in FireFox)
I don't want to use textarea/input tags, because there must be available to change only particular words in text, and must be displayed inline (not block).

Is there any way how to do that ?

Dani-Br
  • 2,289
  • 5
  • 25
  • 32
  • 1
    Why not use an `input` element and style away the border/padding and make in `display:inline` or `display:inline-block` – Chad Jan 01 '12 at 16:20

4 Answers4

34

Not really. Recent WebKit browsers support the HTML5 input event on contenteditable elements, which is ideal, but not supported in other browsers (UPDATE 31 December 2012: Firefox supports this as of version 14). Otherwise you may be able to get by with DOM mutation events DOMNodeInserted, DOMNodeRemoved and DOMCharacterDataModified, but these have two disadvantages: firstly, they are not supported in IE < 9 or any version of Opera for contenteditable elements, and secondly, a new spec with replacement events is in the works, meaning they are likely to be replaced in future browsers.

Live example: http://jsfiddle.net/MBags/

Or you could go lower level and handle key, mouse and clipboard events (cut and paste), which will work in all major browsers but will mean you'll need to check whether the editable content has changed each time such an event fires, which will get laggy and harm user experience for large pieces of content.

Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 1
    onInput event is very poor solution. first reason - it happens every time when user change even a single letter. second reason - it dosn't work even in FireFox 8.0 – Dani-Br Jan 01 '12 at 16:39
  • 4
    @Dani-Br: What? My answer says that the `input` event is only supported in WebKit, hence not in Firefox. Second, your question is not at all clear about *when* you need to detect changes to the editable content; the `input` event is the most similar event to to the `change` event of form inputs, although I concede that they are quite different. – Tim Down Jan 02 '12 at 01:06
  • Unfortunately the _input_ event does not work on _contenteditable_ elements in IE, not even in IE10! – TechAurelian Oct 03 '13 at 13:12
15

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');
                    htmlOld = htmlNew;
                }
            })
        })
    }
})(jQuery);

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

You can also remove / add events if you wish

Blowsie
  • 40,239
  • 15
  • 88
  • 108
9

Because non-input elements don't have traditional input-like events, you have to sort of mash something together yourself. To that end:

var editable = document.querySelectorAll('div[contentEditable]');

for (var i=0, len = editable.length; i<len; i++){
    editable[i].setAttribute('data-orig',editable[i].innerHTML);

    editable[i].onblur = function(){
        if (this.innerHTML == this.getAttribute('data-orig')) {
            // no change
        }
        else {
            // change has happened, store new value
            this.setAttribute('data-orig',this.innerHTML);
        }
    };
}

JS Fiddle demo.

Though as noted by Tim Down this shouldn't be necessary for too much longer, with the obvious exception of supporting older browsers.

Community
  • 1
  • 1
David Thomas
  • 249,100
  • 51
  • 377
  • 410
9

The function that I wrote:
One call of this function fixes all ContentEditable elements in the page.

function fix_onChange_editable_elements()
{
  var tags = document.querySelectorAll('[contenteditable=true][onChange]');//(requires FF 3.1+, Safari 3.1+, IE8+)
  for (var i=tags.length-1; i>=0; i--) if (typeof(tags[i].onblur)!='function')
  {
    tags[i].onfocus = function()
    {
      this.data_orig=this.innerHTML;
    };
    tags[i].onblur = function()
    {
      if (this.innerHTML != this.data_orig)
        this.onchange();
      delete this.data_orig;
    };
  }
}
Dani-Br
  • 2,289
  • 5
  • 25
  • 32
  • 1
    If you need to know about "live" changes, the above principle can be adapted to work under a `setInterval` (with a sensible interval settings and an eventual `clearInterval`). – Oliver Moran Nov 19 '13 at 23:35