jQuery的事件系统给每个通过jQuery.event.add()方式绑定事件的DOM对象生成了下面这种结构
从这张图中可以看出,我们除了可以给元素添加像click mouseover mouseout这样的原生事件之外
还可以添加selfEvent这样的自定义事件
自定义事件需要手动通过trigger触发
jQuery.macros = { each: { bind: function( type, fn ) { if ( fn.constructor == String ) fn = new Function("e", ( !fn.indexOf(".") ? "$(this)" : "return " ) + fn); jQuery.event.add( this, type, fn ); }, unbind: function( type, fn ) { jQuery.event.remove( this, type, fn ); }, trigger: function( type, data ) { jQuery.event.trigger( type, data, this ); } } };
很容易看出bind和unbind底层还是调用了add和remove方法
再来看扩展的一些更顶层的方法
new function(){ var e = ("blur,focus,load,resize,scroll,unload,click,dblclick," + "mousedown,mouseup,mousemove,mouseover,mouseout,change,reset,select," + "submit,keydown,keypress,keyup,error").split(","); for ( var i = 0; i < e.length; i++ ) new function(){ var o = e[i]; jQuery.fn[o] = function(f){ return f ? this.bind(o, f) : this.trigger(o); }; jQuery.fn["un"+o] = function(f){ return this.unbind(o, f); }; jQuery.fn["one"+o] = function(f){ return this.each(function(){ var count = 0; jQuery.event.add( this, o, function(e){ if ( count++ ) return; return f.apply(this, [e]); }); }); }; }; }
类似$("#div1").click(function(){})这种方法也可以很明显看到click方法实际上是调用了bind方法,bind最后还会调用add方法去添加事件
而$("#div1").click()就是手动触发绑定的事件,click里面通过判断是否有参数采取不同的操作
但是类似unclick这个方法貌似是不能移除绑定的匿名方法的
类似oneclick的这种方法是在调用add的时候又包装了一层,通过count来保证了它只调用一次
_toggle: jQuery.fn.toggle, toggle: function(a,b) { return a && b && a.constructor == Function && b.constructor == Function ? this.click(function(e){ this.last = this.last == a ? b : a; e.preventDefault(); return this.last.apply( this, [e] ) || false; }) : this._toggle.apply( this, arguments ); },
toggle实在没什么好说的,源码很简单
hover: function(f,g) { function handleHover(e) { var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget; while ( p && p != this ) p = p.parentNode; if ( p == this ) return false; return (e.type == "mouseover" ? f : g).apply(this, [e]); } return this.mouseover(handleHover).mouseout(handleHover); },
hover里面处理了原生的onmouseover和onmouseout事件里面的一个问题
通过一个例子说明问题:
<script> $(function(){ $("#div1").hover(function(){ $(this).css("background","#f00"); },function(){ $(this).css("background","#0f0"); }); }); </script>
<div id="div1"> <div id="div2"> <div id="div3"></div> </div> </div>
这个代码明显是希望移入#div1的时候变成红色,移出的时候变成绿色
以移出(onmouseout)为例,从#div1移出的时候如果移到了#div1外面肯定是会触发onmouseout事件的
但是移到#div2或#div3的话也会触发onmouseout事件的
而后一种情况往往是不希望出现的
也就是说移动到#div1的子元素上我们不希望触发onmouseout事件
在hover内部我们可以看到当触发onmouseout事件的时候(e.type为"mouseout")变量p存储的是e.toElement||e.relatedTarget(这种写法必定是为了兼容了)
从toElement名字上可以猜测,p存储的应该是移动到了哪个元素上面,在这个例子中p可能是document.body,可能是#div2,可能是#div3
接下来是一个while循环,看当前的p是不是等于this,this即触发onmouseout的元素,在这里自然是#div1
第一次循环不论是移动到了document.body上、还是#div2上、还是#div3上都不会等于this
所以执行循环体里面的内容p=p.parentNode
接下来的循环我们通过分析可以明白:如果p是document.body的话循环会直接结束
而且往下走也不会满足p==this的条件,那就直接执行我们mouseout绑定的函数
如果p是#div2或#div3的时候p最终会指向#div1
再往下走就满足了p==this的条件,从而直接return false回去
什么都不执行
mouseout到此为止就分析完了,mouseover和它同理