prototype.jsを読み解く

第10回 Prototypeライブラリ(2846~3276行目)

この記事を読むのに必要な時間:およそ 15 分

Abstract.EventObserver オブジェクト

2888: Abstract.EventObserver = function() {}
2889: Abstract.EventObserver.prototype = {
2890:   initialize: function(element, callback) {
2891:     this.element  = $(element);
2892:     this.callback = callback;
2893: 
2894:     this.lastValue = this.getValue();
2895:     if (this.element.tagName.toLowerCase() == 'form')
2896:       this.registerFormCallbacks();
2897:     else
2898:       this.registerCallback(this.element);
2899:   },
2900: 

2888行目からはAbstract.EventObserverです。構成はAbstract.TimedObserverとよく似ていて,initialize()でコンストラクタは定義しつつ,このクラス自体はClass.create()は使っていません。後述するForm.Element.EventObserverとForm.EventObserverから使われます。

コンストラクタでは,同様にパラメータをインスタンス変数に格納し,現在の値をgetValue()で取得して保存しています。その後,<form>タグの場合はregisterFormCallbacks()を呼び,それ以外ならregisterCallback()を呼んで各入力要素にイベントハンドラを設定しています。

個人的にはここはif文で分けるのではなく,ひとつメソッドを定義して派生クラス側でやることを分ける方がすっきりするのではないか,と思います。

2901:   onElementEvent: function() {
2902:     var value = this.getValue();
2903:     if (this.lastValue != value) {
2904:       this.callback(this.element, value);
2905:       this.lastValue = value;
2906:     }
2907:   },
2908: 

2901行目からはonElementEvent()です。

これは各要素の'click'/'change'イベントハンドラとして実際に登録される関数で,現在のフォーム/要素の値をgetValue()で取得し,前回と比較して変わっていればコールバック関数を呼び出す,という形になっています。

Abstract.TimedObserver.onTimerEventでも同じような処理をしていますが,こちらの方がシンプルな条件判断になっています。getValue()がnullを返しうるのは変わらないので,両方揃えたほうが良さそうですが,今のところはこのようなコードになっています。

2909:   registerFormCallbacks: function() {
2910:     Form.getElements(this.element).each(this.registerCallback.bind(this));
2911:   },
2912: 

2909行目では,Form.EventObserverの時に使われるregisterFormCallbacks()メソッドです。

Form.getElementsでフォーム内の全入力要素を取得し,各々についてregisterCallback()を呼び出してイベントハンドラを設定しています。

2913:   registerCallback: function(element) {
2914:     if (element.type) {
2915:       switch (element.type.toLowerCase()) {
2916:         case 'checkbox':
2917:         case 'radio':
2918:           Event.observe(element, 'click', this.onElementEvent.bind(this));
2919:           break;
2920:         default:
2921:           Event.observe(element, 'change', this.onElementEvent.bind(this));
2922:           break;
2923:       }
2924:     }
2925:   }
2926: }
2927: 

2913行目からはregisterCallback()です。要素に対して必要なイベントハンドラを設定します。

type='checkbox', type='radio'の時だけonClickイベントを対象にし,それ以外はonChangeイベントを見張っています。お決まりのthis.メソッド名.bind(this) を使って関数を渡しています。

Form.Element.EventObserver クラス

2928: Form.Element.EventObserver = Class.create();
2929: Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2930:   getValue: function() {
2931:     return Form.Element.getValue(this.element);
2932:   }
2933: });
2934: 

2928行目からはForm.Element.EventObserverクラスです。Insertion.Beforeなどでも使っている形で,Abstract.* オブジェクトをnewして新たにメモリ上に確保したオブジェクトに対してObject.extend()を適用しています。

Form.Element.TimedObserverのEventObserver版で,特に変わったところはありません。TimedObserver, EventObserverで同じようなクラス定義をしているので,もうちょっと冗長性を無くした書き方ができるかもしれません。

Form.EventObserver クラス

2935: Form.EventObserver = Class.create();
2936: Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2937:   getValue: function() {
2938:     return Form.serialize(this.element);
2939:   }
2940: });

Form.EventObserverです。こちらもTimedObserverの時と同様の拡張を行っています。

著者プロフィール

栗山淳(くりやまじゅん)

S2ファクトリー株式会社株式会社イメージソース所属。
本業はWeb制作会社の裏方。得意分野はFreeBSDやPerlのはずだが,必要に迫られるとHTML/CSSやJavaScriptも書く。

コメント

コメントの記入