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>