从一个bug说jquery的事件注册和触发机制

来源:互联网 发布:易语言硬件断点源码 编辑:程序博客网 时间:2024/06/06 07:31

问题描述

同一域名下有两个页面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’)。

分析

猜测了几个原因未果后,决定还是看下源码找问题。

  • bind方法对应jQuery.event.add。
  • trigger方法应jQuery.event.trigger,并最终落实到jQuery.event.handle执行。

我们来看看这两个方法吧(只摘说明问题需要的代码)

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原因

从以上分析不难看出,导致我们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上,问题得以解决。

0 0
原创粉丝点击