JQuery實現click事件綁定與觸發方法分析

原生JS經過什麼方法綁定click事件?

  原生js有一下三種方法爲DOM對象綁定click事件,html

  第一種,在html中添加 onclick屬性,在此屬性中添加要綁定的事件函數,以下, 這種方法爲html處理事件的原始方法,使得html和js過度耦合, 即表現層代碼 和 行爲層代碼耦合:jquery

<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):app

<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, dom

當click函數調用沒有參數,表示觸發此事件,對應this.trigger( name )ide

當click參數爲函數,則將此函數綁定到此對象的click事件上, 對應了 this.on( name, null, data, fn )。函數

 

其次,分析下 this.on的實現:ui

對於click綁定一個事件處理函數的狀況, 相關代碼:this

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 實現,spa

下面分析 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>
相關文章
相關標籤/搜索