inline-block を使ってプレースホルダ付き入力欄を実装する

更新履歴

2010-01-22
jQuery.exPlaceHolder.js を Ver 0.1.1 に更新
  • title 属性(あるいは任意の属性)で、表示するメッセージを指定できるようにしました
  • 専用の css ファイルの読み込みを不要にしました
  • jQuery.exInlineBlock.js ファイルの読み込みを不要にしました
  • 初期値があった場合もメッセージが表示されてしまう不具合を修正しました
  • 記事に使い方を追記しました

まずプレースホルダ付き入力欄のおさらいですが、



こういうやつの事をいいます。入力欄にカーソルがあたるとナビゲーションメッセージが消え、カーソルがはずれるとまた表示されるやつです。

HTML5 では

<input placeholder="google">


と記述すると自動的にこういう制御をしてくれるそうです。ですが HTML5 の普及はまだ先の話なので JavaScript でこれを実装しようという記事がいろいろとあります。

実装方法の問題点


プレースフォルダの実装方法としては input 要素の value 属性にメッセージをセットしておき、フォーカスイン/アウトのタイミングで、value 属性のクリア/再セットをするというのが一般的なようです。
但し、この方法だと他のロジックで入力フィールドのデータチェックなどを行っていた場合、プレースフォルダの値を意識する必要があり、保守性を考慮すると入力メインな画面などで使用するにはあまり現実的と言えません。
そこで value 属性を汚さずに input 要素に重ねる合わせるように (position:absoluteを使って)メッセージを表示させる実装を試してみました。


この実装の場合 HTML の構成は以下のようになります。

ところが textare に対しこれを実行すると FirefoxSafari の場合のみメッセージの表示位置がずれます。



やはり素の inline 要素(span)の中に textarea が存在してるのがまずいようです。コンテナを div にすれば問題は無いのでしょうが、後続要素に影響がでてしまいます。

この時のエントリでは仕方がないので、FirefoxSafari の場合のみ、ずれる量の規則性からメッセージの配置位置を調整するといった対応をとりましたが、気持ち悪さが残りました。

コンテナ要素を inline-block にする


前回のエントリで inline-block 化する jQuery プラグインを作りましたが、これをコンテナ要素に適用します。inline-block なら縦幅、横幅ともに縮む特性があり、inline 要素と同様に横並びに配置されます。またブロック要素としての性質もあり内包してる要素の高さを自身で持つことができるので、このようなケースでの利用は最適かと思います。

プレースホルダを生成する jQuery プラグインをつくる


コンテナ要素を inline-block にして、プレースホルダを生成するプラグインを作ります。

処理概要

  • 指定要素 (input / textarea) を inline-block な div 要素で wrap する
  • プレースホルダのメッセージを position:absolute で、コンテナ要素内に append する
  • 指定要素 (input / textarea) にフォーカスしたらメッセージを非表示にする
  • 指定要素 (input / textarea) からフォーカスが外れたらメッセージを表示する。但し、値が入力されてた場合は何もしない
  • メッセージ要素がクリックされたら指定要素 (input / textarea) にフォーカスさせる


2010/01/22 追記

こちらのプラグイン(フォーム要素にうっすらデフォルト値を入れてヒント表示できる「jQuery Form Tips」- PHPSPOT)からアイディアを拝借し、input 要素の title 属性(あるいは任意の属性)の内容をメッセージとして表示できるように変更しました。また、以前は CSS ファイル、inline-block 化するプラグインらと分離した記述になってましたが、それぞれ大した内容でないので1つのファイルに統合しました。

ソース

jquery.explaceholder.0.1.1.js

(function($){
    //inline-block 処理
    var ex = {
        inlineBlock : function(targets){
            var ver = parseFloat($.browser.version)
            ,oldIE = $.browser.msie && (ver < 8 || !$.boxModel)
            ,oldMOZ = $.browser.mozilla && ver < 1.9;
            targets.css('display',
                oldIE ? 'inline' : oldMOZ ? '-moz-inline-box' : 'inline-block'
            );
            return targets;
        }
    }
    //place holder 処理
    $.ex = $.ex || {};
    $.ex.placeHolder = function(targets,option){
        var s = $.ex.placeHolder,
        c = $.extend({}, s.defaults,option);
        return targets.each(function(idx){
            var target = targets.eq(idx);   
            //ラベルの生成とスタイルの適用
            var label = $('<div>' + (target.attr(c.labelAttName) || c.label) + '</div>')
                .css({
                    width : target.width(),
                    overflow : 'hidden',
                    position:'absolute',
                    top : c.top + c.topAdjust,
                    left :c.left,
                    color : c.color
                });
                !c.css || label.css(c.css);
            //inline-block の適用
            ex.inlineBlock(
                target.wrap('<div style="position:relative"/>').parent().append(label)
            );
            //ラベルの表示・非表示イベントの割り当て
            target.bind('focus blur',function(evt){
                s.display(target,label,evt.type == 'focus');
            }),
            label.click(function(){target.focus()})
            s.display(target,label);
        });
    };
    $.ex.placeHolder.display = function(target,label,focus){
        label[!focus && target.val() == '' ? 'show' : 'hide']();
    }
    $.ex.placeHolder.defaults = {
        labelAttName : 'title', //タイトル属性の内容がラベルになる
        label : null, //パラメータによるラベルメッセージの指定
        top : 3, //ラベルの垂直位置調整
        left : 3, // ラベルの水平位置調整
        topAdjust : - ($.browser.opera ? 2 : 0), //表示位置の微調整
        color : '#a0a0a0', //ラベルのカラー
        css : null //ラベルの一括 style 指定
    }
    $.fn.exPlaceHolder = function(option){
        return $.ex.placeHolder(this,option);
    };
})(jQuery);
使い方

input または textarea の title 属性に表示したいメッセージを記述しておきます。

<input title="入力してください ..."/>
<textarea title="書いてください ..."></textarea>

jquery.js 、jquery.explaceholder.js の順で読み、プレースフォルダを適用したい要素に対し exPlaceHolder() メソッドを実行します。

<script src="jquery.js"></script>
<script src="jquery.explaceholder.js"></script>
<script>
jQuery(function($){
    $('input,textarea').exPlaceHolder();
});
</script>

Demo


title 属性を使用したくない場合は、labelAttName パラメータに使用したい属性名を指定します。例えば以下のように記述すると "hoge" 属性に記述された内容 "fuga" が表示されます。

<input hoge="fuga"/>
$('input').exPlaceHolder({ labelAttName:'hoge' });


HTML の属性を使用したくない場合は、label パラメータで表示したいメッセージを指定します。

$('#word').exPlaceHolder({ label:'search word' });


メッセージは HTML 形式で指定することもできます。

$('textarea').exPlaceHolder({ label:'<span style="font-size:2em">こ</span>ちらの方へ<br/>書いてくださいませ ...' });
<textarea title="<span style='font-size:2em'>こ</span>ちらの方へ<br/>書いてくださいませ ..."></textarea>

Demo


メッセージの色を指定したい場合は、color パラメータを指定します。

$('input').exPlaceHolder({ color:'#ff0000' });


input 要素の border を極端に太くしてる場合などは、メッセージが border にかぶってしまう可能性があります。そのような場合は top , left パラメータでメッセージの表示位置を調整します。

$('input').exPlaceHolder({ top:10 , left:10 });


その他もろもろスタイルを一括指定したい場合は、css パラメータで指定します。

$('input').exPlaceHolder({
    css : {
        'font-family' : 'sans-serif',
        'font-weight' : 'bold',
        'font-style' : 'italic'
    }
});
ダウンロード

こちらからどうぞ