jQuery 源码分析(十七) 事件系统模块 实例方法和便捷方法 详解

实例方法和便捷方法是指jQuery可以直接通过链接操作的方法,是通过调用$.event上的方法(上一节介绍的底层方法)来实现的,常用的如下:

  • on(types,selector,data,fn,one)  ;为匹配元素集合中的每个元素绑定一个或多个类型的事件监听函数
    • types          ;事件类型字符串,多个事件类型之间用空格隔开
    • selector      ;可选,是一个选择器表达式字符串,用于绑定代理事件。
    • data            ;传递给事件监听函数的自定义数据,可以是任何类型。
    • fn           ;待绑定的监听函数
    • one                  ;该事件是否只执行一次,为方法.one()提供支持

     writer by:大沙漠 QQ:22969969

  • off(types,selector,fn)  ;移除匹配元素中每个元素上绑定的一个或多个类型的监听函数,参数如下:
    • types               ;一个或多个以空格分隔的事件类型和可选的命名空间
    • selector           ;可选的选择器表达式字符串,用于移除代理事件
    • fn                    ;待移除的监听函数,可以设置为false,表示内部定义的只返回false的函数
  • off(types,selector,fn)   ;移除匹配元素中每个元素上绑定的一个或多个类型的监听函数
    • types        ;一个或多个以空格分隔的事件类型和可选的命名空间
    • selector    ;可选的选择器表达式字符串,用于移除代理事件
    • fn          ;待移除的监听函数,可以设置为false
  • bind(types,data,fn)            ;绑定一个普通事件
  • trigger(type, data)                  执行每个匹配元素上绑定的监听函数和默认行为,并模拟冒泡过程
  • one(types,selector,data,fn)    ;为匹配元素集合中的每个元素绑定最多执行一次的事件监听函数
  • hover(fnOver, fnOut)              ;用于在匹配元素上绑定一个或两个监听函数,当鼠标指针进入和离开时,绑定的监听函数被执行

