zoukankan      html  css  js  c++  java
  • JQuery实现click事件绑定与触发方法分析

    原生JS通过什么方法绑定click事件?

      原生js有一下三种方法为DOM对象绑定click事件,

      第一种,在html中添加 onclick属性,在此属性中添加要绑定的事件函数,如下, 这种方法为html处理事件的原始方法,使得html和js过分耦合, 即表现层代码 和 行为层代码耦合:

    <html>
    <head>
        <script src="./jquery.js"></script>
    </head>
    <body>
        <div name="template">
            <input type="button" name="testBtn" value="click me" onclick="sayHi()">
        </div>
        <script>
            function sayHi()
            {
                alert("hi")
            }
        </script>
    </body>
    </html>

      第二种方法, 在js中找到DOM对象, 为onclick属性动态绑定一个处理函数。 在html中不声明事件处理, 在js中定位到dom后动态执行click事件处理函数,如下, 好处是解耦, 修改dom的行为只需要修改一处js代码即可:

    <html>
    <head>
        <script src="./jquery.js"></script>
    </head>
    <body>
        <div name="template">
            <input type="button" id="testBtn" value="click me">
        </div>
        <script>
            function sayHi()
            {
                alert("hi")
            }
            document.getElementById("testBtn").onclick=sayHi;
        </script>
    </body>
    </html>

      第三种方法, 是使用通用的事件绑定函数, 将一个事件动态绑定处理函数,如下,这种方法可以自定义事件, 并配合dispatchEvent可以使得事件冒泡(https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events):

    <html>
    <head>
        <script src="./jquery.js"></script>
    </head>
    <body>
        <div name="template">
            <input type="button" id="testBtn" value="click me">
        </div>
        <script>
            function sayHi()
            {
                alert("hi")
            }
            document.getElementById("testBtn").addEventListener("click",sayHi);
        </script>
    </body>
    </html>

    JQuery click 实现分析

    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 ) {
    
        // Handle event binding
        jQuery.fn[ name ] = function( data, fn ) {
            return arguments.length > 0 ?
                this.on( name, null, data, fn ) :
                this.trigger( name );
        };
    });

    首先, click作为JQuery对象的方法, 实际上集成其原型对象的方法fn集合, 其中有一个函数名称叫 click, 

    当click函数调用没有参数,表示触发此事件,对应this.trigger( name )

    当click参数为函数,则将此函数绑定到此对象的click事件上, 对应了 this.on( name, null, data, fn )。

    其次,分析下 this.on的实现:

    对于click绑定一个事件处理函数的情况, 相关代码:

    jQuery.fn.extend({
    
        on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
            var type, origFn;
    。。。
            return this.each( function() {
                jQuery.event.add( this, types, fn, data, selector );
            });
        },

    on绑定是调用 jQuery.event.add 实现,

    下面分析 jQuery.event.add 对于 click绑定一个函数的实现情况。

    解释入下面代码注释, 当事件发生, 会触发 公共事件处理函数 elemData.handle, 在此函数中, 会将当前的事件调用dispatch函数,分发到对应的elem对象上。

    总结下:

    (1) on的实现通过jquery.event.add, 将事件公用处理函数elemData.handle,通过addEventListener绑定到具体的事件类型上,

    (2)并将待触发的函数, 存储于 handlers == elemData.events[type]

    jQuery.event = {
    
        global: {},
    
        add: function( elem, types, handler, data, selector ) {
            var tmp, events, t, handleObjIn,
                special, eventHandle, handleObj,
                handlers, type, namespaces, origType,
                elemData = jQuery._data( elem );
    。。。。
    // 给每一个需要绑定的事件处理函数,定义一个唯一的ID
    // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; }     //定义事件集合,存储于 elem -》data的 events 对象中 // Init the element's event structure and main handler, if this is the first if ( !(events = elemData.events) ) { events = elemData.events = {}; }
        // 定义elem对象的 事件处理函数, 存储于elem-》data的 handle成员中, 为一个函数
            if ( !(eventHandle = elemData.handle) ) {
                eventHandle = elemData.handle = function( e ) {
                    // Discard the second event of a jQuery.event.trigger() and
                    // when an event is called after a page has unloaded
                    return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
                        jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
                        undefined;
                };
                // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
                eventHandle.elem = elem;
            }
    。。。。。。
                // Init the event handler queue if we're the first
                if ( !(handlers = events[ type ]) ) {
                    handlers = events[ type ] = [];
                    handlers.delegateCount = 0;

            // 将定义的事件处理函数, 绑定到具体的事件类型
                    // Only use addEventListener/attachEvent if the special events handler returns false
                    if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
                        // Bind the global event handler to the element
                        if ( elem.addEventListener ) {
                            elem.addEventListener( type, eventHandle, false );

                        } else if ( elem.attachEvent ) {
                            elem.attachEvent( "on" + type, eventHandle );
                        }
                    }
                }
    。。。。。
          // 将待绑定的click参数(响应函数)存储到handlers中, handlers == elemData.events[type]
                // Add to the element's handler list, delegates in front
                if ( selector ) {
                    handlers.splice( handlers.delegateCount++, 0, handleObj );
                } else {
                    handlers.push( handleObj );
                }

    下面看下trigger事件函数如何执行?

    将按照事件名,构建冒泡路径, 依次冒泡执行 addEventListener绑定的事件公共处理函数。

        trigger: function( event, data, elem, onlyHandlers ) {
    。。。。。。
                         // 按照DOM树向上构造 冒泡 路径
                for ( ; cur; cur = cur.parentNode ) {
                    eventPath.push( cur );
                    tmp = cur;
                }    
    ..................
                // jQuery handler
                handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
                if ( handle ) {
                    handle.apply( cur, data );
                }

                // Native handler
                handle = ontype && cur[ ontype ];
                if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
                    event.result = handle.apply( cur, data );
                    if ( event.result === false ) {
                        event.preventDefault();
                    }
                }

    此函数(elemData.handle)中会调用dispatch,将所有的handlers中相同类型事件调用一遍

    下面是dispatch的关键代码。

        dispatch: function( event ) {
                handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
    。。。。。。
            // Determine handlers
            handlerQueue = jQuery.event.handlers.call( this, event, handlers );
    ,,,,,
                while ( (handleObj = matched.handlers[ j++ ]) && !event.
                        ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
                                .apply( matched.elem, args );
    。。。。。。。

     实现思路-描述代码:

    给事件绑定公共函数,

    将注册事件函数存储到数组中,

    公共函数中,对数组中的注册函数依次调用。

    <html>
    <head>
        <script src="./jquery.js"></script>
    </head>
    <body>
        <div name="template">
            <input type="button" id="testBtn" value="click me">
        </div>
        <script>
            /* core code start */
            var eventHandlers = [];
            function clickHandle()
            {
                //触发每个绑定函数
                for (var i in eventHandlers)
                {
                    eventHandlers[i]();
                }
            }
            function addHandle(fn)
            {
                eventHandlers.push(fn);
            }
            /* core code end */
            
            //绑定函数1 绑定
            function sayHi()
            {
                alert("hi");
            }
            addHandle(sayHi);
            
            //绑定函数2 绑定
            function sayHello()
            {
                alert("hello");
            }
            addHandle(sayHello);
            
            //绑定公共处理函数
            document.getElementById("testBtn").addEventListener("click",clickHandle);
            
            //脚本 - 触发公共处理函数
            var clickfn = document.getElementById("testBtn")["click"];
            console.log(clickfn)
            clickfn.apply(document.getElementById("testBtn"))
        </script>
    </body>
    </html>
  • 相关阅读:
    Android开发环境配置
    Spring API后端原理及最佳实践
    Hibernate 编程
    MySQL 远程访问
    MySQL 5.7 8.0 重置密码
    H5 流媒体
    你不知道的项目
    Promise
    Why Vuex
    Vue 技术细节
  • 原文地址:https://www.cnblogs.com/lightsong/p/4034109.html
Copyright © 2011-2022 走看看