在网页中根据 iframe 的内容自动调节其高度
<iframe>
(Inline Frame 的缩写)是 HTML 中的内联框架元素。它可以在一个 HTML 文档中嵌入另一个独立的 HTML 文档。因此常常用于在网页中嵌入外部内容。但是,由于在渲染父网页时,浏览器并不知道嵌入其中的iframe的高度,因此默认情况下,无法根据 iframe 的内容自动调节其高度。实际中,可以通过多种方法,利用 Javascript实现。本文介绍使用 postMessage
来实现。
postMessage
简介 🔗
postMessage
是 HTML5 引入的一种跨源(cross-origin)通信机制。它允许在不同源的窗口(window 对象)、文档(document 对象)、框架(iframe)之间安全地传递消息。
基本用法 🔗
- 发送消息:
otherWindow.postMessage(message, targetOrigin)
- 接收消息:在接收消息的窗口中,需要添加一个
message
事件监听器来处理接收到的消息。
自动调节 iframe 的高度 🔗
在子页面发送网页高度 🔗
在子 HTML 页面(即 iframe 指向的页面)中,重置其样式,然后使用 postMessage
发送高度给父 HTML 页面。简化的示例如下:
<html lang="zh-CN" dir="ltr">
<head>
<style type="text/css">
html,
body {
overflow: hidden;
}
body {
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
const e = document.getElementById("main");
parent.postMessage({ height: e.clientHeight }, "*");
});
</script>
</head>
<body>
<div id="main">
<!-- 在此添加网页内容 -->
</div>
</body>
</html>
在父页面监听高度变化 🔗
在父页面中监听 message
事件,调整 iframe 的高度。简化的示例如下:
<script type="text/javascript">
const iframeSrc = "http://localhost:8080/html"; // 子页面的地址
const element = document.getElementById("iframe-container");
var iframe = document.createElement("iframe");
iframe.setAttribute("src", iframeSrc);
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "0");
element.replaceChildren(iframe); // 将 `#iframe-container` 的内容替换为 iframe。
window.addEventListener(
"message",
function (event) {
const eventData = event.data;
// 出于安全考虑,在此处检查事件的 origin
if (event.origin === "http://localhost:8080") {
const height = eventData.height;
iframe.height = height + "px";
}
},
false,
);
</script>
<!-- iframe 容器 -->
<div id="iframe-container">
<!-- 在加载 iframe 之前的内容(可选) -->
<div>加载中...</div>
</div>
以上使用的是 replaceChildren
修改 iframe 容器里的内容。也可以按需使用 replaceWith
、appendChild
等方法修改页面。但是需要注意的是,在一些老的浏览器中,可能不支持 replaceChildren
方法,此时会遇到类似 replaceChildrenis is not a function
的错误。如果非要使用 replaceChildren
,那么可以使用如下的迂回方法:
<script type="text/javascript">
const iframeSrc = "http://localhost:8080/html"; // 子页面的地址
const element = document.getElementById("iframe-container");
const iframe = document.createElement("iframe");
iframe.id = "iframe-child";
iframe.setAttribute("src", iframeSrc);
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "0");
if (event.replaceChildren) {
element.replaceChildren(iframe); // 将 `#iframe-container` 的内容替换为 iframe。
} else {
// 通过修改元素的 innerHTML 来修改其内容
element.innerHTML = "";
element.innerHTML = iframe.outerHTML;
}
const child = document.getElementById(iframe.id);
window.addEventListener(
"message",
function (event) {
const eventData = event.data;
// 出于安全考虑,在此处检查事件的 origin
if (event.origin === "http://localhost:8080") {
const height = eventData.height;
child.height = height + "px";
}
},
false,
);
</script>
<!-- iframe 容器 -->
<div id="iframe-container">
<!-- 在加载 iframe 之前的内容(可选) -->
<div>加载中...</div>
</div>
处理子页面中的图片 🔗
以上介绍的方法,只能适用于固定高度页面的内容。由于图片是异步加载的,如果子页面中包含图片资源的话,在 DOMContentLoaded
事件后直接获取页面元素的高度时,此时图片可能还没有加载,取得的高度就是错误的。解决方法是在图片加载后,再次更新高度。为此,可使用如下的代码,在子页面中发送页面高度给父页面:
<script type="text/javascript">
(function () {
function notify() {
const e = document.getElementById("main");
parent.postMessage({ height: e.clientHeight }, "*");
}
document.addEventListener("DOMContentLoaded", function () {
notify();
const images = document.querySelectorAll("img");
for (var i = 0; i < images.length; i++) {
const img = images[i];
img.addEventListener("load", notify);
if (img.complete) {
notify();
}
}
});
})();
</script>
测试 🔗
代码开发完成后,务必在不同的浏览器(包括手机端浏览器)进行测试,确保这种方法能够兼容足够的使用场景,才算开发完成。
其他方法 🔗
处理自适应高度的 iframe 并没有唯一的方法,也没有一锤定音的方法。如果在某些设备或者浏览器上的效果不理想,那么就要进行调试,或者使用其他的方法或者现成工具。在 Stack Overflow 上还可以找到其他方法: