惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
S
SegmentFault 最新的问题
博客园 - 司徒正美
aimingoo的专栏
aimingoo的专栏
U
Unit 42
GbyAI
GbyAI
B
Blog RSS Feed
博客园 - Franky
L
LangChain Blog
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
Google Developers Blog
Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 空明流光

c# 正则替换与代码动态替换 正则断言备忘 c# await 异步编程工具类 windows 上 使用 conda 以 指定环境 启动 自定义程序 WPF 数据绑定通过 ElementName 失效后改为 Reference 正常 c# 获取当前时间 svn 忽略选项还原 android 应用分身无限多开,开源免费应用 使用 chrome 调试 android webview 前端 dom script C# 文件同步写入锁定 sql server 折腾时不小心去掉了 sysadmin 权限 wpf 中显示约束只创建一个窗口实例 sqlcipher/sqlite 加解密 Xamarin Android 动态修改菜单颜色 XABLD7000: Xamarin.Tools.Zip.ZipException: Renaming temporary file failed: Permission denied .net8 项目指定包还原位置和读取位置以便备份依赖项 wpf 访问内置资源 C# UTF-8 各种编码比较 vs2022 wpf 无法识别 xaml 中定义的控件名称 wpf 使用 cursor 命令 wpf datagrid 过滤数据两种方式?
与跨域 iframe 通信示例
空明流光 · 2025-11-08 · via 博客园 - 空明流光

定义一个桥接类

class IframeBridge {
    constructor(iframe, targetOrigin = '*') {
        this.iframe = iframe;
        this.targetOrigin = targetOrigin;
        this.pendingRequests = new Map(); // 存储进行中的请求
        this.setupMessageListener();
    }

    setupMessageListener() {
        window.addEventListener('message', (event) => {
            // 安全检查:验证来源[1,3](@ref)
            if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) {
                return;
            }

            const message = event.data;

            // 检查是否为响应消息,并且有对应的请求ID
            if (message?._type === 'RESPONSE' && message?.requestId) {
                const pendingRequest = this.pendingRequests.get(message.requestId);

                if (pendingRequest) {
                    // 从等待列表中移除
                    this.pendingRequests.delete(message.requestId);
                    
                    if (message.success) {
                        // 成功则解析Promise
                        pendingRequest.resolve(message.data);
                    } else {
                        // 失败则拒绝Promise
                        pendingRequest.reject(new Error(message.error || 'Request failed'));
                    }
                }
            }
        });
    }

    async request(action, data = {}, timeout = 5000) {

        // 生成唯一请求ID[7](@ref)
        const requestId = this.generateRequestId();
        
        return new Promise((resolve, reject) => {
            // 设置超时
            const timeoutId = setTimeout(() => {
                this.pendingRequests.delete(requestId);
                reject(new Error('Request timeout'));
            }, timeout);

            // 存储resolve和reject以便后续使用
            this.pendingRequests.set(requestId, { 
                resolve: (responseData) => {
                    clearTimeout(timeoutId);
                    resolve(responseData);
                }, 
                reject: (error) => {
                    clearTimeout(timeoutId);
                    reject(error);
                }
            });

            // 向iframe发送消息[2,5](@ref)
            this.iframe.contentWindow.postMessage({
                _type: 'REQUEST',
                requestId,
                action,
                data
            }, this.targetOrigin);
        });
    }

    generateRequestId() {
        return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    }
}

View Code

Iframe 绑定事件

window.addEventListener('message', async (event) => {

    const message = event.data;
    
    // 处理请求类型的消息
    if (message?._type === 'REQUEST') {
        let responseData;
        let success = true;
        
        try {
            // 根据不同的action执行相应操作
            switch (message.action) {
                case 'GET_USER_DATA':
                    //responseData = await getUserData(message.data.userId);
                    responseData = "I am user " + message.data.userId;
                    break;
                case 'GET_USER_SETTINGS':
                    //responseData = await getUserSettings();
                    responseData = "I am UserSettingsxxxx";
                    break;
                default:
                    throw new Error(`未知操作: ${message.action}`);
            }
        } catch (error) {
            responseData = { error: error.message };
            success = false;
        }
        
        // 回复消息,带回请求ID[3](@ref)
        event.source.postMessage({
            _type: 'RESPONSE',
            requestId: message.requestId, // 必须带回原请求ID
            success,
            data: responseData
        }, event.origin);
    }
});

View Code

所有页面(包括 iframe)绑定事件(CS程序可以强制注入所有 html 页面)

window.addEventListener('load', function(){
    var iframes = document.getElementsByTagName('iframe');
    for (var i = 0; i < iframes.length; i++) {
        var iframe = iframes[i];
        var bridge = new IframeBridge(iframe);
        iframe.bridge = bridge;
    }
});

递归在所有嵌套 iframe 中执行代码返回多个结果,过滤出自己想要的结果

// 使用async/await进行通信
async function test() {
    var iframes = document.getElementsByTagName('iframe');
    for (var i = 0; i < iframes.length; i++) {
        var iframe = iframes[i];
        var bridge = iframe.bridge;
        
        if(!bridge)
            continue;
        
        try {
            console.log('请求用户数据...');
            // 代码会在此"等待"直到收到响应
            const userData = await bridge.request('GET_USER_DATA', { userId: 123 });
            console.log('收到数据:', userData);
            
            // 继续执行后续逻辑
            const settings = await bridge.request('GET_USER_SETTINGS');
            console.log('用户设置:', settings);
            
        } catch (error) {
            console.error('通信失败:', error.message);
        }
    }
    
}