固定背景(background-attachment: fixed)が壊れる場合の対処方法【iOS/IE完全対応版】

動画や画像を背景に置き、全画面表示にするためブラウザのサイズに合わせて伸縮させたい…。
昨今のおしゃれサイト制作時によくみられる構造です。

希望の挙動にするためにまず思いつくのは、
背景固定のcss、”background-attachment: fixed;”、そしてブラウザサイズに背景をフィットさせる”background-size:cover;”でしょう。これらは有用なプロパティなのですが、使用にするにはいくつか気をつけなければいけないポイントがあります。
制作時は気づきにくいのですが実装後、いざ実機で確認してみるとiOSやIEで問題が出てしまいます。

iOS既知のバグ

I CAN USEで「background-attachment」を確認してみると、一部未対応のブラウザがあります。さらに「Known issues」のタブを確認してみると一番下に以下の記述があります。

Can I use [background-attachment]

iOS has an issue preventing background-attachment: fixed from being used with background-size: cover

https://caniuse.com/?search=background-attachment

iOSには、「 background-size: cover」と一緒に「background-attachment: fixed」を使えない問題があります。という内容です。実機で確認するとわかりますが、この状態だとiPhoneやiPadなどで背景が固定されません。こちらのデモページでご確認ください。

こちらのページではbodyに背景画像を配置し、「background-attachment: fixed」「 background-size: cover」を設定してあります。
body{
 height:100vh;
 background-image: url("任意の画像");
 background-attachment: fixed;
 background-position: center top;
 background-repeat: no-repeat;
 background-size:cover;
}

※ここではサイト全体の背景を想定していますのでbodyに設定していますが、実際はbodyでなくて構いません。div等でも同じです。

対応方法

上記の症状に対応するには、背景以外の別のブロック要素を別に準備し、それを疑似背景として使用するという方法を取ります。背景自体は固定できないので、無理やり他要素をコンテンツの一番下レイヤーとして全画面固定してしまおうと言うわけです。
例えば追加の<div>を準備し、その<div>自体をposition:fix;で全画面固定してしまい、その <div>に背景要素をcoverで置いてやる。または、img要素を背景として一番下に配置してしまうなどで実現可能です。

というようにやろうと思えばどのような方法でも行けるのですが、いかんせんhtmlが複雑になりますし、あまりきれいではない。(とはいえ他のコンテンツやサイト全体の構造との兼ね合いで十分選択肢に入ります。)そして何より、組み終わった後のhtmlをあまりいじりたくない…。jsなどバリバリ入れてあると特に触りたくないですよね。

ということで、cssで完結する疑似要素を使用します。
こちらの場合はbody::beforeでの疑似要素を全画面配置することでiOSに対応させています。

body:before{
 content:"";
 display:block;
 position:fixed;
 top:0;
 left:0;
 z-index:-1;
 width:100%;
 height:100vh;
 background-image: url("任意の画像");
 background-position: left top;
 background-repeat: no-repeat;
 background-size:cover;
}

ここでポイントなのが、heightを100vhに設定し、画面の高さが変わっても全画面表示になるよう設定すること。そしてz-index:-1;で他要素の下に配置することです。

もしここで、疑似要素が表示されなくなった場合、疑似要素を配置している親要素(例の場合はdody)にz-indexがついていないか確認してください。
親要素にz-indexが指定してあった場合は多分それが原因です。この場合親のz-indexを削除しするしかないのですが、構造の都合上でできない場合は、別な方法で(上のほうで挙げたブロック要素追加やimgを使用等)対応の必要があります。
また、他にも親要素にopacityやtransformなどのプロパティがついている場合も破綻しますので他の方法を取ってください。

IEの場合

疑似要素が表示されない場合の対応

めでたしめでたし、と思いきやここで新たな問題が発生。
調べても事例が出てこないので私の環境だけなのかもしれないですが、上記DEMO2の状態だとIEで背景画像が表示されない…。出てくるタイミングもあるのですが、出てこないことのほうが多い。これだとクライアントから詰められること間違いなし。
と、言うことでいろいろ試したところ、親要素にposition: relative;を設定することで確実に画像が表示されるように。
position:fixed;の場合は画面に対して固定されるはずなので、そんな設定しなくてもよいはずなんですけどね…。z-index:-1;を外してみたところ親のposition: relative;がなくとも表示されましたのでz-indexがらみで何かが起きているのかもしれないです。
もし同じようにIEで疑似要素が表示されない場合は試してみてください。


body {
 position: relative;
}
body:before{
 content:"";
 display:block;
 position:fixed;
 top:0;
 left:0;
 z-index:-1;
 width:100%;
 height:100vh;
 background-image: url("任意の画像");
 background-position: left top;
 background-repeat: no-repeat;
 background-size:cover;
}

スクロールでガクガク問題

IEの場合はposition:fixed;でもbackground-attachment: fixed;でも固定自体はできるのですが、別の症状が発生します。
スクロールバーを使う場合は問題ないのですが、マウスホイールを回してページスクロールをする場合、固定した背景がガクガクと上下に動いてしまい、閲覧するにはつらい状況になる場合があります。(DEMO03をIEで閲覧、マウスホイールでのスクロールでご確認いただけます。)

原因はIEに設定されている「スムーズスクロール」機能のようです。(ページ内リンクをクリックしたときにスルスルっと動くスムーズスクロールとは別物です。)
右上のあたり、ツール>インターネットオプションを開き、右端詳細設定タブ>ブラウズに項目があります。このチェックを外せはガタつかなくなるのですが、閲覧者にIE設定を変えろというわけにもいきません。

IEスムーズスクロール設定箇所

この場合はこちらで無理やりスムーズスクロールを無効化してあげましょう。
以下のjsを外部ファイルまたはbody直前あたりにおいてください。これでがたつきがストップするはずです。

<script>
if(navigator.userAgent.match(/MSIE 10/i) || navigator.userAgent.match(/Trident\/7\./) ||   
 navigator.userAgent.match(/Edge\/12\./)) {
 $('body').on("mousewheel", function () {
  event.preventDefault();
  var wd = event.wheelDelta;
  var csp = window.pageYOffset;
  window.scrollTo(0, csp - wd);
 });
}
</script>

まとめると…

パララックス等でブラウザサイズいっぱいに背景画像を表示させ、固定した場合に、iOS、IEで閲覧時に問題が発生したら以上の対応が必要です。
場合によってはIE切り捨ても可能かと思いますので、その場合は最初のiOS対応だけできればOKです。

IEについては一部環境で最後のjsでもスムーズスクロールが切れないという事例もあるそうで、その場合はスクロール系のjsライブラリを入れるという手があります。
ただし以前スクロールバーをカスタムするためにそのようなライブラリを入れたら、iOSのような慣性スクロールになったものの、Chromeで挙動がおかしくなってしまったことがありますので、さらに状況を複雑にする可能性もあります。
ですのでできる限り上記のjsで対応することをおすすめいたします。