李恒道 发表于 2022-5-11 09:19:38

[脚本实战篇]实现iframe通信

# 前言
我们之前已经实现了视频的静默播放以及结束播放的检测
现在我们开始尝试编写播放结束后自动关闭对话框
先回忆一下之前的代码
```javascript
            vdobj.hook('setup', function(player) {
                console.log('setup',player)
                // 绑定结束事件
                player.on("ended", function() {
                  console.log('播放完毕')
                });

            });
```
根据内容来看,红色的是我们的视频
可以看到是一个iframe
![图片.png](data/attachment/forum/202205/11/090834l1insy109s1g1t1g.png)
那我们可以编写代码
使用postmessage进行通信
unsafeWindow.parent.postMessage('lhd_close')
同时在外部的iframe里进行监听,我们直接写在之前的OpenOriginDialog函数中
```javascript
function OpenOriginDialog(Content){
    return new Promise((resolve,reject)=>{
      unsafeWindow.require(["Play"],function(Play){
            let courseId=unsafeWindow._courseId;
            let userId=unsafeWindow._userId;
            let companyCode=unsafeWindow._companyCode;
            const ListenMessage= (e)=> {
                if(e.data==='lhd_close'){
                  unsafeWindow.removeEventListener('message', ListenMessage)
                  //关闭窗口代码
                  resolve()
                }
            }
            unsafeWindow.addEventListener('message', ListenMessage);
            Play.dialog({
                //唯一ID
                id:"videoBox-link",
                data:{
                  url:Content.fullResUrl,
                  companyCode:companyCode,//三方公司id
                  resId:Content.id,
                  type:Content.mediaType,
                  userId:userId,
                  courseId:courseId,
                  title:Content.title+'-李恒道破解'
                },
                //弹出框宽度
                width:"auto",
                //弹出框高度
                height:(screen.availHeight-200) + "px",
                //是否开启打点功能
                isTicker:true
            });
      })
    })
}
```
这样就完成了
整理一下
大概逻辑是这样的
在调用打开对话框函数之后
我们做了以下几件事
1.对message进行监听,如果找到lhd_close则关闭对话框,并解除监听
2.打开对话框
3.对话框会加载一个iframe,在适当时机发送lhd_close让父级关闭对话框
那么我们的嵌套层级大概是这样的
![图片.png](data/attachment/forum/202205/11/090905xizmftn5787ovo6i.png)
但是根据我的测试,
青色的在不同的地址里可能出现第三次嵌套
所以我们需要对复杂环境进行判断
这里我直接编写了一个小函数
```
function EmitParentClose(){
    let parentif=unsafeWindow.parent
    while(parentif!==unsafeWindow.top){
      parentif.postMessage('lhd_close')
      parentif=parentif.parent
    }
}
```
因为第一级也就是黄色部分,根据测试是没有任何用的
所以我们可以不对他发送
青色内部的不管多少层的iframe嵌套,如果触发退出时间
都会一直向上追溯直到完毕,每一层iframe都发送一次消息
那我们的通信部分就完毕了
接下来我们研究一下怎么关闭这个窗口
还是老规矩。搜一下标题class
![图片.png](data/attachment/forum/202205/11/091521otwncrwroq3csnws.png)
我们找到了这里
![图片.png](data/attachment/forum/202205/11/091543j5p5s5ve5i855pzm.png)
发现了
```javascript
                //当弹出框隐藏时删除弹出框
                $('#'+dialogId).on('hidden.bs.modal', function () {
                        $('#'+dialogId).remove();
                })
```
也就是说当隐藏的之后直接调用remove函数
那dialogId是什么呢?
全局搜索一下找到了
var dialogId="dialog-myModal"+config.id+cacheNum;
前边的是固定的,后边是自动变化的
也就是说我们可以写一个css语句
document.querySelector('')?.remove()
这里用了id的前缀匹配,同时?是为了在找不到元素的情况下不调用remove,防止报错
那么我们再看一下OpenOriginDialog代码
```javascript
function OpenOriginDialog(Content){
    return new Promise((resolve,reject)=>{
      unsafeWindow.require(["Play"],function(Play){
            let courseId=unsafeWindow._courseId;
            let userId=unsafeWindow._userId;
            let companyCode=unsafeWindow._companyCode;
            const ListenMessage= (e)=> {
                if(e.data==='lhd_close'){
                  unsafeWindow.removeEventListener('message', ListenMessage)
                  document.querySelector('')?.remove()
                  resolve()
                }
            }
            unsafeWindow.addEventListener('message', ListenMessage);
            Play.dialog({
                //唯一ID
                id:"videoBox-link",
                data:{
                  url:Content.fullResUrl,
                  companyCode:companyCode,//三方公司id
                  resId:Content.id,
                  type:Content.mediaType,
                  userId:userId,
                  courseId:courseId,
                  title:Content.title+'-李恒道破解'
                },
                //弹出框宽度
                width:"auto",
                //弹出框高度
                height:(screen.availHeight-200) + "px",
                //是否开启打点功能
                isTicker:true
            });
      })
    })
}
```
我们完成了对话框的打开关闭,以及子iframe的消息监听
而子video的代码也非常简单
我们只需要一个小修改
```javascript
            function EmitParentClose(){
                let parentif=unsafeWindow.parent
                while(parentif!==unsafeWindow.top){
                  parentif.postMessage('lhd_close')
                  parentif=parentif.parent
                }
            }
            ----以下为video的结束钩子代码---
            
            vdobj.hook('setup', function(player) {
                player.on("ended", function() {
                  EmitParentClose()
                });
            });
```
触发关闭条件的时候直接调用EmitParentClose函数即可。
# 结语
那么我们这节课就完成了iframe的监听
撒花~

王一之 发表于 2022-5-11 09:27:32

woc,生产队的驴!

cxxjackie 发表于 2022-5-11 11:49:01

能不能addStyle让对话框一直display: none呢?感觉这样更简单可靠一点,其实postMessage的问题挺多的,如果message在前,脚本注入在后,就会出现收不到的情况,使用的时候可能需要加延迟,或者控制脚本运行时机等等,还有如果网页本身有对message做监听,还需要避免冲突,挺麻烦的,我更倾向于能不做跨域通信就不做。

李恒道 发表于 2022-5-11 12:38:07

cxxjackie 发表于 2022-5-11 11:49
能不能addStyle让对话框一直display: none呢?感觉这样更简单可靠一点,其实postMessage的问题挺多的,如果 ...

这个网页搞的太烂...
我依赖他的大概逻辑做的
如果对话框display:none的话到下个还会继续创建
这不是最离谱的
我怀疑他好像有内存泄漏...
调试的时候发现他的post堆栈有一百多层requirejs?????

李恒道 发表于 2022-5-11 12:42:34

cxxjackie 发表于 2022-5-11 11:49
能不能addStyle让对话框一直display: none呢?感觉这样更简单可靠一点,其实postMessage的问题挺多的,如果 ...

我上面刚说完那些话
果真出问题了....
videojs时间没写好...我在搞一下

cxxjackie 发表于 2022-5-11 22:00:33

李恒道 发表于 2022-5-11 12:38
这个网页搞的太烂...
我依赖他的大概逻辑做的
如果对话框display:none的话到下个还会继续创建


我是说加一条css:
{display:none !important;}
这样他创建几个都没用吧,css还是方便的。
页: [1]
查看完整版本: [脚本实战篇]实现iframe通信