MutationObserver
记录项目中用到MutationObserver的一个场景。
应用场景:
- App中用WebView显示一个本地html文件
- html文件下载JavaScript然后用JSONP显示一个网页
- 这个网页中实际内容是用iframe显示的一个表单
- 表单中有一个Close按钮
- 需求就是点击Close按钮的时候关掉App中引入WebView的页面
简单来说就是想捕捉iframe里面的Close按钮的点击事件。
因为捕捉到了点击事件就可以把这个事件传入到App层,App就可以关闭页面。
尝试解决以及遇到的问题:
最开始我尝试用jQuery为close按钮加上click的点击事件的监听。
代码如下:
$('.cancel').click(function() {
window.postMessage('close', '*')
})
一般情况(*下面会稍稍尝试解释下一般情况是什么情况*)下这么做是没问题的,但对我来说不能用。我先说为什么我不能用这个方法。
我项目中html的结构是这样:
* 本地HTML
** 一个container div
** container div里面有一个iframe 显示了具体表单内容 (JSONP运行之后的结果)
这样以来,我访问container div没问题,因为这些div是被JSONP直接追加到我本地的HTML的DOM中的
有问题的是,我访问不了container div里面的iframe的内容,因为iframe的src是一个远程服务器上的页面,比如 当你在本地一个html文件里面写上:
<iframe id="baidu" src="http://www.baidu.com" height="800" width="800">
</iframe>
在你的HTML文件里你是访问不到这个iframe里面的内容的。
我们知道访问一个iframe的document对象是这样操作:
var document = IFRAME.contentDocument || IFRAME.contentWindow.contentDocument
但当我访问contentDocument对象的时候我遇到了这个:
frame.contentWindow.contentDocument
VM107:1 Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame.
at <anonymous>:1:21
刚开始很不解,我都已经加载了这个页面了 我为什么不能修改他的内容?
后来我想可能是这样,如果可以任意修改iframe里面的内容的话 我可以把Baidu的logo改成任意其他的图片,然后供其他人访问这个页面,这样看起来太不合适了 可能这就是上面提出的 “cross-origin" 即跨域调用的安全性问题。
* 什么是“一般情况”?如果要显示的html文件不是在本地而是在服务器上,比如百度做一个新版首页,而新版首页想要直接加载这个页面那肯定是没问题的,因为他们不是跨域调用。
用MutationObserver解决:
因为点击Close按钮会把iframe从页面中移除,所以,用MutationObserver解决非常合适。
代码简单,如下:
var target = document.querySelector('#container')
var observer = new MutationObserver(function(mutations) { // 设定监听回调函数
if (!$('#container').children().length) {
window.postMessage('close', '*')
console.log('Iframe was closed')
}
});
observer.observe(target, {
childList: true,
})
这样以来,我们对container进行监听,我们设定了监听他的childList,即当他的子节点有变动的时候 会触发我们设定的监听回调函数。在这个函数中我们去看container的子节点数量,如果变为0了说明Close按钮被点击过了。当然,点击Submit按钮也会移除iframe,那么这个回调也会被执行。