【jQuery】真・入力欄にフォーカスすると消えるヒントを表示する方法

まずは、こちら↓をご覧下さい。

SE時代にWeb系の試験をしていて、「~を入力して下さい」ように「ヒント」として初期値を表示しておいてフォーカスが当てられると初期値を消去するフォーム部分にてバグが発覚したことがあります。
当時は「仕様です」という言葉でクローズされたのですが・・・。

今回は「仕様です」と言わずに済むスマートな実現方法を紹介します。

時間のない方は実現方法へ


なにがいけないのか

実現方法を説明する前に、一般的な方法の問題点を説明します。

  1. バグがある
  2. プログラムが冗長になる
  3. 任意の初期値が使えない

1. バグがある

次のテキストボックスに、初期値と異なる文字列を入力してみて下さい。


別の場所をクリックするなどしてフォーカスアウトすると、「入力済みです」という判定が表示されたと思います。
次に、初期値と同じ「ほげほげ」という文字列を入力した後、フォーカスアウトとフォーカスを繰り返してみて下さい。

すると、「未入力です」という判定が表示されたと思います。
さらに、入力したはずの「ほげほげ」が消えてしまいます。

お気づきかと思いますが、「初期値」を入力すると、「未入力」と判定されてしまうというバグがあるのです。
「初期値」と同じなら「未入力」だと判断して表示を消す方法なので、当たり前です。

・HTML

1
2
<input type="text" id="sample2" value="ほげほげ" />
<span id="sample2_message" style="color:red"></span>

・Javascript (jQuery)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(function() {
  var sample2 = $('#sample2');
  var sample2_default = sample2.val();
  var sample2_message = $('#sample2_message');
  sample2.focus(function() {
    if (sample2.val() === sample2_default) sample2.val('');
  }).blur(function() {
    if (sample2.val() === '') {
      sample2.val(sample2_default);
      sample2_message.text('←判定:未入力です');
    } else {
      sample2_message.text('←判定:入力済みです');
    }
  });
});

初期値と同じ内容を入力するようなシチュエーションはほぼ無いでしょうが、あまりスッキリしないと思いませんでしょうか。

プログラムが冗長になる

前述のとおり、一般的な方法は「初期値」と同じなら「未入力」だと判断して表示を消すというものです。
これは、サーバ側にデータを受け渡す際にも同じことが言えます。
上記の例であったならば、サーバは受信したフォームデータが未入力であるかのチェックのために「data == “ほげほげ”」という判定をしなければなりません。
クライアントサイドで送信前に判定するという方法もありますが、それでも各フォームごとに比較をする必要があり、冗長なプログラムになってしまいます。

任意の初期値が使えない

「初期値」と同じなら「未入力」だと判断して表示を消すがために、プログラム側で任意の文字列を初期値として使うことができなくなってしまいます。
例えば、ユーザ名の入力欄に、前回入力したものと同じユーザ名を表示しておく、というようなことができなくなってしまいます。

以上のように、初期値を利用した一般的なヒント表示方法では、「ちょっとずつイヤな問題」が拭いきれません。


真・実現方法

前述のバグなしに、よりスマートにヒントを表示するために、HTMLの要素「label」を使用します。
HTML, CSS, jQuery でカンタンにできてしまいます!

・HTML

1
2
3
4
<div class="textfield_wrap">
  <label for="sample1" id="sample1_label">名前を入力して下さい</label>
  <input type="text" id="sample1" />
</div>

「sample1」というIDが割り当てられた入力欄に対してlabelを設置します。
このlabelをヒントとして利用するので、ヒントの文言「名前を入力して下さい」を記述します。
さらに、「textfield_wrap」というクラスのdivでlabelと入力欄を囲みます。

・CSS

1
2
3
4
5
6
7
8
.textfield_wrap {
  position: relative;
}
.textfield_wrap label {
  position: absolute;
  padding: 10px 6px;
  color: #888;
}

CSSのポイントは次の2点です。

  1. 入力欄とlabelを囲んだdiv(textfield_wrap)のposition指定をstatic以外にする
  2. labelのposition指定をabsoluteにする

・Javascript (jQuery)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(function() {
  var sample1 = $('#sample1');
  var sample1_label = $('#sample1_label');
 
  // フォーカスイベントハンドラ
  sample1.focus(function() {
    if (sample1_label.is(':visible')) sample1_label.fadeOut(200);
  })
 
  // フォーカスアウトイベントハンドラ
  sample1.blur(function() {
    if ($.trim(sample1.val()) === '') sample1_label.fadeIn(200);
  });
});

jQueryのコードは見ての通りシンプルです。
フォーカスイベントハンドラは、「ラベルが表示されてたら非表示にする」
フォーカスアウトイベントハンドラは、「なにも入力されていなければラベルを表示する」

デモ↓

— 追記:2012/2/21 —

以下は、入力欄が複数あっても対応できる汎用版です。

・HTML

1
2
3
4
5
6
7
8
<div class="textfield_wrap">
  <label for="username">お名前を入力して下さい</label>
  <input type="text" name="username" id="username" />
</div>
<div class="textfield_wrap">
  <label for="password">パスワードを入力して下さい</label>
  <input type="password" name="password" id="password" />
</div>

・CSS (同じ)

1
2
3
4
5
6
7
8
.textfield_wrap {
  position: relative;
}
.textfield_wrap label {
  position: absolute;
  padding: 10px 6px;
  color: #888;
}

・Javascript (jQuery)

1
2
3
4
5
6
7
8
9
10
$(function(){
  var slct_textfield_wrap = 'div.textfield_wrap';
  $(slct_textfield_wrap + ' > input').focus(function(){
    var label = $(this).closest(slct_textfield_wrap).children('label');
    if (label.is(':visible')) label.fadeOut(200);
  }).blur(function() {
    if ($(this).val().length == 0)
      $(this).closest(slct_textfield_wrap).children('label').fadeIn(200);
  });
});

以上です。

Posted in: Javascript, Web