JavaScript でプレースホルダつき入力欄を作る (3)
JavaScript で input 要素にプレースホルダをつけるようにして、このブログの右上につけてみた。

ユーザーが入力するべきものを入力欄自身に表示するこのインターフェース、調べたら Input Prompt と呼ぶ向きもあるらしい。
ここではその実装と設計 (というほどおおげさなものじゃないけど) について、制作過程の細々とした考えを文章化してみます。
インターフェースを決める
どこかで見たことある部品なので、入力欄がページの読者に提供するインターフェースは、もうかたまっている。
- 入力前は説明文を、灰色かなにかで表示する
- フォーカスがあたると説明文が消える
- 入力後はそのまま、なにも入力されていなければ説明文が再び表示される
ページの作者に提供するインターフェースはこうした。
var field = new TextField($('searchKeyword'));
field.setPlaceholder('Search');
「説明文」を setPlaceholder で設定する。また、説明文を表示しているときは input 要素に placeholder クラスをあてて、説明文の色は CSS 経由で設定してもらうことにする。
細かいところだと、TextField のコンストラクタにはページ上の要素そのものを渡すようにしている。Google Maps の GMap2 と同じだ。たまに、SWFObject の swfobject.registerObject のような、文字列で ID 属性を渡すことを要求するものがあるけど、これをされると動的に生成した要素を引数にあたえづらい。
var input = document.createElement('input');
var field = new TextField(input);
特別な事情がない場合は、要素そのものがとれるようにするべきだと思う。
ライブラリを選ぶ
JavaScript は様々なブラウザ間の差異とか、よくやる処理に対して便利なショートカットがないとか、面倒なところがいくつかある。この面倒は、Prototype, MochiKit, jQuery など既存のライブラリでだいたい回避できる。自分でゼロから書いて面倒に対処するのも勉強にはなるけど、勉強がすんだ後はやっぱり有名なライブラリにのったほうが、あとから読む人に優しい。
最近は jQuery が流行っているけど、私は Prototype のほうが好きだ。今回も Prototype を使った。
InfoQ の Prototype vs. jQuery で紹介されている Why I still prefer Prototype to jQuery では、コメント欄に MooTools を推すひとがちらほらいた。
プライベート
プライベートな変数・メソッドは JavaScript でも区別するべきなので、いくつかの変数・メソッドには名前の先頭に “_” をつけている。クロージャをつかって外から呼び出すことを本気で防止するとか、そういうことはしていない。設計の意図をしめす意味での、紳士協定としての区別があれば十分だと思う。
ソースコード
最後にソースコードをはっておきます。著作権とかは主張しないので、自由につかってください。
var TextField = Class.create({
initialize: function (element) {
this._element = $(element);
this._placeholder = '';
this._changed = this._element.value != '';
this._element.observe('focus', this._onfocus.bindAsEventListener(this));
this._element.observe('blur', this._onblur.bindAsEventListener(this));
this._element.observe('change', function () {
this._changed = true;
}.bindAsEventListener(this));
Event.observe(window, 'unload', this._unload.bindAsEventListener(this));
},
_onfocus: function () {
if (! this._changed) {
this._element.value = '';
}
this._showPlaceholder(false);
},
_onblur: function () {
if (this._element.value == '') {
this._changed = false;
}
this._showPlaceholder(true);
},
_unload: function () {
if (! this._changed) {
this._element.value = '';
}
},
setPlaceholder: function (str) {
this._placeholder = str;
this._showPlaceholder(true);
},
_showPlaceholder: function (flag) {
if (flag && ! this._changed) {
this._element.value = this._placeholder;
this._element.addClassName('placeholder');
} else {
this._element.removeClassName('placeholder');
}
}
});
まとめ
とりとめないけど、今回主張したことを並べてみた。
- 引数は ID 属性ではなく、要素そのものを渡せるようにしよう
- 既存のライブラリを使おう
- プライベートは (がんばって呼べなくすることまでやらなくても) 区別はしよう
JavaScript はいわれるほど変な言語ではない。今回のソースコードも (Prototype の Class.create のおかげもあって) 他言語ができれば JavaScript が読めなくてもわかるレベルにおさまっていると思う。
ふつうにやりましょう。
追記
Safari で遷移後に戻ったときにプレースホルダが残ってしまう問題があったため、unload まわりのコードを追加しました。こういうのが続くとふつう説がゆらぐのでなんとかしないと。ええと、WebKit はフォームの状態を保存するのです。
あれっすよ、kzys
これを本気でやるなら、親をたどってform要素のonsubmitもフックしないといけなくなるんですけど
このブログのUIだとfocusなしで検索することができないようになってるんですね。
たしかにそうだ。もともとここのために書いたのでそこらへん抜けてますね。
あとで、来週までには直します。
再来週になってしまいましたが、続きを書きました。
http://blog.8-p.info/2009/03/textfield-js-2