OnChange событие с contenteditable

Код:

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

Событие onChange не работает. (по крайней мере, в FireFox)
Я не хочу использовать теги textarea/input, потому что должно быть доступно изменение только определенных слов в тексте и должно отображаться в строке (а не в блоке).

Есть ли способ, как это сделать?

Ответ 1

Функция, которую я написал:
Один вызов этой функции фиксирует все элементы ContentEditable на странице.

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

Ответ 2

Не совсем. Недавние браузеры WebKit поддерживают событие HTML5 input на contenteditable, что идеально, но не поддерживается в других браузерах (UPDATE 31 декабря 2012: Firefox поддерживает это с версии 14). В противном случае вы сможете пройти через события мутации DOM DOMNodeInserted, DOMNodeRemoved и DOMCharacterDataModified, но они имеют два недостатка: во-первых, они не поддерживаются в IE < 9 или любой версии Opera для элементов contenteditable, а во-вторых, новая спецификация с замещающими событиями работает, то есть они, вероятно, будут заменены в будущих браузерах.

Пример в реальном времени: http://jsfiddle.net/MBags/

Или вы можете пойти на более низкий уровень и обрабатывать события клавиш, мыши и буфера обмена (cut и paste), которые будут работать во всех основных браузерах, но будут означать, что вам нужно будет проверить, изменился ли изменяемый контент каждый время, когда такое событие срабатывает, которое будет ломаться и вредить пользователю большим количеством контента.

Ответ 3

Я построил плагин jQuery для этого.

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

Вы можете просто вызвать $('.wysiwyg').wysiwygEvt();

Вы также можете удалить/добавить события, если хотите

Ответ 4

Поскольку элементы, не входящие в элементы ввода, не имеют традиционных событий, связанных с вводом, вам нужно как-то объединить что-то вместе. С этой целью:

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.

Хотя, как отмечено Tim Down, это не должно быть необходимо слишком долго, с очевидным исключением для поддержки старых браузеров.