在网页中根据 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 上还可以找到其他方法: