コンテンツ編集可能な変更イベント


359

ユーザーがdivwith contenteditable属性のコンテンツを編集するときに関数を実行したいのですが。onchangeイベントに相当するものは何ですか?

私はjQueryを使用しているため、jQueryを使用するソリューションが推奨されます。ありがとう!


私は通常これを行います:document.getElementById("editor").oninput = function() { ...}
Basj

回答:


311

私はあなたがいることを認識する必要がありますけれども、キーイベントにアタッチリスナーは、編集可能な要素により解雇をお勧めしたいkeydownkeypress、イベント自体が変更されたコンテンツの前に解雇されています。これは、コンテンツを変更するすべての可能な手段をカバーするわけではありません。ユーザーは編集またはコンテキストブラウザメニューから切り取り、コピー、貼り付けを使用することもできるため、cut copyおよびpasteイベントも処理することができます。また、ユーザーはテキストやその他のコンテンツをドロップできるので、そこにはより多くのイベントがあります(mouseupたとえば)。フォールバックとして要素のコンテンツをポーリングすることができます。

2014年10月29日更新

HTML5のinputイベントは、長期的に答えています。執筆時点ではcontenteditable、現在のMozilla(Firefox 14以降)およびWebKit / Blinkブラウザーの要素でサポートされていますが、IEではサポートされていません。

デモ:

document.getElementById("editor").addEventListener("input", function() {
    console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>

デモ:http : //jsfiddle.net/ch6yn/2691/


11
また、ブラウザーの編集メニューまたはコンテキストメニューを使用してコンテンツを切り取り、コピー、または貼り付けることにより、編集可能な要素を変更できるのでcut、それらをサポートするブラウザー(IE 5 +、Firefox)でイベントcopypasteイベントを処理する必要があります。3.0以降、Safari 3以降、Chrome)
Tim Down

4
キーアップを使用でき、タイミングの問題は発生しません。
ブロージー

2
@Blowsie:はい。ただし、イベントが発生する前に、自動繰り返しのキープレスから多くの変更が発生する可能性があります。この回答では考慮されていない編集可能なテキストを変更する他の方法もあります。たとえば、ドラッグアンドドロップや、編集メニューやコンテキストメニューによる編集などです。長期的には、これはすべてHTML5 inputイベントでカバーされるはずです。
ティムダウン、

1
私はデバウンスでキーアップを行っています。
Aen Tan

1
@AndroidDev私はChromeをtestet、および仕様によって要求されるように、入力イベントは、コピーペーストやカットに解雇されています。w3c.github.io/uievents/#events-inputevents
フィッシュボーン

188

これはon、すべてのcontenteditableに使用するより効率的なバージョンです。ここでのトップアンサーに基づいています。

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

プロジェクトはここにあります: https //github.com/balupton/html5edit


CoffeeScriptとJavascriptの両方に感謝します
jwilcox09

7
contenteditableが不鮮明になるまで、このコードでキャッチされないいくつかの変更があります。例:ドラッグアンドドロップ、コンテキストメニューからの切り取り、ブラウザーウィンドウからの貼り付け。ここで操作できるこのコードを使用するサンプルページを設定しました
jrullmann

@jrullmannはい画像の読み込みをリッスンしないため、画像をドラッグ&ドロップするときにこのコードに問題がありました(私の場合、サイズ変更操作を処理するのが問題でした)
Sebastien Lorber '26

@jrullmann非常に素晴らしい、これはjavascriptで値を変更しても機能しますdocument.getElementById('editor_input').innerHTML = 'ssss'。コンソールで試してください。欠点は、IE11が必要なことです

1
@NadavBすべきではありません。「if($ this.data( 'before')!== $ this.html()){」の目的は
balupton

52

MutationObserverの使用を検討してください。これらのオブザーバーは、DOMの変更に対応するように設計されており、Mutation Eventsのパフォーマンスに代わるものです。

長所:

  • 発生した場合他の回答により示唆されるようにキーイベントを聞くことによって達成することは困難である変化が起き、。たとえば、ドラッグアンドドロップ、斜体、コピー/切り取り/貼り付けのコンテキストメニューを使用すると、これらすべてがうまく機能します。
  • パフォーマンスを考慮して設計されています。
  • シンプルでわかりやすいコード。10個のイベントをリッスンするコードよりも、1つのイベントをリッスンするコードを理解してデバッグする方がはるかに簡単です。
  • Googleには、MutationObserversを非常に簡単に使用できる優れた突然変異要約ライブラリがあります。

短所:

  • 最新バージョンのFirefox(14.0以降)、Chrome(18以降)、またはIE(11以降)が必要です。
  • 理解する新しいAPI
  • ベストプラクティスやケーススタディに関する情報はまだ多くありません

もっと詳しく知る:

  • MutationObserersを使用してさまざまなイベントを処理することと比較するための小さなスニペットを書きました。私はバラプトンのコードを使用しました。彼の回答の投票数が最も多いからです。
  • MozillaにはAPIに関する優れたページがあります
  • MutationSummaryライブラリをご覧ください

DOMミューテーションはスクリプトとユーザー入力によって発生する可能性があるinputため、HTML5 イベント(contenteditableミューテーションオブザーバーをサポートするすべてのWebKitおよびMozillaブラウザーでサポートされています)とはまったく異なりますが、これらのブラウザーでは実行可能なソリューションです。inputイベントよりもパフォーマンスに悪影響を与える可能性があると思いますが、これについて明確な証拠はありません。
Tim Down

2
+1ですが、Mutation Eventsがcontenteditableの改行の影響を報告しないことをご存知ですか?スニペットでEnterキーを押します。
シティキッド2013

4
@citykid:これは、スニペットがキャラクターデータの変更のみを監視しているためです。DOM構造の変化を観察することも可能です。たとえば、jsfiddle.net/4n2Gz /1を参照してください。
Tim Down

Internet Explorer 11以降はMutation Observersをサポートしています。
サンプソン2014年

私は(少なくともChrome 43.0.2357.130で)inputこれらの状況(具体的には、ドラッグアンドドロップ、斜体、コンテキストメニューからのコピー/切り取り/貼り付け)でHTML5 イベントが発生することを確認できました。また、cmd / ctrl + bを使用して太字をテストし、期待どおりの結果を得ました。また、プログラムによる変更でinputイベント発生しないことを確認し、確認できました(これは明白なようですが、関連するMDNページの混乱する言語に間違いなく矛盾しています。このページの下部を参照して、意味を確認してください:開発者.mozilla.org / en-US / docs / Web / Events / input
mikermcneil 2015

22

私はlawwantsinの答えをそのように変更しましたが、これは私にとってはうまくいきます。うまく機能するkeypressの代わりにkeyupイベントを使用します。

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

22

非jQueryの迅速かつ汚い答え:

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

3
この削除イベントは何ですか?
James Cat

7

2つのオプション:

1)最新(常緑)ブラウザーの場合: 「入力」イベントは、代替の「変更」イベントとして機能します。

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

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

または

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

または(jQueryを使用)

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

2)IE11および最新の(常緑)ブラウザーを考慮する: これは、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 });

