TRBLメソッドでbackground-sizeを代替する

親要素に対して子要素を上下左右に中央配置するTRBLメソッドというものがある。

.wrap{
  position: relative;
  width: 300px;
  height: 300px;

  border: 1px solid magenta;
  resize: both;
  overflow: hidden;
}

.inner{
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;

  width: 176px;
  height: 244px;

  background-color: grey;
}

デモで確認できる

.innerをimgタグにして、画像の大きさが不定の時でも頑張る方法は 以前記事にしているので参考までに。

このTRBLメソッドでさらに頑張ると、背景指定ではなくimgタグを使いつつもbackground-size: 100% auto;のような表示が可能になる。

元ネタは そめさんのツイートから。

要件

  • div > imgの構造
  • divの幅と高さは固定
  • imgの大きさは不定
  • imgはdivに対して横幅いっぱいに表示させたい
  • divに対してimgの高さが大きい時は上下に均等にはみ出させて隠す
  • divに対してimgの高さが小さい時は上下に均等に余白を残す
  • IE8でも表示できる

普通にimg{width: 100%;}するだけだと上下方向のセンタリングができない。背景画像を指定する方法で幅のフィットだけ(クライアントに)諦めさせたいところだけど、構造が変えられないので不可。background-sizeでやれれば 一番簡単だけどIE8はbackground-sizeプロパティに対応していない。ということでTRBLメソッドを使う。

画像の横幅が常に親要素にフィットするということはwidth: 100%;を加えるとできるが、それだけではダメで4辺の値を工夫する必要がある。

.wrap{
  position: relative;
  width: 300px;
  height: 300px;
  overflow: hidden;
}

.wrap img{
  position: absolute;
  top: -100%;
  right: -100%;
  bottom: -100%;
  left: -100%;
  margin: auto;
  width: 100%;
}

ボタンを押すとねこ画像が切り替わって挿入されるデモを作った。どのサイズの画像でもbackground-size: 100% auto;と同じ表示形式になっているのが確認できると思う。画像の読み込みが少し重い。JavaScriptがイーカゲンなのには目をつぶって欲しい。PlaceKittenはサイズの組み合わせによっては表示してくれない場合があり、珍妙なif文を書かざるを得なかった。

containもcoverも

background-sizeの他の値もTRBLメソッドの応用で代替できる。代表的な値は100% auto, auto 100%, contain, coverだろう。.wrapのルールセットは同じなので、画像のスタイルだけそれぞれまとめると下記のようになる。

/* like background-size: 100% auto; */
.wrap img{
  position: absolute;
  top: -100%;
  right: -100%;
  bottom: -100%;
  left: -100%;
  margin: auto;

  width: 100%;
}
/* like background-size: auto 100%; */
.wrap img{
  position: absolute;
  top: -100%;
  right: -100%;
  bottom: -100%;
  left: -100%;
  margin: auto;

  height: 100%;
}
/* like background-size: contain; */
.wrap img{
  position: absolute;
  top: -100%;
  right: -100%;
  bottom: -100%;
  left: -100%;
  margin: auto;

  max-width: 100%;
  max-height: 100%;
}
/* like background-size: cover; */
.wrap img{
  position: absolute;
  top: -100%;
  right: -100%;
  bottom: -100%;
  left: -100%;
  margin: auto;

  min-width: 100%;
  min-height: 100%;
}

それぞれの表示を ヤンチャに切り替えられるデモを置いた。JSでクラスを操作する関係でCSSが少し読みづらい。なのでルールセットは上記を参考にしてほしい。

TRBL: 0;ではなくTRBL: -100%;

containを代替したい時は4辺の値は-100%でも0でよいが、それ以外の時は-100%でないと、ブラウザによっては縦や横方向のセンタリングがうまく効いてくれない場合があった。4辺0値の時の表示でこちらで確認できた組み合わせは以下のとおり。

4辺0値のTRBLメソッドでセンタリングができない方向
代替したいbackground-size値 Internet Explorer 8 Firefox 36 Chrome 41
100% auto - -
auto 100%
contain - - -
cover 横と縦

チェックはこのページを使った。すこしややこしいのだが、上表の方向はそのまま子要素の画像が縦長か横長かとイコールになっている。つまり、「4辺0値のTRBLメソッドで100% autoを表現しようとすると 縦長の画像縦方向のセンタリングがFirefox 36で効かない」。横長の画像なら縦方向のセンタリングはFirefox 36でもちゃんと効く、ということ。

coverについてはFirefox 36だけは画像のアスペクト比にかかわらず両方向でセンタリングが効かず、top: 0, left: 0だけ指定されたような配置になる。辺指定なしのデフォルトとも見える。

互いに引っ張り合う座標指定

フィットさせて親要素より上下や左右がはみ出る画像だと、0値ではセンタリングがうまくいかなくなるのは不思議だ。上と左へのマイナス方向の配置が不足している感じがする。-100%値だと、4辺が お互いを大きく引っ張り合うように座標が指定され、上下も左右もセンタリングしてくれるようになるのということなのだろうか。

値は4つとも同じであれば-200%でも-1000%でもいいが、子要素の画像の幅や高さは親要素ベースで%計算されているので、-50%より大きな数値にしてしまうと画像サイズによってはズレが生じてくる気がする。-100%にしておくのが無難だと思う。


2015年になってもTRBLメソッドについて書いた。 2012年にまとりさんが記事にしてからだいたい年に1回はネタにしてる。このルールセットには仕事でもずいぶん助けられたし、CSSの面白さも感じられるいいメソッドだと思う。

Special Thanks! @understandard(検証を手伝ってくれたので今半をおごる可能性が高い)