インライン要素に背景画像を指定する

Fig 1: インライン要素に背景画像を指定

CSS でインライン要素に背景画像を指定する場合、IE6 と IE7 では致命的なバグがあるので注意。たとえば Fig 1 のように、パラグラフ中のハイパーリンクにアイコンを表示させたいとする。となると CSS はこんな感じになるだろう:

a.pdf {
  padding-left: 20px;
  background: url(/img/pdf.png) no-repeat 0 50%;
}
a.external {
  padding-right: 20px;
  background: url(/img/external.png) no-repeat 100% 50%;
}

すべてのモダン・ブラウザで Fig 1 のようなレンダリング結果が得られるが、背景画像を指定したインライン要素が改行して複数行にわたる場合、IE6/7 は Fig 2 のようにしくじる。

Fig 2: IE6/7 では改行を含むインライン要素で背景画像の表示位置がずれてしまう

この回避策が IE対策: 改行される可能性のあるインライン要素に背景画像を指定しない。- 我流天性 - がらくた屋 で紹介されている。パディングと背景画像を該当のインライン要素には指定せず、かわりにその内側に仕込んだ空の span 要素に持たせるメソッドだ。

... <a href="..." class="pdf"><span>&nbsp;</span>テキスト</a> ...
a.pdf span {
  padding-left: 20px;
  margin-right: -0.3em;
  background: url(/img/pdf.png) no-repeat 0 50%;
}

空の span といっても、上記のように &nbsp; を入れないとうまくいかない。ちなみに負のマージンはこの余分な &nbsp; を相殺するためのもの。

これで IE6/7 でも Fig 1 のようにレンダリングされる。とはいえこんな無駄なマークアップをいちいち書くのはちょっとうんざりだし、そもそも IE6/7 以外のモダン・ブラウザには不要だし。というわけで、もう少し冴えたやり方を考えてみた。

まず、例によって jQuery の力を借りる:

<script type="text/javascript" src="/js/jquery.js"></script>

次に、JavaScript ファイルに以下のプラグインを用意:

$.fn.extend({
  inlineBgFix: function() {
    if ($.browser.msie && $.browser.version < 8.0) {
      $(this)
        .addClass('inline-bg-fix')
        .prepend('<span class="bg-l">&nbsp;</span>')
        .append('<span class="bg-r">&nbsp;</span>');
    }
  }
});

あとは必要に応じて上記の inlineBgFix() メソッドを呼び出す。例えば pdf という class を持つ a 要素に背景画像を適用するなら:

$(function() {
  $('a.pdf').inlineBgFix();
});

この結果、バージョン 8 未満の IE では以下のマークアップが生成される:

<a href="..." class="pdf inline-bg-fix">
  <span class="bg-l">テキスト<span class="bg-r">
</a>

そして CSS。まず IE6/7 用の記述から。「本来の」要素に指定されるパディングと背景画像を打ち消し、余分に挿入されたスペースを相殺しておく。なお スペースの幅はフォントによって異なる ので調整のこと:

.inline-bg-fix {
  padding: 0 !important;
  background: none !important;
}
.inline-bg-fix .bg-l {
  margin-right: -0.3em;
}
.inline-bg-fix .bg-r {
  margin-left: -0.3em;
}

最後に、パディングと背景画像をまっとうなブラウザ用と IE6/7 用と一緒に指定。以下は要素の左側に配置する場合の例:

a.pdf,
a.pdf .bg-l {
  padding-left: 20px;
  background: url(/img/pdf.png) no-repeat 0 50%;
}

面倒だけど、とりあえずはこれでうまくいく。IE8 では修正されているのがせめてもの救い。