iOSのSafariでposition:fixedのiframeをスクロールさせる方法

技術的な内容の記事です。

現在、まめわざでは管理ページのスマホ対応化を進めています。
問合せフォームなどの設定をiframeで行っていますが、position:fixedで表現する小窓内にiframeを置いた場合、iOSのSafariでスクロールしなくなる現象に悩まされました。
試行錯誤の結果、解決方法を見つけたのでここに記します。

検証テスト結果

以下のHTMLとCSSの組み合わせで、position:fixedのiframeがスクロールするかを、iOSのSafariで検証しています。

結果HTML・CSSの説明HTML例CSS例
×iframeにposition:fixed<iframe class="type1" frameborder="0"></iframe> iframe.type1 {
    position:fixed;
    overflow:auto;
    left:0;
    top:0;
    width:100%;
    height:100%;
} 
×position:fixedの下にiframe<div class="type2"><iframe frameborder="0"></iframe></div> div.type2 {
    position:fixed;
    left:0;
    top:0;
    width:100%;
    height:100%;
}
div.type2 firame {
    width:100%;
    height:100%;
} 
×position:fixedの下にiframe

-webkit-overflow-scrolling:touch;の魔法
<div class="type3"><iframe frameborder="0"></iframe></div> div.type3 {
    position:fixed;
    left:0;
    top:0;
    width:100%;
    height:100%;
}
div.type3 firame {
    width:100%;
    height:100%;
    -webkit-overflow-scrolling:touch;
} 
×スクロールするposition:fixedの下にiframe<div class="type4"><iframe frameborder="0"></iframe></div> div.type4 {
    position:fixed;
    left:0;
    top:0;
    width:100%;
    height:100%;
    overflow: auto;
}
div.type4 firame {
    width:100%;
    height:100%;
} 
スクロールするposition:fixedの下にiframe

-webkit-overflow-scrolling:touch;の魔法
<div class="type5"><iframe frameborder="0"></iframe></div> div.type5 {
    position:fixed;
    left:0;
    top:0;
    width:100%;
    height:100%;
    overflow: auto;
    -webkit-overflow-scrolling:touch;
}
div.type5 firame {
    width:100%;
    height:100%;
} 

重要なポイントは以下です。

  • iframeだけでは実装不能
  • スクロールするのはiframeではなくdivの方
  • -webkit-overflow-scrolling:touch;も必要。divの方に要設置。
  • iframeにwidth:100%;height:100%とframeborder=0
    枠は上位の要素が担当

まず、iframeそのものをposition:fixedにする場合はiOSでスクロールできません。必ずposition:fixedとなる上位の要素の中にiframeを設置する必要があります。
次に、iframeにscrollを担当させてはいけません。ここがなかなか気がつかないポイントです。position:fixedとした上位の要素をoverflow:autoにして、iframeのスクロールの仕事を上位の要素に担当させます。
またに、iOSのスクロール系の解決策として検索すると出てくる
-webkit-overflow-scrolling:touch;
という魔法が必要ですが、これも上と同じ理由で、iframeではなく上位の要素(スクロールを担当する要素)にセットする必要があります。
最後に、このギミックで出来上がった要素をiframeっぽく見せるため、またクロスブラウザ化するために、iframeのwidthとheightを100%にして、frameborder=0をHTMLに設置します。この状態ではiframeの枠が消えるので、上位の要素に枠を定義します。

position:staticの場合にも使えます

この問題は、iOSのSafariが「iframeの中身をスクロール不要で全て表示する」ことに起因しています。
position:fixedに限らず、文章の一部としてiframeを利用する場合でも、heightは無視され中身が全て表示されてしまいます。
この場合も、

  • iframeをネスト化
  • 上位の要素に
    height:任意の高さ;
    overflow:auto;
    -webkit-overflow-scrolling:touch;
    をセット
  • iframeっぽく見えるように体裁を整える

このようにして対策が可能です。

蛇足)iframeの今後

iframeを使った処理は今後なるべく減らしたほうが良い、と考えています。例えば次のような理由が挙げられます。

  • ブラウザによって得意な動作をする要素は避けたほうが無難(今回の問題)
  • スマートフォンではwindowとiframeの多重スクロールが閲覧しづらい(iframeがスクロールしきってからwindowがスクロールする)
  • 共有のcssやjavascriptを親子で重複して読み込む必要があり不効率
  • jQueryなどのライブラリによりAJAXが使いやすくなったため、iframeを使わない代替処理を実装しやすくなった
  • https化で問題になる場合あり

2番目は特に重要です。モバイル環境を第一に考えるモバイルファーストという言葉がありますが、使いやすいインターフェースを考えた場合、iframeは既に避けた方が無難な選択肢の1つだと言えます。
まめわざではiframeによる処理を実装してしまいましたが、少しずつ使用しないようにプログラムを変更していこうと考えています。

2016/5/27