2019年3月12日火曜日

iframeを良い感じに表示するCSSテクニックまとめ

高さのわからないiframeを指定した余白いっぱいに、良い感じに表示するのは割と難しい。 いつも物凄くハマるので、何が駄目で、どう書くと良いかという事例ベースでテクニックをまとめておく。

html,body { margin:0; height:100%; overflow-y:hidden; }
iframe { width:100%; height:100%;  }
ネットでよく見かける書き方だけど、これはスマホでスクロールできなくなる。 html,bodyに height:100% を指定するのは基本的に危険。 もちろんあえてスクロールさせたくない場合には付けても良い。

html,body { margin:0; width:100%; overflow-x:hidden; }
iframe { width:100%; height:100vh; }
overflow-x:hidden; を入れても良さそうに見えるけど、これはPCサイトでhtml,bodyのスクロールの余白が残ってしまう。

html,body { margin:0; width:100vw; }
iframe { width:100%; height:100vh; }
overflow-yを指定しない路線でいくならこれが一番まともだと思う。 html,bodyのスクロールバーを100vwであえて表示しないようにして、iframeのスクロールバーと重複した位置に配置する。 見た目上は普通のスクロールバーに見えるので、ぱっと見では違和感がない。 ただページの最上部と最下部でスクロールバーが本当は2つある事によって若干不自然なスクロールになるのが欠点。 これがあるので overflow-y:hidden; が良い。

html,body { margin:0; width:100vh; overflow-y:hidden; }
iframe { width:100vw; height:100vh; }
vw, vhもよく見かけるけど注意が必要。上記はスマホではスクロールバーぶんシフトされた表示になってしまう。 あといかにもページが固定されそうだけど固定されない。
PCでも色々問題が起きる。 例えばページ全体が固定される、フッター部分に余白がないと見切れる、横スクロールぶんシフトされる問題などが発生する。

html,body { margin:0; width:100%; overflow-y:hidden; }
iframe { width:100vw; height:100vh; }
上記の問題に対処するため、html,bodyは%で設定してスクロールバーを考慮する。 iframeのheightは100vhにしてあるけど可視領域に応じて調整する。 たぶんこれが正解の書き方だと思う。



他にも以下のような書き方があります。 iframe-wrapperでiframeを囲んでその領域全体にiframeを記述する方法です。 これもたまに見かける書き方で、これでも動きます。
<style>
  html,body { margin:0; width:100%; height:100%; overflow:hidden; }
  #iframe-wrapper {
    position:absolute; top:50px; left:0px; bottom:0px; width:100%;
  }
  #iframe { width:100%; height:100%; position:absolute; top:0px; bottom:0px; }
</style>
<div id="iframe-wrapper">
  <iframe id="iframe" src="/example.html"></iframe>
</div>


ここまで書いてきた2つの書き方ならたいていのケースでうまく表示できるのですが、厳密にはまだ駄目です。 たいていのケースでは確かにうまくいくんですが、preのように折り返しのないオブジェクトがあった時、どうやってもiOSで表示が崩れてしまう。 これ私はどうやってもCSSで対処できなかったのでGoogleがどうやってるか見てみたのですが、 Googleはiframeを使う事を諦めて、外部サイトをサーバで読み込んだものをDOMに直接書き込んでレンダリングしていました。

iframe駄目じゃん。闇は深い。


そもそもどうしてpreがあると表示が壊れるかですが、これは推測しかできませんが、 おそらくiOSではiframe内の画面サイズが設定されていないんじゃないかなあと思います。 だからpreが可能な限り横方向を伸ばそうとして表示が崩れてしまう。

iframeを使わないというのも1つの手ですが、他にもonload後に固定幅を指定し直す方法が考えられます。 例えば以下のような感じ。
const iDoc = docuemnt.getElementById('iframe').contentWindow.document;
iDoc.body.style = document.body.clientWidth + 'px';
これはDOMを直接書き込むような実装変更よりずっと簡単に対処できるので割とおすすめですが、弱点もあります。 iframeのonload後にしか指定できないので若干レンダリングが遅くなるかも知れません。 実装量と速度のどちらを取るかは開発者次第。


何にしてもこういったものを初見で書ける気がしないんだよなあ。 何年書いていてもCSSは全然慣れないしわからない。

0 件のコメント: