with the flow

WEBプログラマを目指すWEBデザイナーが書き綴る開発日誌のようなもの

input type="file"をCSSで装飾(複数input対応版)。

【追記】よりコンパクトに、デザインも改善した新しいものとして作り直しました。
http://me.hateblo.jp/entry/20121226/1356479631



せっかくブログ作ったので、
趣味でやってるhtmlやCSSphp関連のものを
書いていきます。

まずは表題のもの。
やってみたらあっさり解決できてしまったので。

  • やりたいこと
  1. ブラウザ間で表示が全然違うことで有名なinput type="file"をIE7〜とモダンブラウザで統一し、「参照」ボタンへのロールオーバーも実現する。
  2. 1の実現には疑似input(text)を作成するが、この疑似input内に、参照が完了(change)したタイミングで参照ファイル名を入れる。
  3. 1ページ内に何度かinput fileが現れることを前提として、コンフリクトしないようにする。
  4. xhtmlcssでできないところだけにjQueryを使用する。(画像の名前とか変更要素があった時、いちいちjs開いて探すのが手間なので)
  • きっかけ

若干古いですが以下を見て。
CSS2/DOM - Styling an input type="file"

透明にした本物のファイル参照inputを、装飾した疑似inputの上にopacity:0で重ねて
クリックしてるように見せる方法。考えるなーと思った反面、
これって、装飾用inputとかはJavaScriptで作っていて、
validじゃないのが残念だったのと(閉じタグ無いとか,divの多用とか)、
参照ボタンがマウスオーバーに対応してないという部分がもやもやした。
ネスケとか古いブラウザも含めて対象にしているのでそうしてるんだろうけど。。。

ということで、以下のようにします。

  • html
<form action="hogehoge.php" id="hogeForm" name="hogeForm">
<p class="fileinputs">
    <input type="file" class="file" size="20" id="hogeID" name="hogeName" />
    <span class="fakefile">
        <input type="text" class="fakeInput" />
        <img src="images/common/btn_select-files.png" alt="参照..." />
    </span>
</p>
</form>

※fakeInputへの装飾は書いてません。背景画像で置き換えるなりおすきにどうぞ。

.fileinputs {
    clear: both;
    float: left;
    width: 100%;
    margin:  0 0 15px 0;
    position: relative;
}
.fakefile {
    display: block;
    position: absolute;
    top: 0px;
    left: 0px;
    z-index: 1;
}
input.file {
    position: relative;
    text-align: right;
    -moz-opacity:0 ;
    filter:alpha(opacity: 0);
    opacity: 0;
    z-index: 2;
    cursor: pointer;/*IEでのみ有効*/
}
$(function(){
    //擬似inputに参照したファイルの名前を入れる
    $("#hogeForm").find(".file")
      .change(function(){
        $('.fakefile input',$(this).parent()).val($(this).val());
      })
      .mouseover(function(){
        $fakeFileBtn = $('.fakefile img',$(this).parent());
        $fakeFileBtn.attr("src",$fakeFileBtn.attr("src").replace(/^(.+)(\.[a-z]+)$/, "$1_on$2"))
      })
      .mouseout(function(){
        $fakeFileBtn.attr("src",$fakeFileBtn.attr("src").replace(/^(.+)_on(\.[a-z]+)$/, "$1$2"));
        })
});
  • 以上です。以下説明など。

まあなんてことはない、単にvalue値入れ替えとロールオーバーをやってるだけ。
でもjQueryで書くとこんなに短くなっちゃう不思議。

画像"btn_select-files.png"は当然"btn_select-files_on.png"も必要。
正規表現使って画像を探しにいってます。
また、できるだけはじめからxhtml内に必要なコードは書いてしまい、
必要なものだけjQueryで補っています。

"cursor: pointer"はIEでだけ有効。FFはデフォルト矢印のまま。。。
解決法わかる方はぜひツッコミを。
.fileを透明にして.fakefileの上に重ねることで、擬似的に装飾されてるように
見せてるだけなので、.fakeの領域から.fakefileがはみ出さないよう注意。

  • 今後の課題:

・IEは透明にした.fileinput部分へのフォーカスと名前の変更ができてしまう。でも値を表示してるのはz-indexでその下にあるinputだから、本物の値は書き換えられているのに目に見える部分の表示はかわっていないように見えてしまう。このやろう。。。(泣
まあ参照変えたい人はまた参照ボタン押すはずだからいいよねという妥協で自分を納得させた。
→【追記】コメントでhikeさんにcontenteditable=falseとreadonly属性で直接編集は回避できると教えていただきました★

Chromeだと値を参照した際にパスがC:〜〜とならない(C:fakepathとかってなる。セキュリティの対策かな?)。


※コードの使用は自由ですが責任は持てませんのであしからず^^;


【追記】よりコンパクトに、デザインも改善した新しいものとして作り直しました。
http://me.hateblo.jp/entry/20121226/1356479631