JavaScript でプレースホルダつき入力欄を作る (3)

2009-03-08 09:25

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

Placeholder

ユーザーが入力するべきものを入力欄自身に表示するこのインターフェース、調べたら 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なしで検索することができないようになってるんですね。

KATO Kazuyoshi

たしかにそうだ。もともとここのために書いたのでそこらへん抜けてますね。

あとで、来週までには直します。

KATO Kazuyoshi

再来週になってしまいましたが、続きを書きました。

http://blog.8-p.info/2009/03/textfield-js-2

Leave a Reply