atsukanrockのブログ

Microsoft系技術を中心にぼちぼち更新します

テキストボックスのonchangeイベントを信用するべからず

はじめに

JavaScriptで、テキストボックスのonchangeイベントを信用してはならない。本エントリでは、今回私が経験した、期待とは異なる動作を紹介し、対処方法を提案する。

期待動作

これまでの開発経験から私が信じていた、テキストボックス*1のonchangeイベントの仕様は、次のとおり。

  • 発生タイミング:フォーカスを失った時*2
  • 発生条件:テキストボックスの値(valueプロパティ値)が、フォーカスされた時とフォーカスを失った時で異なる

しかし今回、以降で挙げるような、期待とは異なる動作を経験した。

期待しない動作1:オートコンプリートでの入力時に発生しない

確認ブラウザ*3
詳細

T/O。オートコンプリートでの入力時には、onchangeイベントが発生しない。

このあたりMSDN]に記述があるそうだ。参照先ページでは、onpropertychangeMSDN]なるイベントの使用を推奨している。しかしこれは、IE限定だと思われるため、よほどIE限定で良いJavaScript以外では使うべきでないだろう。

期待しない動作2:変更してなくても発生する

確認ブラウザ
  • Win XP SP3上のSafari 4.0.4
詳細

発生タイミングは前述の期待動作のとおりだが、発生条件が異なる。次のように操作すると、onchangeイベントが発生する。フォーカスが外れた時点では、フォーカスされた時点と同じ値なのにも関わらずだ。

前状態として、値が"hoge"のテキストボックスを、ブラウザが表示しているとする。

  1. 当該テキストボックスにフォーカス
  2. 入力。値:"fuga"
  3. 当該テキストボックスにフォーカスしたまま続けて入力。値:"hoge"
  4. 当該テキストボックスからフォーカスを外す

対処方法

上のとおり、onchangeイベントは信用ならない。従って、別の方法を考える。例えば次のような方法が考えられる。

  • 当該テキストボックスのonfocusイベントをハンドル
  • onfocusイベントハンドラ内で、当該テキストボックスの現在値を、変更前の値として、当該テキストボックス自身に記録*4
  • 当該テキストボックスのonblurイベントをハンドル
  • onblurイベントハンドラ内で、当該テキストボックスの現在値と、onfocusイベントハンドラ内で記録した変更前の値を比較し、値が異なったらonchangeイベントの期待動作相当と見なす
サンプルコード(YUI 2を使用)
function init(textBoxId) {
    YAHOO.util.Event.addListener(textBoxId, "focus", function() {
        this._oldValue = this.value;
    });

    YAHOO.util.Event.addListener(textBoxId, "blur", function() {
        if (this.value != this._oldValue) {
            // onchange の期待動作相当
        }
    });
}

*1:input type="text"、およびtextarea

*2:onblurイベントの発生との前後関係は、記憶していない。私はそれに依存するコードを書かないため

*3:あくまで、私が確認したブラウザなことに注意。実際には、他のブラウザでも同様な可能性がある。この注意点は以降同じ

*4:onfocusイベントだけでは不足する可能性がある。例えばJavaScriptのfocusメソッドによるフォーカス時(例:ASP.NETのForm.DefaultFocus)、onfocusイベントが発生しないかもしれない(うろ覚え)