iframe
通信 - MessageChannel
<!-- index.html -->
<h3>MessageChannel</h3>
<input id="input" type="text" oninput="handleInput(this.value)" />
<hr />
<iframe src="./demo.html"></iframe>
<script>
// 定义通信实例
const { port1, port2 } = new MessageChannel();
// 保存到全局
[window.port1, window.port2] = [port1, port2];
const input = document.getElementById('input');
// port1监听port2发送的信息
window.port1.onmessage = (e) => input.value = e.data;
// port1发送信息port2接收信息
const handleInput = value => window.port1.postMessage(value);
</script>
<!-- demo.html -->
<input id="input" type="text" oninput="handleInput(this.value)" />
<script>
const input = document.getElementById('input');
// port2监听port1发送的信息
top.port2.onmessage = (e) => input.value = e.data;
// port2发送信息port1接收信息
const handleInput = value => top.port2.postMessage(value);
</script>
跨窗口通信核心源码
实现方式
- 通过监听本地存储实现
window.addEventListener("storage", (e) => { /* code... */ })
- 通过广播通信实现 -
BroadcastChannel
new BroadcastChannel('xxx')
本地存储实现
<label for="input">
<span>本地存储实现 - localStorage:</span>
<input id="input" type="text" oninput="handleInput(this.value)" />
</label>
<script>
const input = document.getElementById('input');
// 初始化表单数据
input.value = localStorage.getItem('value') || '';
// 监听表单输入(保存通信数据到本地存储中)
const handleInput = value => localStorage.setItem('value', value);
// 监听本地存储
window.addEventListener("storage", (e) => e.key === 'value' && (input.value = e.newValue));
</script>
广播通信实现 - BroadcastChannel
<label for="input">
<span>广播通信实现 - BroadcastChannel:</span>
<input id="input" type="text" oninput="handleInput(this.value)" />
</label>
<script>
const input = document.getElementById('input');
// 定义通信实例
const broadcastChannel = new BroadcastChannel('value');
// 监听通信
broadcastChannel.onmessage = (e) => input.value = e.data;
// 监听表单输入(发送通信数据)
const handleInput = value => broadcastChannel.postMessage(value);
</script>
跨窗口通信示例1 - 矩形在不同窗口拖拽穿梭
部分位置、宽高属性含义
实现效果(关闭开发者模式
)
源码1
<style>
.cube {
position: fixed;
width: 400px;
height: 400px;
}
</style>
<div class="cube"></div>
<script>
const cube = document.querySelector('.cube');
const barHeight = window.outerHeight - window.innerHeight;
cube.style.backgroundColor = new URLSearchParams(location.search).get('color') || 'red';
// 窗口坐标转屏幕坐标
function winToScreenPosition(x, y) {
return [x + window.screenX, y + window.screenY + barHeight];
}
// 屏幕坐标转窗口坐标
function screenToWinPosition(x, y) {
return [x - window.screenX, y - window.screenY - barHeight];
}
// 监听本地存储
window.addEventListener("storage", (e) => {
if(e.key === 'position') {
const position = JSON.parse(e.newValue);
const [x, y] = screenToWinPosition(...position);
cube.style.left = `${x}px`;
cube.style.top = `${y}px`;
}
});
// 鼠标按下
cube.onmousedown = (e) => {
// 鼠标在cube内的x、y坐标
const [cubeX, cubeY] = [e.pageX - cube.offsetLeft, e.pageY - cube.offsetTop];
// 鼠标移动
window.onmousemove = (e) => {
// 计算出矩形左上角相对页面的位置
const [x, y] = [e.pageX - cubeX, e.pageY - cubeY];
cube.style.left = `${x}px`;
cube.style.top = `${y}px`;
// 保存相对于屏幕的坐标
localStorage.setItem('position', JSON.stringify(winToScreenPosition(x, y)));
}
// 鼠标抬起
window.onmouseup = () => {
window.onmousemove = null;
window.onmouseup = null;
};
};
</script>
源码2
<style>
.cube {
position: fixed;
width: 400px;
height: 400px;
}
</style>
<div class="cube"></div>
<script>
const broadcastChannel = new BroadcastChannel('position');
const cube = document.querySelector('.cube');
const barHeight = window.outerHeight - window.innerHeight;
cube.style.backgroundColor = new URLSearchParams(location.search).get('color') || 'red';
// 窗口坐标转屏幕坐标
function winToScreenPosition(x, y) {
return [x + window.screenX, y + window.screenY + barHeight];
}
// 屏幕坐标转窗口坐标
function screenToWinPosition(x, y) {
return [x - window.screenX, y - window.screenY - barHeight];
}
broadcastChannel.onmessage = (e) => {
const position = e.data;
const [x, y] = screenToWinPosition(...position);
cube.style.left = `${x}px`;
cube.style.top = `${y}px`;
}
// 鼠标按下
cube.onmousedown = (e) => {
// 鼠标在cube内的x、y坐标
const [cubeX, cubeY] = [e.pageX - cube.offsetLeft, e.pageY - cube.offsetTop];
// 鼠标移动
window.onmousemove = (e) => {
// 计算出矩形左上角相对页面的位置
const [x, y] = [e.pageX - cubeX, e.pageY - cubeY];
cube.style.left = `${x}px`;
cube.style.top = `${y}px`;
// 发送相对于屏幕的坐标
broadcastChannel.postMessage(winToScreenPosition(x, y));
}
// 鼠标抬起
window.onmouseup = () => {
window.onmousemove = null;
window.onmouseup = null;
};
};
</script>
跨窗口通信示例2 - 新建页面时新建矩形,相对于屏幕位置不变
部分位置、宽高属性含义
实现效果(关闭开发者模式
)
源码
<style>
.cube {
width: 200px;
height: 200px;
position: fixed;
border: 1px solid red;
}
</style>
<script>
// 导航栏高度
const barHeight = window.outerHeight - window.innerHeight;
// 窗口坐标转屏幕坐标
const winToScreenPosition = (x, y) => [x + window.screenX, y + window.screenY + barHeight];
// 屏幕坐标转窗口坐标
const screenToWinPosition = (x, y) => [x - window.screenX, y - window.screenY - barHeight];
// 渲染元素
const rendererElement = (cubes) => {
// 每次渲染清空页面
document.body.innerHTML = `<pre>${JSON.stringify(cubes)}</pre>`;
// 循环渲染元素
cubes.forEach(d => {
const cube = document.createElement('div');
cube.setAttribute('class', 'cube');
const [x, y] = screenToWinPosition(...d.position);
cube.innerText = `(${x}, ${y})`;
cube.style.left = `${x}px`;
cube.style.top = `${y}px`;
document.body.appendChild(cube);
});
// 动画-监听窗体移动
requestAnimationFrame(() => rendererElement(cubes));
}
// 获取cube数据
let cubes = JSON.parse(localStorage.getItem('cubes')) || [];
// 定义唯一标识,每次新建页面创建全新的唯一标识
let id = cubes.length && cubes[cubes.length - 1].id;
id++;
// 当前cube的信息
const cube = { id, position: winToScreenPosition(window.innerWidth / 2 - 100, window.innerHeight / 2 - 100) };
// cube数据
cubes.push(cube);
// 保存cube数据
localStorage.setItem('cubes', JSON.stringify(cubes));
// 渲染元素
rendererElement(cubes);
// 监听本地存储
window.addEventListener('storage', (e) => {
if (e.key === 'cubes') {
cubes = JSON.parse(e.newValue) || [];
rendererElement(cubes);
}
});
// 监听页面关闭(包括页面刷新)
window.addEventListener('beforeunload', (e) => localStorage.setItem('cubes', JSON.stringify(cubes.filter(d => d.id !== id))));
// 监听页面尺寸变化(不包含浏览器窗口的位置的监听)
window.addEventListener('resize', (e) => rendererElement(cubes));
</script>