67
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptでテキストが変更された瞬間を検出するために必要なイベントを調べた。

Last updated at Posted at 2016-11-23

テキストボックスの変更を検出しようと思ったとき、まずchangeイベントが思いつくが、
changeイベントはテキストボックスからフォーカスが外れた時にしか発火しない。

文字を入力した瞬間を検出するにはkeypressやkeydownが使えるが、IME入力モードだと検出できない。

全部漏らさずに拾おうとすると結構大変だったのでメモも兼ねて残します。
以下、Chrome, Firefox, IEについての記述です
他のブラウザは未検証です。

##入力方法の区別をしなくてよい場合##

InputEventで全て拾える。

$("#text").on({
    "input" : function(e) {
        // 入力時の処理
    }
});
  • 全ての入力をinputイベントで拾える
  • IME入力と半角入力やカット・ペースト・ドロップ等の区別はできない
  • キャンセルはできない
  • 戻すものがもうなかったりしてアンドゥリドゥができなかったとき
    • Chrome,IEは発火しない
    • Firefoxは発火する
  • クリップボードが空でペーストしても値が変わらなかったとき
    • Firefox,IEは発火しない
    • Chromeは発火する
  • テキストが選択されておらず、カットするものがなかったとき
    • 全てのブラウザで発火しない

##入力方法の区別をしたい場合 もしくは入力をキャンセルしたい場合##

半角入力はkeypress
アンドゥ・リドゥはkeydown
IME入力はkeydownかCompositionEvent
カット・ペースト・ドロップはそれぞれのイベントで拾う

var isProcessed = false;
$("#text").on({
    "keypress" : function(e) {
        if(!e.ctrlKey && !e.altKey){
            // 半角入力の処理
            isProcessed = true;
        }
    },

    "keydown" : function(e) {
        if(e.ctrlKey && e.keyCode == 90){
            // アンドゥの処理
            isProcessed = true;
        }
        else if(e.ctrlKey && e.keyCode == 89){
            // リドゥの処理
            isProcessed = true;
        }
        if(e.keyCode == 229){
            // IE,Chromeのみ
            // IME入力中の処理 キャンセルしたい場合はここ
            return false;
        }
    },
    
    "compositionstart" : function(){
        // IME入力開始時の処理
        isProcessed = true;
    },
    
    "compositionupdate" : function(){
        // IME入力中の処理
        isProcessed = true;
    },
    
    "compositionend" : function(){
        // IME確定時の処理
        isProcessed = true;
    },
    
    "paste" : function(e) {
    	// ペーストの処理
        isProcessed = true;
    },
    
    "cut" : function(e) {
    	// カットの処理
        isProcessed = true;
    },
    
    "drop" : function(e) {
        // ドロップの処理
        isProcessed = true;
    },

    "input" : function(e) {
        if(!isProcessed){
            // 右クリックメニュー アンドゥ・リドゥの処理
        }
        else{
            isProcessed = false;
        }
    }
});
  • keypress, keydown, paste, cut, dropイベントはキャンセル可能
  • CompositionEventはキャンセル不可
  • DOM Level 3 仕様書ではキャンセル可能と定義されていますが、ブラウザ固有の事情でキャンセルできないケースがあるらしい。少なくともChrome、Firefox、IE11ではキャンセルできないことを確認しました
  • IE,Chromeの場合は、keydown時にkeyCodeに229が渡されていればIME入力中と判別でき、キャンセルが可能
  • FirefoxではIME入力中はKeyEventが発火しないので、どうあがいてもキャンセルできないみたい
  • Firefoxはアンドゥリドゥの場合でもkeypressが発火する
  • Chromeはアンドゥリドゥをしたとき戻すものがなかったらkeypressが発火する
  • IEではどの場合でも発火しない
  • 上記2点の挙動があるので、keypressではctrl, altを伴う入力はスルーする
  • compositionendは、IME入力中のフォーカスアウトや 変換→エンターを押さずに入力 も拾ってくれる
  • 右クリックメニューからのアンドゥリドゥはkeypressでは拾えない。inputイベントはkeypress, keydown, compositionupdate, paste, cut, dropよりも後に発火するので、処理済みだったら何もしないようにすれば、inputでアンドゥリドゥが拾える。ただしアンドゥリドゥの区別はできない。
  • クリップボードが空でペーストするものがなかったとき
    • IEはpasteが発火しない
    • Chrome,Firefoxはpasteが発火する
  • テキストが選択されておらず、カットするものがなかったとき
    • IEはcutが発火しない
    • Chrome,Firefoxはcutが発火する

###CompositionEventが使えない場合###

CompositionEventさえ使えれば結構シンプルだけど、まだサポートしていないブラウザも多い。

IE,Chromeの場合は、上記のようにkeydownで拾える。
Firefoxでは、inputでIME入力中かどうかを表すプロパティを取得できる

    "input" : function(e){
        if(!isProcessed){
            if(e.originalEvent.isComposing){
                // IME入力中の処理
            }
            else{
                // 右クリックメニュー アンドゥ・リドゥの処理
            }
        }
        else{
            isProcessed = false;
        }
    }

###Paste/Cut/DropEventが使えない場合###

ペーストカットはアンドゥリドゥと同じようにkeydownで拾う。
右クリックメニューからのペーストカットも同じようになるので、区別できないけどinputで拾うしかない。
ドロップは、他のwindowからドラッグしてきた場合mouseupイベントが発火しないので、inputでしか拾えない。

###InputEventが使えない場合###

レガシーなブラウザではinputが使えない場合もある。
KeyEventで入力を検出する。

    // IE,Chromeの場合
    "keydown" : function(e) {
        if(e.keyCode == 229){
            // IME入力中の処理
        }
    }

    // Firefoxの場合
    var isComposing = false;
    "keydown" : function(e) {
        if(e.keyCode == 0){
            isComposing = true;
        }
    }
    "keyup" : function(e) {
        if(e.keyCode == 13 && isComposing){
            // IME確定時の処理
            isComposing = false;
        }
    }
    "change" : function(e) {
        if(isComposing){
            // IME確定時の処理
            isComposing = false;
        }
    }
  • 上記の通り、IE,ChromeではkeyCodeが229の時はIME入力中
  • FirefoxではIME入力中はKeyEventが発火しないので、確定時に処理をするしかない
    • keydown時にkeyCodeが0だったらIME入力開始
    • keyupでEnterが押された かつ IME入力中の場合、確定と判定する
    • IME入力中にフォーカスアウトした場合を想定して、changeにも処理を入れる
    • 変換→エンターを押さずに入力 は拾えないかも・・・

##あとがき##
右クリックメニューからの操作を拾うイベントがほしい。
あとアンドゥリドゥも。

##参考##
jQueryでIME入力確定時にイベントを発行する - Qiita

67
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
67
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?