7

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


2
これは面白いですね。誰かが何らかの説明を提供できますか?😇
WoIIe

3

これが私のために働いたものです:

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

3

このスレッドは、対象を調査しているときに非常に役立ちました。

ここで使用可能なコードの一部をjQueryプラグインに変更しました。これは、主に私のニーズを満たすために再利用可能な形式になるが、他の人はcontenteditableタグを使用してジャンプスタートするためのよりシンプルなインターフェースを高く評価する場合があります。

https://gist.github.com/3410122

更新:

その人気の高まりにより、プラグインはMakesites.orgに採用されました

ここから開発が続きます:

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


2

これが私が最終的に使用したソリューションで、すばらしい働きをします。代わりに$(this).text()を使用しています。これは、コンテンツの編集が可能な1行のdivを使用しているためです。ただし、この方法で.html()を使用しても、グローバル変数または非グローバル変数のスコープを気にする必要がなく、beforeが実際にエディターの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!');
    }
});

1

タイマーと「保存」ボタンを回避するには、要素がフォーカスを失ったときに発火するぼかしイベントを使用できます。ただし、要素が実際に変更されたことを確認するために(フォーカスとデフォーカスだけでなく)、そのコンテンツを最後のバージョンと比較する必要があります。またはkeydownイベントを使用して、この要素に「ダーティ」フラグを設定します。


1

JQueryで簡単な答え、私はこのコードを作成し、他の人にも役立つと思いました

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

$("div [contenteditable=true]")は、コンテンツ編集可能なdivの子を直接または間接的にすべて選択することに注意してください。
Bruno Ferreira

1

JQuery以外の回答...

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

それを使用するには、(たとえば)id = "myHeader"を含むヘッダー要素を呼び出します

makeEditable(document.getElementById('myHeader'))

その要素は、フォーカスを失うまでユーザーが編集できるようになります。


5
なぜ、説明なしの反対投票なのか。これは、答えを書いた人と後で答えを読んだ人の両方に害を及ぼします。
マイクウィリス

0

onchangeイベントは、contentEditable属性を持つ要素が変更されても発生しません。推奨されるアプローチは、ボタンを追加してエディションを「保存」することです。

その方法で問題を処理するこのプラグインを確認してください:


後世のために、10年先にあり、「保存」ボタンは必要ありません。承認された回答のjsfiddleを確認してください
反覆

0

MutationEventsでDOMCharacterDataModifiedを使用すると、同じ結果になります。タイムアウトは、誤った値を送信しないように設定されています(たとえば、Chromeではスペースキーに問題がありました)

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


1
+1- DOMCharacterDataModifiedユーザーが既存のテキストを変更した場合、たとえばボールドやイタリックを適用した場合は発生しません。 DOMSubtreeModifiedこの場合、より適切です。また、レガシーブラウザはこれらのイベントをサポートしていないことを覚えておいてください。
アンディE

3
パフォーマンスの問題により、Mutation Eventsはw3cによって非推奨になっていることに注意してください。詳細については、このスタックオーバーフローの質問をご覧ください。
jrullmann、

0

これを行うための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')
                }
            })
        })
    }
})(jQuery);

あなたは単に呼び出すことができます $('.wysiwyg').wysiwygEvt();

必要に応じて、イベントを削除/追加することもできます


2
編集可能なコンテンツが長くなると(innerHTMLコストがかかります)、速度が遅くなり、時間がかかります。inputそれが存在するイベントを使用して、このような何かにフォールバックすることをお勧めしますが、何らかのデバウンスを伴います。
Tim Down

0

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


0

次の簡単な解決策に従ってください

.tsでは:

getContent(innerText){
  this.content = innerText;
}

.html:

<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>

-1

このアイデアをチェックしてください。 http://pastie.org/1096892

近いと思います。HTML 5では、変更イベントを仕様に追加する必要があります。唯一の問題は、コンテンツが実際に$(this).html()で更新される前に(before == $(this).html())かどうかをコールバック関数が評価することです。setTimeoutが機能せず、悲しいです。どう考えているか教えてください。


キープレスの代わりにキーアップを試してください。
Aen Tan 2013

-2

@baluptonの答えに基づいて:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.