ミッションインポッシブル ~background-sizeをIE8に対応させよ~

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
テッテッテッテ♪ テッテッテッテ♪
テッテッテッテ♪ テッテッテッテ♪
テレレ~♪ テレレ~♪ テレレ~♪

あ、これはミッションインポッシブルのあの曲です。脳内再生で夜露死苦。



さてさて。

最近のCSSにはbackground-sizeっていうのがあって、背景の画像サイズを調整できるようになっているんですね。これのおかげで、ある程度大きな背景画像でも望む大きさで表示させることができるので、なかなか便利なやつだと思います。

でもこのbackground-size、ChromeとかSafariなんかでは問題なく使えるんですが、IEの場合はバージョンが9以上じゃないと使えないみたいなんですね。なのでうっかりIE8なんかでサイトを見ると、背景画像がでっかいまま表示されてしまっている。

「これ見てくれよジョニー。IE8じゃこのでかいフランクフルトの背景画像が指定サイズに収まらないぜ。まるでパンツに収まらないオレのフランクフルトのようじゃないか!! HAHAHA!!」

そんな感じです。



IE8とか7とかをhtml5に対応させたりcss3が適用するようにしたりってのは結構いろんな人が頑張っているので、もしかしたらこのbackground-sizeもIE8に対応させるプラグインとか誰か作ってんだろうなあと思って調べてみたんですけど、意外とないみたい。

background-sizeにcoverとかcontainを指定する場合の代替策みたいなのは参考文献が見つかったんですけど、具体的に「〇〇px」とかで指定されたものをIE8でも見れるようにするっていうのは、僕がググった限りではちょっと見つかりませんでした。

誰もやってないってことは、何かやらない方が良い理由があるのか、「おとなしくちょうどいいサイズの画像を用意しとけよ。フランクフルトを止めてポークビッツにしとけよ」ってことなのか……それは分かりません。

とは言え、今回、できればIE8でもbackground-sizeが使えた方が良いなって場面に直面してしまったので、マリー何とかネットさんを崇拝している僕としては、「誰も作ってないなら自分で作れば良いじゃない」の精神で、ちょっと作ってみることにしました。

それがこのbackgroundsize-ie8.jsです。

もし良かったら、使ってみてちょ。

あ、ちなみにjquery読み込まないと動かないからね。



やっていることとしては、背景画像をimgタグに変換して、同じ位置に出力させているだけです。ようは背景画像じゃなくなるってことですね。

ページ読み込み時にCSSファイルを読み込んで、background-sizeが指定されている要素に対して以下のような変換処理を施しています。

//css
div {
  background-image:url(sample.png) no-repeat;
  background-size:10px 10px;
}

//変換前
<div>
  <p>サンプル</p>
</div>

//変換後
<div style="background-image:none">
  <img src="sample.png" style="width:10px;height:10px" />
  <p>サンプル</p>
</div>

サンプルページ作ったんで、良かったらIE8で確認してみてください。

サンプルのダウンロードはこっち



一応、それなりに使えるように作ったつもりではあるんですけど、あくまでも僕自身が自分で運用しているサイトで発生した問題を何とかするために作っただけのものなので、場合によっては上手く動かない部分もあると思います。

とりあえず、考えられる注意点をいくつか。



背景ではなくなるってことに注意

何と言っても一番はこれですね。

背景のままだとどうしてもサイズの指定ができないんで、imgタグに直しているわけなんですけど、それによってページのデザインが崩れる可能性はあると思います。



positionが勝手につく

上のコードには書いてないですけど、実際はdivやp要素に「position:relative」がついたり、imgタグに「position:absolute」がついたりするので、やっぱりそれによってデザインが崩れてしまう可能性は、あるかもしれない。

僕のサイトではとりあえず大丈夫だったけど。



background-repeatがno-repeatじゃないと対応できない

なんせimgタグを出力する形に変換しているものですから、画像一枚分しか出せないんですね。だから背景画像がリピートしているような場合は対応できない。

なのでno-repeat以外の場合は処理を行わないようにしています。



containとかcoverはあまり真面目に対応していない

何か「background-size:contain」とか「background-size:cover」ってやつの違いが今ひとつよく分からなかったんで、わりと適当な対応になってます。

たぶんこーゆーことなんだろうなあって感じなので、もしかしたら望む動きにならないかもしれない。ごめんちゃい。

まあさっきも言ったけど、containとかcoverは他に対応している人がいるみたいだから、そっちググって使ってみて。



中身は子要素にしとかないと上手く動かない

えー、これはどういうことかって言うと……見た方が早いかな。

//サンプルの文字がimgタグの上に表示される
<div>
  <p>サンプル</p>
</div>

//サンプルの文字がimgタグの下に隠れてしまう
<div>
  サンプル
</div>

何でこうなるのかって言うと、対象要素の子要素(上の場合はpタグ)に「position:relative」をつける処理を入れているからです。z-indexを何も指定してないから、こうしないと生成したimgタグに隠れちゃうんです。imgタグの方には「position:absolute」をつけてるのでね。



