原生js實現on與off 方法

使用過 jQuery的同窗,應該對事件綁定方法 .on() .off() 有必定的瞭解。 在我的類庫 jTool 中實現了這兩個方法,這裏就來細說下原生實現方式。

實現方式

如下爲我的類庫 jToolEvent 實現方式。
代碼中使用到一個基礎方法對象 utilities ,該對象爲 jTool 的基礎類。 若是想了解更多,能夠經過點擊進入查看原碼。

一個空殼子

首先經過一個空架子來了解下實現邏輯,核心實如今getEventObject() 方法內的包裝函數
var Event = {
    // 綁定
    on: function(event, querySelector, callback, useCapture){
        return this.addEvent(this.getEventObject(event, querySelector, callback, useCapture));
    },
    // 解除綁定
    off: function(event, querySelector) {
        return this.removeEvent(this.getEventObject(event, querySelector));
    },
    // 獲取 jTool Event 對象
    getEventObject: function (event, querySelector, callback, useCapture){
        // 事件代理實現核心
        // 注意: 這個方法爲包裝函數,此處的this爲觸發事件的Element
        var fn = callback;
            callback = function(e){
                // 驗證子選擇器所匹配的nodeList中是否包含當前事件源 或 事件源的父級
                // 注意: 這個方法爲包裝函數,此處的this爲觸發事件的Element
                var target = e.target;
                while(target !== this ){
                    if([].indexOf.call( this.querySelectorAll(querySelector), target) !== -1){
                        fn.apply(target, arguments);
                        break;
                    }
                    target = target.parentNode;
                }
            };
    
        return {
                eventName: event + querySelector,
                type: event,
                querySelector: querySelector,
                callback: callback || utilities.noop,
                useCapture: useCapture || false
            };
    },
    // 增長事件,並將事件對象存儲至DOM節點
    addEvent: function (eventList){
    },
    // 刪除事件,並將事件對象移除出DOM節點
    removeEvent: function (eventList){
    
    }
}

參數說明

  • event: 事件名, 如 'click', 並支持 'click.scope1' 事件域的方法(雖然支持,但因爲暫時沒有使用場景。因此僅對參數進行了處理,功能上沒有實現)
  • querySelector: 子選擇器
  • callback: 事件觸發後執行的函數
  • useCapture: 指定事件是否在捕獲或冒泡階段執行.true - 事件句柄在捕獲階段執行 false- 默認。事件句柄在冒泡階段執行
  • eventList: 由 .getEventObject() 方法生成的 Event 對象,

on的實現邏輯

  • 經過調用jTool實例的on方法,傳入參數
  • 調用getEventObject() 獲取事件對象
  • 調用addEvent() 方法進行事件綁定

off的實現邏輯

經過調用jTool實例的off方法,傳入參數前端

調用getEventObject()獲取事件對象node

調用removeEvent()方法解除事件綁定git

完整Event對象

var _Event = {
    on: function(event, querySelector, callback, useCapture) {
        // 將事件觸發執行的函數存儲於DOM上, 在清除事件時使用
    },
    
    off: function(event, querySelector) {
        return this.removeEvent(this.getEventObject(event, querySelector));
    },
    
    bind: function(event, callback, useCapture) {
        return this.on(event, undefined, callback, useCapture);
    },
    
    unbind: function(event) {
        return this.removeEvent(this.getEventObject(event));
    },
    // 獲取 jTool Event 對象
    getEventObject: function(event, querySelector, callback, useCapture) {
        // $(dom).on(event, callback);
        if (typeof querySelector === 'function') {
            useCapture = callback || false;
            callback = querySelector;
            querySelector = undefined;
        }
        // event callback 爲必要參數
        if (!event) {
            utilities.error('事件綁定失敗,緣由: 參數中缺失事件類型');
            return this;
        }
    
        // 子選擇器不存在 或 當前DOM對象包含Window Document 則將子選擇器置空
        if(!querySelector || utilities.type(this.DOMList[0]) !== 'element'){
            querySelector = '';
        }
        // #Event003 存在子選擇器 -> 包裝回調函數, 回調函數的參數
        // 預綁定功能實現
        if(querySelector !== ''){
            var fn = callback;
            callback = function(e){
                // 驗證子選擇器所匹配的nodeList中是否包含當前事件源 或 事件源的父級
                // 注意: 這個方法爲包裝函數,此處的this爲觸發事件的Element
                var target = e.target;
                while(target !== this ){
                    if([].indexOf.call( this.querySelectorAll(querySelector), target) !== -1){
                        fn.apply(target, arguments);
                        break;
                    }
                    target = target.parentNode;
                }
            };
        }
        var eventSplit = event.split(' ');
        var eventList = [],
            eventScopeSplit,
            eventObj;
    
        utilities.each(eventSplit, function(i, eventName) {
            if (eventName.trim() === '') {
                return true;
            }
    
            eventScopeSplit = eventName.split('.');
            eventObj = {
                eventName: eventName + querySelector,
                type: eventScopeSplit[0],
                querySelector: querySelector,
                callback: callback || utilities.noop,
                useCapture: useCapture || false,
                // TODO: nameScope暫時不用
                nameScope: eventScopeSplit[1] || undefined
            };
            eventList.push(eventObj);
        });
        return eventList;
    },
    
    // 增長事件,並將事件對象存儲至DOM節點
    addEvent: function(eventList) {
        var _this = this;
        utilities.each(eventList, function (index, eventObj) {
            utilities.each(_this.DOMList, function(i, v){
                v.jToolEvent = v.jToolEvent || {};
                v.jToolEvent[eventObj.eventName] = v.jToolEvent[eventObj.eventName] || [];
                v.jToolEvent[eventObj.eventName].push(eventObj);
                v.addEventListener(eventObj.type, eventObj.callback, eventObj.useCapture);
            });
        });
        return _this;
    },
    
    // 刪除事件,並將事件對象移除出DOM節點
    removeEvent: function(eventList) {
        var _this = this;
        var eventFnList; //事件執行函數隊列
        utilities.each(eventList, function(index, eventObj) {
            utilities.each(_this.DOMList, function(i, v){
                if (!v.jToolEvent) {
                    return;
                }
                eventFnList = v.jToolEvent[eventObj.eventName];
                if (eventFnList) {
                    utilities.each(eventFnList, function(i2, v2) {
                        v.removeEventListener(v2.type, v2.callback);
                    });
                    v.jToolEvent[eventObj.eventName] = undefined;
                }
            });
        });
        return _this;
    }
};

.on().bind()均可以進行事件綁定, 區別在於.on()使用事件代理。 事件代理是一種事件預綁定機制, 該機制能夠在事件節點不存在的狀況下, 將事件綁定至已存在父級節點之上的方式。github

隨筆一行
這是前端最好的時代, 這也是前端最壞的時代。 衆多前端框架滿天飛,隨着 jQuery 在前端行業的慢慢弱化,老是會有一種斯人遠去,何者慰籍的感受。互勉吧,各位。前端框架

另推薦個表格組件gridManagerapp

相關文章
相關標籤/搜索