同一域名下有两个页面parent.html,child.html。parent.html中通过iframe嵌入child.html。父页面触发自定义事件,子页面对其进行响应。 两页面代码如下: 1.parent.html
<body>
我是父页面
<iframe></iframe>
<script src="/js/jquery.min.js"></script>
<script>
$("iframe").attr('src', 'child.html');
/*0.5s后触发自定义事件myevent*/
setTimeout(function(){
$(document).trigger('myevent');
}, 500);
</script>
</body>
2.child.html
<body>
我是子页面
<script src="/js/jquery.min.js"></script>
<script>
/*响应父页面myevent*/
$(parent.document).bind("myevent",function(){
console.log('i get it')
})
</script>
</body>
运行后,并没有在控制台输出”i get it”。也就是说子页面未能响应父页面的trigger(‘myevent’)。
猜测了几个原因未果后,决定还是看下源码找问题。
我们来看看这两个方法吧(只摘说明问题需要的代码)
jQuery.event = {
/*
* elem:dom元素
* types:事件
* handler:处理函数
*/
add : function(elem, types, handler, data){
/*给处理函数指定唯一id*/
handler.guid = this.guid++;
/*从缓存中取出已有处理函数*/
events = jQuery.data(elem, "events");
handlers = events[type];
/*以唯一id为key,存入新的处理函。*/
handlers[handler.guid] = handler;
},
handle : function(event){
/*取出dom元素上的所的事件处理函数, 顺次执行*/
handlers = (jQuery.data(this, "events") || {})[event.type];
for (var j in handlers){
...
}
}
}
两个方法均使用到了jQuery.data, 此函数只是拿来作缓存之用,所有数据存到了jQuery.cache。cache就是jQuery的一个内部变量,被初始化为{}。
jQuery.extend({
cache: {},
data: function(elem, name, data) {
jQuery.cache[id][name] = data;
}
})
注:以上代码摘自jquery-1.2.6版本,新版本代码要复杂一些,但实现机制类似。
至此,我们可以总结jQuery的事件注册/触发机制如下: - 对元素进行事件绑定(bind/on)时,事件会以elem->handles的kv对记录在内部缓存jQuery.cache中。 - 触发事件时,从cache中查找该元素对应的所有事件,依次执行。
从以上分析不难看出,导致我们bug的原因如下: - 子页面的jQuery和父页面的jQuery是功能相同的两个不同对象。就像双胞胎,外表一致,内里却不尽相同。 - 子页面的myevent处理函数保存在了子页面的jQury.cache中 - 父页面的jQury.cache上没有myevent处理函数,触发时当然也不会有调用。
在jQuery内部代码的add和trigger中加log也可以看出这一点
将child.html中的js代码改为
parent.$(parent.document).bind("myevent",function(){
console.log('i get it')
});
如此一来,父子页面的事件触发,子页面的事件响应作用在了父页面的jQuery.cache上,问题得以解决。