外部CSSファイルに記述しないと動かない

さっきもちょっと書きましたけど、実際どんな動きをしているかをもう少し詳細に説明すると、まずページ読み込み時に「document.styleSheets」ってやつで外部CSSファイルの内容を読み込んでいます。

んで、まあ何やかんやとやると、CSSファイルに記述している内容を文字列で取得することができるんですね。

いや、ものすごい適当な説明で申し訳ないんだけど、そうなのよ。「javascritp style cssText」とかでググってみるよろし。

文字列を取得したら、正規表現を使ってbackground-sizeが指定されているセレクタを抽出して、ページ内の各要素に対してimgタグの変換を行うと。

ソースを見てもらった方が早いかもしれないね。言葉だけで説明しても上手く伝わらないかも。僕、説明下手だから。

見逃しちゃった人のために、サンプルのダウンロードはこっちからね。



とにかく、直接記述しているようなものは適用外ってことです。

//css
div {
  background-image: url(sample.png) no-repeat;
}

#sample1 {
  background-size:10px 10px;
}

//html
//適用される
<div id="sample1">
  <p>サンプル</p>
</div>

//適用されない
<div id="sample2" style="background-size:10px 10px">
  <p>サンプル</p>
</div>

こういうことですね。



動的に作られる要素は別の形で対応

ページ読み込み時に処理が走るように作ってあるので、後で動的に生成された要素には利きません。サンプルページで言うところのサンプル8みたいなね。

しつこいようだけど、サンプルのダウンロードはこっちだよ。



一応そういうときのために「$(element).remakeImage()」っていう、個別に背景画像をimgタグに変換するコードも入れてあるので、試しに使ってみるよろし。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="backgroundsize-ie8.js"></script>

<script>
$(function(){
  $('#sample1').remakeImage({
    backgroundSize:'10px 10px'
  });

  //background-size:auto autoと同じ
  $('#sample2').remakeImage();
});
</script>

こんな感じで使う。

こっちの場合は、自分でbackground-sizeを書いてやらないと動かないんですけどね。書かない場合は、勝手に「background-size:auto auto」として処理されます。



どうやらIE8の場合、background-size自体は適用してくれないけど、jqueryでbackground-sizeを突っ込んだ場合、「$(element).css(‘backgroundSize’)」で値を取得することだけはできるみたいなんですよ。何でだろね?

//css
#sample1 {
  background-size:10px 10px;
}

//html
<div id="sample1">
  サンプル
</div>

<div id="sample2" style="background-size:10px 10px">
  サンプル
</div>

<div id="sample3">
  サンプル
</div>

<script>
$(function(){
  //取得できない(undefinedが返って来る)
  sample1 = $('#sample1').css('backgroundSize');

  //取得できない(undefinedが返って来る)
  sample2 = $('#sample2').css('backgroundSize');

  //取得できる(ただしスタイルは適用されない)
  $('#sample3').css({backgroundSize:'10px 10px'});
  sample3 = $('#sample3').css('backgroundSize');
});
</script>

IE8だと、こんな結果になるみたいです。ただし上にも書いてある通り、sample3という変数に「10px 10px」が入ってるというだけで、実際にidがsample3のdivの背景サイズは10pxにはなっていない。

でもまあ値は取れるんで、それ使って同じようにimgタグへ変換することならできるというすんぽーです。



たまに上手く動かない

もうね、理由とかよく分かんないんだけど、なぜかたまーにjavascriptエラーが出て動かないことがある。何回かリロードすると直るんだけどね。

どうもCSSファイルの読み取りに失敗してるとかそんなエラーっぽい感じなんだけど……キャッシュが利いて読み取れないのかなとかも思ったんだけど、キャッシュ消しても動かないことがあるから、ほんまに原因が分からへん。

ま、IE8でだけだから良いかな~ってことに、とりあえずしてあります。






基本的にこれはIE8でしか動くことを想定していないです。だから動作もIE8しか検証してない。いや、Safariとかでも動くんだけどね。でもSafariでは動く必要がない。background-sizeが使えるからね。

だから使うときはこんな風に読み込むと良いと思う。

<!--[if lt IE 9]>
<script type="text/javascript" src="backgroundsize-ie8.js"></script>
<![endif]-->

これでIE8以下のときにしか読み込まれない。でもIE7での動きは特に検証してないです。



ごちゃごちゃといろいろ書きましたけど、こういうのは百聞は一見にしかずの精神で、実際に使ってみた方が早いですね。

最後にもう一度言うけど、サンプルのダウンロードはこっちですからね。よろしく頼みます。

まるで怪しいインターネット商材並みにダウンロードを進めてるな……大丈夫かな。本当にソースがダウンロードできるのかって、怪しまれてないかな。ウイルス仕込まれてんじゃねーのって、疑われてないかな。

そんなに心配しなくても大丈夫だよ。ウイルスナンテシコンデナイヨ。



