原生js實現on與off 方法

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

實現方式

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

一個空殼子

首先經過一個空架子來了解下實現邏輯,核心實如今getEventObject() 方法內的包裝函數git

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方法,傳入參數github

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

調用removeEvent()方法解除事件綁定前端框架

完整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()使用事件代理。 事件代理是一種事件預綁定機制, 該機制能夠在事件節點不存在的狀況下, 將事件綁定至已存在父級節點之上的方式。app

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

另推薦個表格組件gridManagerdom

相關文章
相關標籤/搜索