让 iFrame 元素与内容页面高度相同的方法

iFrame 元素的尺寸与内容页面尺寸大小相同是一个非常常见的需求,但是实际实现起来是非常麻烦的,昨天维护博客留言板的时候顺把留言板改成了有留言就自动调整 iFrame 大小的样子,其中遇到了几个技术点,考虑到可能有遇到类似需求的朋友,所以做一个记录方便查阅 (<ゝω・)☆

解决这个问题的主要技术难点包括:

  • 框内和框外的跨域通讯问题,当二者不处于同一域下不能直接操作彼此的元素或者读取信息;
  • 当框内文档大小发生变化的时候如何进行捕捉。

为了解决这两个问题,我们使用两个API:

  • 利用 postMessage API 来确保跨域也可以进行通信;
  • 利用 Mutation Observer 捕捉文档结构的变化。

代码

框内(假设页面长度由 #abcde 决定):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//确定影响页面长度的元素
const $heightElement = document.querySelector('#abcde');

//判断页面是否处于 iFrame 中
const inIframe = window.location !== window.parent.location;

//如果处于 iFrame 中,那么初始化观察器

if (inFrame) {
// lastHeight 是上次 DOM 树变动之后页面的长度
let lastHeight = 0;
// elementHeight 被设计为本次 DOM 树变动之后页面的长度
let elementHeight = -1;

//实例化一个观察器
MutationObserver = window.MutationObserver;

documentObserver = new MutationObserver(() => {
// 获得元素高度
elementHeight = $heightElement.scrollHeight;

// 如果元素高度发生了变化
if lastHeight != elementHeight) {
// 那么,先清空观察队列 (可选)
documentObserver.takeRecords();
// 利用 Post Message API 向上层发送元素高度消息
window.parent.postMessage({documentHeight: elementHeight}, '*');
}
});

// 观察 DOM 元素的哪些变动
documentObserverConfig = {
attributes: true,
childList: true,
characterData: true,
subtree: true
};

// 开始观察 DOM 树
documentObserver.observe(document.body, documentObserverConfig);
}

框外(假设 iFrame 元素的选择器为 #ghijk):

1
2
3
4
5
6
7
8
9
10
11
// 找到 iFrame 元素
$iFrame = document.querySelector('#ghijk');

//监听来自 iFrame 发来的消息
window.addEventListener('message', (e) => {
// 如果消息中有数据,且数据中包含 documentHeight
if (e.data && e.data.documentHeight){
// 改变 iFrame 元素高度
$iFrame.style.height = `${e.data.documentHeight}px`;
}
}, false);

如果你希望确保发来的信息的确是来自目标 iFrame 而不是什么其他的页面,我们可以通过事件回调传入的 e.origin 进行判断,它看起来长这样:

1
2
3
4
// 假设会向父级页面传参的 iFrame 为 https://example.com/
if (e.origin === 'https://example.com'){
console.log('(〃∀〃)');
}

以上就是本次的介绍,Happy Coding ヽ( ° ▽°)ノ