ま、まあとにかく。

実際に使ってみて、もしちゃんと動かないようなら、怒りのコメントを俺にぶつけるか、自分で良い感じに作り直して「アンパンマン、新しいコードよ!」って俺にぶつけるかするのが良いと思います。

できればちゃんと動いたとしても、よりちゃんと動くために誰かが修正してくれると嬉しいね。そんときゃぜひ一報も欲しい。ってか修正したファイルを俺にくれ。

それを見て俺も勉強させてもらいやす。






追記

ちょびっと修正しました(2013/3/13)

上記にもあるように、このjsは対象要素の子要素に「position:relative」をつけてるんですけど、例えばその子要素に「position:absolute」のついた画像とかがある場合、absoluteからrelativeに変更されてしまうというご指摘をいただきました。

ご指摘ありがとうございます。確かにおっしゃる通りでした。exactlyでした。すみません。

//css
div {
  background-image:url(sample.png) no-repeat;
  background-size:10px 10px;
}

//変換前
<div>
  <img src="sample2.png" style="position:absolute" />
</div>

//変換後
<div style="background-image:none">
  <img src="sample.png" style="width:10px;height:10px" />
  <img src="sample2.png" style="position:relative" />
</div>

つまりこんな感じですね。divの中のsample2.pngの方のpositionが変わっちゃってます。

ご指摘いただいた方から、ありがたいことにその解決法も伝授していただいたんですが、positionが指定されてない要素、staticな要素のpositionがrelativeになればたぶん解決だなと思ったので、jsで各子要素のpositionを見て、staticだったらrelativeに変更するような処理にしてみました。

//css
div {
  background-image:url(sample.png) no-repeat;
  background-size:10px 10px;
}

//変換前
<div id="sample1">
  <img src="sample2.png" />
</div>

<div id="sample2">
  <img src="sample2.png" style="position:absolute" />
</div>

//変換後
//sample2.pngにposition:relativeがつく
<div id="sample1" style="background-image:none">
  <img src="sample.png" style="width:10px;height:10px" />
  <img src="sample2.png" style="position:relative" />
</div>

//sample2.pngはposition:absoluteのまま
<div id="sample2" style="background-image:none">
  <img src="sample.png" style="width:10px;height:10px" />
  <img src="sample2.png" style="position:absolute" />
</div>

動きとしては、こんな感じになってます。

サンプルの方もposition:absoluteの画像が入ってる場合を載せたんで、確認してもらえればと。サンプル10ってやつ。ちょっといろんな画像と文字が重なり過ぎて見辛いかもだけど、ご勘弁を。

これで大丈夫だと思うんだけど……どうかな?

もしまた何か不具合が見つかるようなら、その都度解決していきたいと思います。
 もしかしたら何か関連しているかも? 
 みんなからのコメント 
2013年01月23日 18:59:11
[...] 良いかも!(まだ未検証) カテゴリー: Ajax・JS, CSS   作成者: tama [...]
2013年03月12日 15:42:27
開発者A
箱の中に別の画像が入っていた場合・・・

箱 {
background-image: url(bgi.jpg);
background-image-size: cover;
position: relative;
}

箱 > 画像 {
position: absolute;
}

箱の中の画像もrelativeに変更されちゃうので、
backgroundsize-ie8.jsで変換されちゃう背景画像に
何かクラスを付けて、そのクラス付きの画像だけ
rerativeをかけた方がいいかもです。

とてもいいJSでビックリしました。
使わせていただきます。ありがとうございました。
2013年03月13日 13:00:53
まっち~(管理人)
>開発者Aさん
おお……確かに。おっしゃる通りです。気づきませんでした。

ただ、positionがabsoluteじゃないような場合には、やはりposition:relativeをつけないと現状は上手く動いてくれないので、今回は箱の中の要素がposition:staticになってるものをrelativeに変更するという処理で対応しました。

あ、でもそれらの要素に任意のクラスをつけてそのクラスにrelativeをかけるっていうのも、やり方としては同じですかね……むしろそうしておいた方が良いのかな?

ご指摘いただきありがとうございました。使用する中でまた何か問題が出るようでしたら、そのときは言っていただると助かります。
2013年03月21日 02:44:21
感謝
開発中のサイトで使わせていただきました! 解決策がなかなか見つからなかったのでありがたいです。
2013年03月22日 10:51:22
まっち~(管理人)
>感謝さん
こちらこそ、ありがとうございます!
もし何か不具合があるようでしたら、ご連絡いただけると助かります。
また追記で修正記事書き足しますんで。
2014年02月02日 11:14:43
[...] ミッションインポッシブル ~background-sizeをIE8に対応させよ~ | 日記の間 ...     [...]
2014年02月02日 11:29:38
[...] ミッションインポッシブル ~background-sizeをIE8に対応させよ~ | 日記の間 ... [...]
2014年08月11日 12:15:10
[…] ミッションインポッシブル ~background-sizeをIE8に対応させよ~ | 日記の間 … […]