通常情况下,iframe 的尺寸由父页面进行控制,在 iframe 内打开的子页面只能在这个限定的区域内显示内容,而几乎所有浏览器也都是按照这种方式去实现的。但在 iOS 系统中,iframe 的高度却与此有了很大出入:如果 iframe 的高度小于其内容高度,则会强制拉高 iframe 以适应内容高度,且父页面没有任何手段控制这个行为。

这会为子页面带来很多问题,比如子页面无法滚动且无法监听到任何滚动事件,定位元素无法正常定位等。虽然不知道 Apple 为什么要这么做,但当我们真的需要在页面中打开一个 iframe 时(比如在一个单页面电商应用中打开一个专题页),这会为我们带来不少的麻烦。

因为 iOS 中没有提供任何手段让我们关闭这个机制,所以我们只能通过一些 hack 的方式绕过它,目前有两种方案可供使用,一种需要修改在 iframe 中所打开的子页面,如果你可以修改它,那么接着看下去;另外一种是在父页面中进行一些调整,可以直接跳到下一章节去看,虽然该方案并不能完全解决问题。

子页面对 iframe 做兼容处理

该方案需要在子页面的 body 元素内添加一个标签,并将该页面内的所有内容移到该标签内:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Subpage</title>
    </head>
    <body>
        <div id="page">
            Subpage Content ...
        </div>
    </body>
</html>

并应用如下样式:

html, body, #page {
    width: 100%;
    height: 100%;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
}

#page {
    width: 1px;
    min-width: 100%;
}

这会带来一些额外的麻烦,比如监听页面滚动时需要改为在 #page 元素上绑定滚动事件,而不再是在 window 上绑定。

不过无论如何,这总是最好的方案,因为你想要的任何东西都还可以实现,只是更麻烦了一点而已。

父页面对 iframe 做兼容处理

在子页面中做兼容处理是要有个前提的,就是你得能修改子页面。

如果你要在 iframe 中加载一个第三方页面,而这个页面还没有对自己将要被放在一个 iframe 中做任何准备,那要怎么办呢?

很简单,不要这么做!!!这种时候,最好老老实实地在当前页签内打开第三方页面,并在用户回来后好好地重现现场。因为起码,这是一个稳妥的方案。

当然,如果你要打开的子页面就只是一个普通的页面,比如文章页,它里面没有滚动监听,也没有相对视口依靠,那么如果你非要在 iframe 中打开它,也不是不可以。

很简单,把 iframe 放入一个限定宽高的 div 元素中,让其在这个 div 中滚动就是了:

<div class="wrap">
    <iframe src="http://a-page.com"></iframe>
</div>
.wrap {
    width: 100%;
    height: 300px; /* or other values */
    overflow: auto;
    -webkit-overflow-scrolling: touch;
}

不过这种方案不好,非常不好。它的限制性太强,而你又完全无法控制你所嵌入的那个第三方页面,你不会知道什么时候它会做出一引发问题的改动。这就好像在你的页面中埋下了一颗地雷。

总结

最后,那就要多问一句了:为什么你非要用 iframe ?