场景介绍
一个页面需要打开另一个页面,并且两个页面之间还需要通信。为了方便叙述我们将前一个页面称为父页面后一个页面称为子页面,父页面需要知道子页面是否关闭(换句话就是子页面关闭的时候需要通知父页面),以方便父页面根据子页面的状态来做一下操作(比如是打开子页面还是和已经打开的子页面进行交互)。.
关键代码
父页面 p.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery-1.11.1.min.js"></script>
</head>
<body>
<a >AAA</a>
<a >BBB</a>
<a >CCC</a>
<script>
window.closeWindowCb = function () {
window.childrenWindow = undefined;
};
$('a').click(function () {
var dom = $(this);
var text = dom.html();
if (window.childrenWindow === undefined) {
window.childrenWindow = window.open('./c.html');
// 子页面加载完成后执行回调
window.childrenWindow.onload = function () {
window.childrenWindow.initChildrenPage(text, function (body) {
var a = '';
});
}
}
else {
window.childrenWindow.changeChildrenPage(text, function (body) {
var a = '';
});
}
});
</script>
</body>
</html>
子页面 c.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>child</title>
<script src="jquery-1.11.1.min.js"></script>
<script>
window.initChildrenPage = function (text, cb) {
document.body.innerHTML = text;
cb(document.body);
};
window.changeChildrenPage = function (text, cb) {
document.body.innerHTML += ' - ' + text;
cb(document.body);
};
$(window).bind('beforeunload', function () {
window.opener.closeWindowCb();
// 可以更具业务逻辑定制,但是用户的决定无法拿到
// return "您之前的操作尚未保存";
});
</script>
</head>
<body>
</body>
</html>
原理简介
父页面定义一个全局变量来存储子页面的打开状态,window.open
方法执行后会将子页面的window
返回,方便我们来记录子页面的打开情况,并且为调用子页面的方法提供便利。
window.childrenWindow = window.open('./c.html');
然后就可以监听子元素的 onload
事件,事件触发时调用子页面的初始化函数,在父页面监听而不在子页面中监听的好处是可以将父页面中的信息作为参数传递给子页面。
window.childrenWindow.onload = function () {
window.childrenWindow.initChildrenPage(text, function (body) {
var a = '';
});
}
子页面通信父页面通过 window.opener
来拿到父页面的 window
对象并调用父页面的全局方法。
不完美的地方
- 全局变量满天飞,注意覆盖;
- 如果子页面想提示用户是否关闭,js 无法拿到用户的最后决定。
Demo
postMessage 方案
父页面向子页面发送 message
父页面打开子页面。
var popup = window.open(/* popup details */);
父页面向子页面发送消息
const data = {};
popup.postMessage(data);
子页面接收消息,触发自己的逻辑
window.addEventListener('message', event => {
// 数据获取方式
event.data
});
子页面向父页面发送 message
父页面先定义监听,于子页面的监听类似:
window.addEventListener('message', event => {
// 数据获取方式
event.data.customId
event.data.name
});
子页面向父页面发送消息:
window.opener.postMessage({
customId: 'aaaaa',
name: 'bbbbbbb'
});
注意的点
需要注意的是,子页面刷新后依然可以获取到父页面,并且可以接收到父页面的消息,父页面刷新后无法找到存放在内存中的 popup 变量。
原文地址: https://github.com/xiaoqiang-zhao/my-cellar/blob/master/web/articles/browser-tab/main.md