button要素で画像ボタンを作る

フォームのボタンを画像で表現したい場合、input要素のtype属性をimageにしますが、button要素を使うこともできます。src属性とalt属性を使うinput要素と異なり、button要素は

<button type="submit"><img src="btn.png" alt="送信" /></button>

のように要素の内容を持てるので、画像とテキストを両方含められる、JavaScriptで動作するボタン(type="button")も画像で表現できるなど自由度が高いです。

ただし、CSSでのスタイル当てにはいくつか注意しなくてはいけないことがあります。また、input要素による画像ボタンは HTML 2.0(RFC1866) で既に定義されていたのに対し、button要素は HTML 4.0 で登場したこともあり、古いブラウザ(IE6, 7)での対応状況が芳しくないので解決法を探ってみました。

リセットスタイルを書く

§

デフォルト状態だとpaddingborderが設定されていますが、文字だけのボタンはともかく画像ボタンでは不要なケースが多いのでリセットします。

button {
  padding: 0;
  border: none;
  background: transparent;
}

button img {
  display: block;
}

img要素に display: block; しているのは画像の下に空きができるのを防ぐためです。button要素に対して line-height: 0; でも空きは消えますが、代替テキストが表示される場合(画像が読み込まれない場合)のことを考えると好ましくありません。一方でボタン内に画像とテキストを両方含む場合などは改行されてしまうので、別の方法をとる必要があります。

また、 Internet Explorer と Opera はボタンを押下したときに数ピクセル右下に動きます。いかにもボタンを"押した"感がありますが、もし好ましくない場合は :active 疑似クラスを使って無効化します。

button:active,
button img {
  position: relative;
}
  • button要素の中身(img要素)にも指定しているのはIE8以下のためです。サポート対象がIE9以上の場合は button:active のみで大丈夫です。

Firefoxの ::-moz-focus-inner

§

上記のリセットスタイルだけでは、Firefoxのみ依然空きが残ります。

サムネイル画像
Firefoxにおける画像ボタンのbuttoninput[type="image"]での描画比較(分かりやすいように赤背景を設定)オリジナル画像

これは ::-moz-focus-inner という疑似要素が設定されているためで、MDNにはこんなことが書いてあります。

Firefox はアクセシビリティの観点から、フォーカスされたボタンに小さな点線ボーダーを表示するようになっています。このボーダーはブラウザのデフォルト・スタイルシートで宣言されています。これは次の様なセレクタを用い、あなた自身のスタイルで上書きする事が出来ます。

button::-moz-focus-inner { }

button 要素 - HTML | MDN(developer.mozilla.org)

ということで、スタイルを追加します。

button::-moz-focus-inner {
  padding: 0;
  border: none;
}

しかし、こうするとキーボード操作でボタンにフォーカスを合わせたときに何の変化も起きないので、ユーザーは今どこにフォーカスがあるのか分からなくなってしまいます。MDNでも念を押すようにアクセシビリティに配慮したスタイリングを心がけて下さい。(developer.mozilla.org)と言っていることですし、何らかのスタイルを当てた方が良いでしょう。例えばアウトラインを出すにはこうします。

button:moz-focusring {
  outline: 1px dotted #000;
}

2017年5月1日追記ここのサンプルは button:focus としていたのですが、 Chrome などではデフォルトで青い実線アウトラインになるところ、この指定によってボタンだけアウトラインのスタイルが変わってしまうため、 Firefox でのみ適用される:-moz-focusring 疑似クラス(developer.mozilla.org)へ変更しました。

Internet Explorer 6, 7 の挙動

§

button要素内側の空きを消す

§

IE7でもボタンの内側に空きが残ります。

サムネイル画像
IE7における画像ボタンのbuttoninput[type="image"]での描画比較(分かりやすいように赤背景を設定)オリジナル画像

これはbutton要素に画像と同じ幅と高さを指定すれば消えました。

button {
  padding: 0;
  border: none;
  *width: 44px; /* for IE6-7 */
  *height: 50px; /* for IE6-7 */
  background: transparent;
}

ただしこうすると、画像が表示されない場合に代替テキストが途中で切れてしまう恐れがあります。あまり好ましくないので、IE8や他のブラウザでは無視されるような指定をした方が良いでしょう。この例ではいわゆるアスタリスクハックをしています。

type属性のデフォルト値はbutton

§

type属性には submit, button, reset の3種類を指定することができますが、type属性を指定しなかった場合のデフォルト値は submit です。なので仕様上、送信ボタンの場合はtype属性を省略できることになりますが、IE7以下はbuttonと判定されてしまい、押しても何も起こりません。MSDNには以下の記述があります。

In IE8 Standards mode, the default value is submit. In other compatibility modes and earlier versions of Windows Internet Explorer, the default value is button.

button element | button object (Internet Explorer)(msdn.microsoft.com)

これの対処は簡単で、type属性を省略しなければ問題ありません。

ボタンに名前を付けた場合

§

送信ボタンには名前と初期値を設定することができます。input要素による画像ボタンではvalue属性を指定することができないので、フォーム内に複数のボタンがあるときにどのボタンが押されたかを名前でなく値で判別したい場合など、button要素の採用が大きなメリットとなります。

ところが、IE7以下はvalue属性値でなくbutton要素の中身をテキストとして送信してしまいます。つまりこんなHTMLの場合、

<button type="submit" name="btn" value="1"><img src="btn.png" alt="送信" /></button>

本来なら btn=1 が送られるのが正しいのですが、IE7以下は btn=<IMG alt=送信 src="btn.png"> となってしまいます。

  • 実際にはこれをURLエンコードした値となります。

これもMSDNに記載がありますね。

When the BUTTON element is submitted in a form, the value depends on the current document compatibility mode. In IE8 mode, the value attribute is submitted. In other document modes and earlier versions of Internet Explorer, the innerText value is submitted.

button element | button object (Internet Explorer)(msdn.microsoft.com)

IE6は複数ボタンを置けない

§

さらにIE6は、フォーム内に複数の送信ボタンがあった場合に、名前の付いたすべてのbutton要素の値を送信してしまうというバグが存在します。これではサーバー側で判別することができません。ひどいですね…。


結論としては、フォーム内に複数の送信ボタンが必要なケースでは、button要素を使うのは時期尚早と言えます。レイアウトが崩れるといった些細な問題ではなく、正しい送信処理ができないことにもなるので、IE6が絶滅するまで(少なくとも Microsoft が Windows XP の延長サポートを終了する来年4月まで)は一般のウェブサイトでは採用すべきでないでしょう。また、IE7でも値(value)での判別ができないなどの制約があります。

一方で送信ボタンがひとつの場合や type="button" なボタンで使用する分には、送信ボタンであってもtype属性を省略しないこと、またFirefoxの ::-moz-focus-inner に対する対処方法などを知っていれば使用に大きな問題はないと思います。