我们还是以上一节的实例为例,用实例方法改写一下,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
    <style>div{width: 200px;padding-top:50px;height: 150px;background: #ced;}div button{margin:0 auto;display: block;}</style>
</head>
<body>
    <div>        
        <button id="button">按钮1</button>    
    </div>
    <script>
        $("div").on('click',()=>console.log('div普通单击事件'));
        $('div').on('click','button',()=>console.log('d1代理事件'))
    </script>
</body>
</html>

渲染如下:

和上一节一样,我们在div上绑定了一个普通事件和代理事件,当点击div时触发普通事件,点击按钮时分别触发普通事件和代理事件。

另外为了更方变使用事件,jQuery还定义了很多的便捷事件方法,可以直接在jQuery实例上调用,注意:便捷方法不能绑定代理事件,只能绑定普通事件,所有的便捷方法如下:

blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu

我们将上面的例子改写一下,用便捷方法来实现,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
    <style>div{width: 200px;padding-top:50px;height: 150px;background: #ced;}div button{margin:0 auto;display: block;}</style>
</head>
<body>
    <div>        
        <button id="button">按钮1</button>    
    </div>
    <script>
        $("div").click(()=>console.log('div普通单击事件'));                //用便捷事件来实现
        $('div').on('click','button',()=>console.log('d1代理事件'))        //代理事件不能用便捷方法来操作,因此我们用实例方法来实现
    </script>
</body>
</html>

效果和上面是一样的。

 

源码分析


实例方法是定义在jQuery.fn上的,on主要对参数做一些判断,以支持多种格式的调用方法,实现如下:

jQuery.fn.extend({

    on: function( types, selector, data, fn, /*INTERNAL*/ one ) {    //该方法主要是修正参数。为匹配元素集合中的每个元素绑定一个或多个类型的事件监听函数。
        var origFn, type;

        // Types can be a map of types/handlers                            //如果types是对象时,即参数格式是.on(Object,selector,data,one)或.one(Object,data,one)则
        if ( typeof types === "object" ) {
            // ( types-Object, selector, data )
            if ( typeof selector !== "string" ) {
                // ( types-Object, data )
                data = selector;
                selector = undefined;
            }
            for ( type in types ) {                                            //遍历参数types,递归调用方法.on(types,selector,data,fn,one)绑定事件。
                this.on( type, selector, data, types[ type ], one );
            }
            return this;
        }

        if ( data == null && fn == null ) {                                //如果没有参数3、4,则认为格式是.on(types,fn)
            // ( types, fn )
            fn = selector;                                                    //把第二个参数修正为fn。
            data = selector = undefined;
        } else if ( fn == null ) {                                        //传入了三个参数时
            if ( typeof selector === "string" ) {                            //如果第二个参数是字符串,则认为格式是:.on(types,selector,fn)    忽略参数data,并把第三个参数作为参数fn。
                // ( types, selector, fn )
                fn = data;
                data = undefined;
            } else {                                                        //否则则认为忽略参数selector,并把第而个参数作为参数data,并把第三个参数作为参数fn。格式是.on(types,data,fn)
                // ( types, data, fn )
                fn = data;
                data = selector;
                selector = undefined;
            }
        }
        if ( fn === false ) {                                            //如果参数fn是布尔值false,则把它修正为总返回false的函数returnFalse()。
            fn = returnFalse;
        } else if ( !fn ) {                                                //如果fn没有值则直接返回。
            return this;
        }

        if ( one === 1 ) {                                                //当方法one()调用.on()时,该参数为1,就会把监听函数fn重新封装为一个只会执行一次的新监听函数。
            origFn = fn;
            fn = function( event ) {
                // Can use an empty set, since event contains the info
                jQuery().off( event );
                return origFn.apply( this, arguments );
            };
            // Use same guid so caller can remove using origFn
            fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
        }
        return this.each( function() {                                    //遍历当前的this
            jQuery.event.add( this, types, fn, data, selector );            //调用add()绑定事件
        });    
    },    
    one: function( types, selector, data, fn ) {                        //为匹配元素集合中的每个元素绑定一个或多个类型的事件监听函数,每个监听函数在每个匹配元素上最多执行一次。该方法简单的通过调用.on(types,selector,data,fn,one)来实现。
        return this.on.call( this, types, selector, data, fn, 1 );
    },
    /**/
})

对于便捷方法来说,他就是在$.fn上定义每一个属性,值为一个函数,内部还是调用$.fn.on来实现添加事件的,如下:

jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
    "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
    "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {    //参数1是一个数组 参数2是个函数,其中name是值,比如blur、focus

    // Handle event binding
    jQuery.fn[ name ] = function( data, fn ) {            //初始化事件便捷方法,在jQuery.fn对象上添加元素,这样jQuery实例就可以直接访问了
        if ( fn == null ) {                                    //修正参数,如果只传入一个参数,则把该参数视为fn参数,把data视为null
            fn = data;
            data = null;
        }

        return arguments.length > 0 ?                        //根据参数个数决定是绑定事件还是触发事件
            this.on( name, null, data, fn ) :                     //如果参数个数大于1,则调用方法.on()绑定事件监听函数
            this.trigger( name );                                 //如果没有参数,则调用方法.trigger()触发事件监听函数和默认行为
    };

    if ( jQuery.attrFn ) {                                    //记录事件便捷方法名,在调用jQuery.attr()读取或设置HTML属性时,如果属性名与事件便捷方法名同名,则会改为调用同名的事件便捷方法a
        jQuery.attrFn[ name ] = true;
    }

    if ( rkeyEvent.test( name ) ) {
        jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
    }

    if ( rmouseEvent.test( name ) ) {
        jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
    }
});

可以看到,如果执行便捷方法时不传递参数将触发该事件,例如:$('div').click()将会触发在该div上绑定的普通事件。

posted @ 2019-10-30 07:33  大沙漠  阅读(446)  评论(0编辑  收藏  举报