HTML5 の placeholder 属性を jQuery でクロスブラウザに

2011-09-23更新: 一部を修正して Gist に置きました


フォームの入力フィールドにあらかじめテキストを表示させておく「プレースホルダ」。ブラウザの検索バーなんかに見られるような、フィールドが空のときは薄く文字が表示されていて、フォーカスすると消えるあれです。このプレースホルダの機能、HTML5 では input 要素や textarea 要素の placeholder 属性で簡単に実現できます。

<label>お名前 <input type="text" name="fn" placeholder="山田 太郎"></label>

しかしながら、この placeholder 属性をサポートしているのは今のところ Safari と Chrome のみ。そのほかのブラウザでは無視されてしまいます。そこで、この placeholder 属性をクロスブラウザで扱えるようにする jQuery スクリプトを書いてみました。実際に動作しているデモ も用意したのでどうぞ。

$(function () {
  var supportsInputAttribute = function (attr) {
    var input = document.createElement('input');
    return attr in input;
  };
  if (!supportsInputAttribute('placeholder')) {
    $('[placeholder]').each(function () {
      var
        input = $(this),
        placeholderText = input.attr('placeholder'),
        placeholderColor = 'GrayText',
        defaultColor = input.css('color');
      input.
        focus(function () {
          if (input.val() === placeholderText) {
            input.val('').css('color', defaultColor);
          }
        }).
        blur(function () {
          if (input.val() === '') {
            input.val(placeholderText).css('color', placeholderColor);
          } else if (input.val() === placeholderText) {
            input.css('color', placeholderColor);
          }
        }).
        blur().
        parents('form').
          submit(function () {
            if (input.val() === placeholderText) {
              input.val('');
            }
          });
    });
  }
});

解説してみます。まずはじめのポイントは、ブラウザが placeholder 属性をサポートしているかどうかを判定している点です。判定には実際にはドキュメントに現れないダミーの input 要素を作り、placeholder プロパティの有無を調べます。その結果、もしブラウザが placeholder をサポートしていればスクリプトは何もしません。以降のスクリプトはサポートしていないブラウザでのみ実行されます。

var supportsInputAttribute = function (attr) {
  var input = document.createElement('input');
  return attr in input; // 引数で指定された属性がサポートされていれば true を返す
};
if (!supportsInputAttribute('placeholder')) {
  // placeholder 属性をサポートしないブラウザでのみ実行
}

placeholder 属性は input 要素だけではなく textarea 要素でも使われるので、セレクタは以下のように placeholder 属性を持つすべての要素を対象とします。

$('[placeholder]').each(function () {
  // placeholder 属性を持つすべての要素で実行
});

次に、対象の各要素の placeholder 属性の値と、プレースホルダの前景色をそれぞれ変数に格納します。ここではプレースホルダの前景色に具体的な色ではなくシステムカラーの GrayText を指定。OS やブラウザの GUI で選択できないテキストやなんかに使われる色ですね。実際の色は環境に依存するわけですが、だいたい #999 とか #aaa あたりになるようです。また読み込み時のフィールドの前景色も保持しておきます。

var
  input = $(this),
  placeholderText = input.attr('placeholder'), // placeholder 属性の値
  placeholderColor = 'GrayText', // プレースホルダの前景色
  defaultColor = input.css('color'); // フィールドの本来の前景色

そして、フィールドがフォーカスを得たときと失ったときにその value を調べ、プレースホルダの表示と非表示を実行します。ページの読み込み時にはフィールドがブラー状態になるようにし、その時点でフィールドが空なら placeholder 属性の値をフィールドにセット、そしてフォーカス時にプレースホルダが表示されていればそれを消去。同時に前景色も切り替えています。最後に、フィールドの value にプレースホルダが入った状態でそのまま送信されないように、フォームのサブミット時にも値をチェックしています。

input.
  focus(function () { // フォーカス時
    if (input.val() === placeholderText) {
      input.val('').css('color', defaultColor);
    }
  }).
  blur(function () { // ブラー時 (フォーカスが外れたとき)
    if (input.val() === '') {
      input.val(placeholderText).css('color', placeholderColor);
    } else if (input.val() === placeholderText) {
      input.css('color', placeholderColor);
    }
  }).
  blur(). // ページ読み込み時にいったんブラー
  parents('form').
    submit(function () { // サブミット時
      if (input.val() === placeholderText) {
        input.val('');
      }
    });

プレースホルダをどのように表現するかについては、新たに span などの要素を生成して CSS でフィールドに重ねるという手法もありますが、ここではフィールドの value にプレースホルダのテキストを突っ込むというアプローチを採りました。そのため、クライアントサイドでフォームをバリデートするようなスクリプトとの共存は難しいかもしれません。

またこのスクリプトの弱点として「placeholder 属性の値と同じ文字列をサブミットできない」という点があります。つまり、たとえば “Lorem ipsum” というプレースホルダが設定されたフィールドに “Lorem ipsum” という文字列を入力しても、それはプレースホルダと見なされてしまい送信できないわけです。これはうまく解決する方法が思いつかなかったので、とりあえずプレースホルダの文言を工夫するしかないです…

ところで、プレースホルダにはどのようなテキストがふさわしいのでしょうか。W3C の仕様では以下のように書かれています:

The placeholder attribute represents a short hint (a word or short phrase) intended to aid the user with data entry. A hint could be a sample value or a brief description of the expected format. The attribute, if specified, must have a value that contains no U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR) characters.

Note: For a longer hint or other advisory text, the title attribute is more appropriate.

The placeholder attribute should not be used as an alternative to a label.

4.10.7.2.11 The placeholder attribute — HTML 5 [W3C Working Draft 4 March 2010]

というわけで、placeholder が示すのは「ユーザのデータ入力を補助するためのちょっとしたヒント」といったところですね。具体的には「こんな感じで入力してくださいね」という記入例のようなものなどでしょうか。また長いヒントや注意書きなどは title 属性のほうがよいでしょう、とされています。ちなみに今回のスクリプトの デモ は上記仕様にあったマークアップ例を使わせてもらってます。

とくに注意したいのが「label 要素の代替として用いられるべきではない」という部分です。たとえば以下のようなマークアップはけっこうやってしまいそうですが、あまりよろしくないですね:

<input type="text" placeholder="お名前">
<input type="email" placeholder="メールアドレス">