欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-20 16:54:46
实例方法和便捷方法是指jQuery可以直接通过链接操作的方法,是通过调用$.event上的方法(上一节介绍的底层方法)来实现的,常用的如下: on(types,selector,data,fn,one) ;为匹配元素集合中的每个元素绑定一个或多个类型的事件监听函数 types ;事件类型字符串,多个 ......

实例方法和便捷方法是指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>

渲染如下:

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

和上一节一样,我们在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上绑定的普通事件。