jQuery源码学习10——和事件相关的方法

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和它同理

原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/4997409.html