前言

一个月前帮一个同事解决了一个 iOS 上的 IFrame 会自动增高导致的问题,本以为这个 Bug 应该会很罕见,就没有记录下来。今天又有一个其他组同事遇到了这个神奇的 Bug ,记录一下原因和解决办法。

当然,移动端使用 IFrame 不是一个很常见的做法,不过我第一次遇到的场景是在一个 Hybrid 项目里面嵌入了另外一个域名下的项目,所以使用 IFrame 是一种正确的解决办法。

这个 Bug 会造成的问题

  • 第一次遇到的问题是内容页面中一个显示在页面底部的元素显示不出来,这个元素的 position 为 fixed 。
  • 这次遇到的问题是IFrame 中的页面使用了一个无限滚动的插件,在滚动到底部的时候,没办法让无限滚动触发。

这两个问题出现的原因

这两个问题出现的原因都是因为 iOS 下的 Safari 的 IFrame 的高度会根据页面的内容自适应,造成了 IFrame 的高度过高。

  • 页面底部的元素无法显示是因为 IFrame 变高了,所以显示在了屏幕外面。
  • 无限滚动无法触发是因为 IFrame 变高了,里面的内容根本没有滚动条,无法触发内容页面的window 的 onscroll 事件。

重现 Bug

第一次遇到这个问题的时候,把问题定位到 IFrame 的高度这花了很久,以为是 Hybrid 项目出了问题,花了很久去看了 Hybrid 项目是不是做了什么特殊的设置。定位到是 IFrame 的高度的问题之后,写了两个简单的页面重现这个问题。

包含 HTML 的页面代码如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div>
  <style>
    iframe {
      border: 1px solid #ccc;
      width: 100%;
      height: 300px;
    }
  </style>
  <iframe frameborder="0" src="content.html"></iframe>
</div>
</body>
</html>

内容页的文件名为 content.html,内容页代码如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<script type="text/javascript">
  var addcontent = function() {
    document.body.appendChild(document.getElementById('test').cloneNode(true));
  };
</script>
<button onclick="addcontent()">Add content</button>
<div id="test">
  <p>test content</p>
  <p>test content</p>
  <p>test content</p>
  <p>test content</p>
  <p>test content</p>
  <p>test content</p>
</div>
</body>
</html>

重现这个问题相当简单,使用 iOS 上的 Safari 打开,点击 Add content 按钮,虽然定义了IFrame 的高度为300,但是IFrame 的高度会自动增加。

解决办法

知道了原因之后想办法绕过去总是很简单的,既然 IFrame 的高度是由内容页面的 body 的高度决定的,那就想办法让内容页面的 body 的高度永远不会超过 window 的高度就不会触发这个问题了。
那就把内容页面中的内容移动到一个绝对定位的元素里面,这个绝对定位的元素显示为全屏。
修正过的内容页面的内容如下:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<style>
#content-wrapper {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  overflow: hidden;
}
</style>
<script type="text/javascript">
  var addcontent = function() {
document.getElementById('content-wrapper').appendChild(document.getElementById('test').cloneNode(true));
  };
</script>
<div id="content-wrapper">
  <button onclick="addcontent()">Add content</button>
  <div id="test">
    <p>test content</p>
    <p>test content</p>
    <p>test content</p>
    <p>test content</p>
    <p>test content</p>
    <p>test content</p>
  </div>  
</div>
</body>
</html>