前端基础(三十八):iframe通信、浏览器跨窗口通信

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>

跨窗口通信核心源码

实现方式

  1. 通过监听本地存储实现
    • window.addEventListener("storage", (e) => { /* code... */ })
  2. 通过广播通信实现 - 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>
  • 18
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Prosper Lee

您的赏赐将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值