iOSでposition:fixedした要素がスクロール中だけz-indexを無視して前面に表示されてしまうバグ

More than 1 year has passed since last update.

ブラウザの表示領域に対して要素の位置を固定してくれるposition: fixed;は、表示領域が限られるスマートフォンでこそ使いどころがたくさんあるのだが、(特にAndroidで)バグが多くて実用にはまだまだ慎重にならざるを得ない。

今回は、iOSで遭遇したバグについて記述しておく。

症状

position: fixedを指定した要素がz-indexを無視する。
精確に説明すると、position: fixedを指定した要素が、表示領域外にある部分が、スクロールされて表示領域に入ってきたときに、スクロールされている間だけ、z-indexが無視されて、より大きなz-indexが指定されている(即ちより手前の)要素より前面に表示される。

実際のコードをご覧いただこう。

   <div class="contents">ここに通常のコンテンツ</div>
   <div class="bg"></div><!-- 背景表示のための要素 -->
   <div class="footer">
      iPhoneだと通常スクロールしなければ見えない要素
   </div>
   .contents {
      position: relative;
      z-index: 10;
      height: 500px;
   }
   .bg {
      position: fixed;
      z-index: 1;
      top: 0px;
      left: 0px;
      width: 100%;
      height: 100%;
      background-image: url(bg.jpg);
   }
   .footer {
      position: relative;
      z-index: 10;
      height: 200px;
      background-color: #fff;
   }

div.footerは、スクロールしなければ見えない位置にある。
スクロールすると見えるはずなのだが、なぜかdiv.bgの下に隠れて見えないのだ。そしてこの現象が発生するのは、 スクロールしている間だけ であり、スクロールが停止した段階で、前面に正しく表示される。
また、div.footerがスクロール前に表示領域内に一部でも見えていれば、この現象は発生しない。

現象を確認した環境

  • iOS7 Safari, Chrome
  • iOS6 Safari, Chrome

iOS版 Operaではこの現象は見られなかった。

position:fixedを使った理由

なぜ、div.contentsbackgroundを設定するのではなく、div.bgという要素をわざわざ用意しているかというと、背景画像をスクロールさせずに固定位置に配置したかったからである。
背景画像を固定にするためのプロパティbackground-attachment: fixedは、スマートフォンではうまく機能しないケースが多いため、このようなハックが出回っているが、そのために別のバグにでくわしたというわけだ。

対応策

下に隠れてしまうdiv.footerに以下のcssを指定することで解消される。

.footer {
   -webkit-transform: translate3d(0,0,0);
}

ただし、translate3dを設定することによって、Androidでは全く別のバグを誘発しうるので注意を要する。
必要に応じて、JavaScriptでユーザーエージェントで判定してclass属性を出し分けるなどして、このCSS記述がiOSのときだけ反映されるように工夫してやる。


position:fixedくらい、そろそろ普通に使いたい。background-attachement:fixedも。