本篇內(nèi)容主要講解“Javascript事件機制兼容性解決方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Javascript事件機制兼容性解決方法是什么”吧!
創(chuàng)新互聯(lián)專注于淇縣網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供淇縣營銷型網(wǎng)站建設(shè),淇縣網(wǎng)站制作、淇縣網(wǎng)頁設(shè)計、淇縣網(wǎng)站官網(wǎng)定制、成都微信小程序服務(wù),打造淇縣網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供淇縣網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
本文的解決方案可以用于Javascript native對象和宿主對象(dom元素),通過以下的方式來綁定和觸發(fā)事件:
或者
var input = document.getElementsByTagName('input')[0]; var form = document.getElementsByTagName('form')[0]; Evt.on(input, 'click', function(evt){ console.log('input click1'); console.log(evt.target === input); console.log(evt.modified); //evt.stopPropagation(); console.log(evt.modified); }); var handle2 = Evt.on(input, 'click', function(evt){ console.log('input click2'); console.log(evt.target === input); console.log(evt.modified); }); Evt.on(form, 'click', function(evt){ console.log('form click'); console.log(evt.currentTarget === input); console.log(evt.target === input); console.log(evt.currentTarget === form); console.log(evt.modified); }); Evt.emit(input, 'click'); Evt.emit(input, 'click', {bubbles: true}); handle2.remove(); Evt.emit(input, 'click');
After函數(shù)
為native對象添加事件的過程主要在after函數(shù)中完成,這個函數(shù)主要做了以下幾件事:
如果obj中已有響應(yīng)函數(shù),將其替換成dispatcher函數(shù)
使用鏈式結(jié)構(gòu),保證多次綁定事件函數(shù)的順序執(zhí)行
返回一個handle對象,調(diào)用remove方法可以去除本次事件綁定
下圖為after函數(shù)調(diào)用前后onlog函數(shù)的引用
(調(diào)用前)
(調(diào)用后)
詳細解釋請看注釋,希望讀者能夠跟著運行一遍
var after = function(target, method, cb, originalArgs){ var existing = target[method]; var dispatcher = existing; if (!existing || existing.target !== target) { //如果target中沒有method方法,則為他添加一個方法method方法 //如果target已經(jīng)擁有method方法,但target[method]中target不符合要求則將method方法他替換 dispatcher = target[method] = function(){ //由于js是此法作用域:通過閱讀包括變量定義在內(nèi)的數(shù)行源碼就能知道變量的作用域。 //局部變量在聲明它的函數(shù)體內(nèi)以及其所嵌套的函數(shù)內(nèi)始終是有定義的 //所以在這個函數(shù)中可以訪問到dispatcher變量 var results = null; var args = arguments; if (dispatcher.around) {//如果原先擁有method方法,先調(diào)用原始method方法 //此時this關(guān)鍵字指向target所以不用target results = dispatcher.around.advice.apply(this, args); } if (dispatcher.after) {//如果存在after鏈則依次訪問其中的advice方法 var _after = dispatcher.after; while(_after && _after.advice) { //如果需要原始參數(shù)則傳入arguments否則使用上次執(zhí)行結(jié)果作為參數(shù) args = _after.originalArgs ? arguments : results; results = _after.advice.apply(this, args); _after = _after.next; } } } if (existing) { //函數(shù)也是對象,也可以擁有屬性跟方法 //這里將原有的method方法放到dispatcher中 dispatcher.around = { advice: function(){ return existing.apply(target, arguments); } } } dispatcher.target = target; } var signal = { originalArgs: originalArgs,//對于每個cb的參數(shù)是否使用最初的arguments advice: cb, remove: function() { if (!signal.advice) { return; } //remove的本質(zhì)是將cb從函數(shù)鏈中移除,刪除所有指向他的鏈接 var previous = signal.previous; var next = signal.next; if (!previous && !next) { dispatcher.after = signal.advice = null; dispatcher.target = null; delete dispatcher.after; } else if (!next){ signal.advice = null; previous.next = null; signal.previous = null; } else if (!previous){ signal.advice = null; dispatcher.after = next; next.previous = null; signal.next = null; } else { signal.advice = null; previous.next = next; next.previous = previous; signal.previous = null; signal.next = null; } } } var previous = dispatcher.after; if (previous) {//將signal加入到鏈式結(jié)構(gòu)中,處理指針關(guān)系 while(previous && previous.next && (previous = previous.next)){}; previous.next = signal; signal.previous = previous; } else {//如果是***次使用調(diào)用after方法,則dispatcher的after屬性指向signal dispatcher.after = signal; } cb = null;//防止內(nèi)存泄露 return signal; }
解決兼容性
IE瀏覽器從IE9開始已經(jīng)支持DOM2事件處理程序,但是對于老版本的ie瀏覽器,任然使用attachEvent方式來為dom元素添加事件。值得慶幸的是微軟已宣布2016年將不再對ie8進行維護,對于廣大前端開發(fā)者無疑是一個福音。然而在曙光來臨之前,仍然需要對那些不支持DOM2級事件處理程序的瀏覽器進行兼容性處理,通常需要處理以下幾點:
多次綁定一個事件,事件處理函數(shù)的調(diào)用順序問題
事件處理函數(shù)中的this關(guān)鍵字指向問題
標準化event事件對象,支持常用的事件屬性
由于使用attachEvent方法添加事件處理函數(shù)無法保證事件處理函數(shù)的調(diào)用順序,所以我們棄用attachEvent,轉(zhuǎn)而用上文中的after生成的正序鏈式結(jié)構(gòu)來解決這個問題。
//1、統(tǒng)一事件觸發(fā)順序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); };
對于事件處理函數(shù)中的this關(guān)鍵字指向,通過閉包即可解決(出處),如:
本文也是通過這種方式解決此問題
//1、統(tǒng)一事件觸發(fā)順序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); }; function fixListener(listener) { return function(evt){ //每次調(diào)用listenser之前都會調(diào)用fixEvent debugger; var e = _fixEvent(evt, this);//this作為currentTarget if (e && e.cancelBubble && (e.currentTarget !== e.target)){ return; } var results = listener.call(this, e); if (e && e.modified) { // 在整個函數(shù)鏈執(zhí)行完成后將lastEvent回歸到原始狀態(tài), //利用異步隊列,在主程序執(zhí)行完后再執(zhí)行事件隊列中的程序代碼 //常規(guī)的做法是在emit中判斷l(xiāng)astEvent并設(shè)為null //這充分體現(xiàn)了js異步編程的優(yōu)勢,把變量賦值跟清除代碼放在一起,避免邏輯分散,缺點是不符合程序員正常思維方式 if(!lastEvent){ setTimeout(function(){ lastEvent = null; }); } lastEvent = e; } return results; } }
對于事件對象的標準化,我們需要將ie提供給我們的現(xiàn)有屬性轉(zhuǎn)化為標準的事件屬性。
function _fixEvent(evt, sender){ if (!evt) { evt = window.event; } if (!evt) { // emit沒有傳遞事件參數(shù),或者通過input.onclick方式調(diào)用 return evt; } if(lastEvent && lastEvent.type && evt.type == lastEvent.type){ //使用一個全局對象來保證在冒泡過程中訪問的是同一個event對象 //chrome中整個事件處理過程event是***的 evt = lastEvent; } var fixEvent = evt; // bubbles 和cancelable根據(jù)每次emit時手動傳入?yún)?shù)設(shè)置 fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false; fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true; fixEvent.currentTarget = sender; if (!fixEvent.target){ // 多次綁定統(tǒng)一事件,只fix一次 fixEvent.target = fixEvent.srcElement || sender; fixEvent.eventPhase = fixEvent.target === sender ? 2 : 3; if (!fixEvent.preventDefault) { fixEvent.preventDefault = _preventDefault; fixEvent.stopPropagation = _stopPropagation; fixEvent.stopImmediatePropagation = _stopImmediatePropagation; } //參考:http://www.nowamagic.net/javascript/js_EventMechanismInDetail.php if( fixEvent.pageX == null && fixEvent.clientX != null ) { var doc = document.documentElement, body = document.body; fixEvent.pageX = fixEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); fixEvent.pageY = fixEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); } if (!fixEvent.relatedTarget && fixEvent.fromEvent) { fixEvent.relatedTarget = fixEvent.fromEvent === fixEvent.target ? fixEvent.toElement : fixEvent.fromElement; } // 參考: http://www.cnblogs.com/hsapphire/archive/2009/12/18/1627047.html if (!fixEvent.which && fixEvent.keyCode) { fixEvent.which = fixEvent.keyCode; } } return fixEvent; } function _preventDefault(){ this.defaultPrevented = true; this.returnValue = false; this.modified = true; } function _stopPropagation(){ this.cancelBubble = true; this.modified = true; } function _stopImmediatePropagation(){ this.isStopImmediatePropagation = true; this.modified = true; }
在_preventDefault、_stopPropagation、_stopImmediatePropagation三個函數(shù)中我們,如果被調(diào)用則listener執(zhí)行完后使用一個變量保存event對象(見fixListener),以便后序事件處理程序根據(jù)event對象屬性進行下一步處理。stopImmediatePropagation函數(shù),對于這個函數(shù)的模擬,我們同樣通過閉包來解決。
注意這里不能直接寫成這種形式,上文中fixListener也是同樣道理。
需要注意一點,我們將event標準化目的還有一點,可以在emit方法中設(shè)置參數(shù)來控制事件過程,比如:
Evt.emit(input, 'click');//不冒泡 Evt.emit(input, 'click', {bubbles: true});//冒泡
根據(jù)我的測試使用fireEvent方式觸發(fā)事件,無法設(shè)置{bubbles:false}來阻止冒泡,所以這里我們用Javascript來模擬冒泡過程。同時在這個過程中也要保證event對象的***性。
// 模擬冒泡事件 var sythenticBubble = function(target, type, evt){ var method = 'on' + type; var args = Array.prototype.slice.call(arguments, 2); // 保證使用emit觸發(fā)dom事件時,event的有效性 if ('parentNode' in target) { var newEvent = args[0] = {}; for (var p in evt) { newEvent[p] = evt[p]; } newEvent.preventDefault = _preventDefault; newEvent.stopPropagation = _stopPropagation; newEvent.stopImmediatePropagation = _stopImmediatePropagation; newEvent.target = target; newEvent.type = type; } do{ if (target && target[method]) { target[method].apply(target, args); } }while(target && (target = target.parentNode) && target[method] && newEvent && newEvent.bubbles); } var emit = function(target, type, evt){ if (target.dispatchEvent && document.createEvent){ var newEvent = document.createEvent('HTMLEvents'); newEvent.initEvent(type, evt && !!evt.bubbles, evt && !!evt.cancelable); if (evt) { for (var p in evt){ if (!(p in newEvent)){ newEvent[p] = evt[p]; } } } target.dispatchEvent(newEvent); } /*else if (target.fireEvent) { target.fireEvent('on' + type);// 使用fireEvent在evt參數(shù)中設(shè)置bubbles:false無效,所以棄用 } */else { return sythenticBubble.apply(on, arguments); } }
附上完整代碼:
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <meta http-equiv="window-target" content="_top"> <title>Writing to Same Doc</title> <script language="JavaScript"> var after = function(target, method, cb, originalArgs){ var existing = target[method]; var dispatcher = existing; if (!existing || existing.target !== target) { //如果target中沒有method方法,則為他添加一個方法method方法 //如果target已經(jīng)擁有method方法,但target[method]中target不符合要求則將method方法他替換 dispatcher = target[method] = function(){ //由于js是此法作用域:通過閱讀包括變量定義在內(nèi)的數(shù)行源碼就能知道變量的作用域。 //局部變量在聲明它的函數(shù)體內(nèi)以及其所嵌套的函數(shù)內(nèi)始終是有定義的 //所以在這個函數(shù)中可以訪問到dispatcher變量 var results = null; var args = arguments; if (dispatcher.around) {//如果原先擁有method方法,先調(diào)用原始method方法 //此時this關(guān)鍵字指向target所以不用target results = dispatcher.around.advice.apply(this, args); } if (dispatcher.after) {//如果存在after鏈則依次訪問其中的advice方法 var _after = dispatcher.after; while(_after && _after.advice) { //如果需要原始參數(shù)則傳入arguments否則使用上次執(zhí)行結(jié)果作為參數(shù) args = _after.originalArgs ? arguments : results; results = _after.advice.apply(this, args); _after_after = _after.next; } } } if (existing) { //函數(shù)也是對象,也可以擁有屬性跟方法 //這里將原有的method方法放到dispatcher中 dispatcher.around = { advice: function(){ return existing.apply(target, arguments); } } } dispatcher.target = target; } var signal = { originalArgs: originalArgs,//對于每個cb的參數(shù)是否使用最初的arguments advice: cb, remove: function() { if (!signal.advice) { return; } //remove的本質(zhì)是將cb從函數(shù)鏈中移除,刪除所有指向他的鏈接 var previous = signal.previous; var next = signal.next; if (!previous && !next) { dispatcher.after = signal.advice = null; dispatcher.target = null; delete dispatcher.after; } else if (!next){ signal.advice = null; previous.next = null; signal.previous = null; } else if (!previous){ signal.advice = null; dispatcher.after = next; next.previous = null; signal.next = null; } else { signal.advice = null; previous.next = next; next.previous = previous; signal.previous = null; signal.next = null; } } } var previous = dispatcher.after; if (previous) {//將signal加入到鏈式結(jié)構(gòu)中,處理指針關(guān)系 while(previous && previous.next && (previousprevious = previous.next)){}; previous.next = signal; signal.previous = previous; } else {//如果是***次使用調(diào)用after方法,則dispatcher的after屬性指向signal dispatcher.after = signal; } cb = null;//防止內(nèi)存泄露 return signal; } //1、統(tǒng)一事件觸發(fā)順序 //2、標準化事件對象 //3、模擬冒泡 emit時保持冒泡行為,注意input.onclick這種方式是不冒泡的 //4、保持冒泡過程中event的***性 window.Evt = (function(){ var on = function(target, type, listener){ debugger; if (!listener){ return; } // 處理stopImmediatePropagation,通過包裝listener來支持stopImmediatePropagation if (!(window.Event && window.Event.prototype && window.Event.prototype.stopImmediatePropagation)) { listener = _addStopImmediate(listener); } if (target.addEventListener) { target.addEventListener(type, listener, false); return { remove: function(){ target.removeEventListener(type, listener); } } } else { return fixAttach(target, type, listener); } }; var lastEvent; // 使用全局變量來保證一個元素的多個listenser中事件對象的一致性,冒泡過程中事件對象的一致性;在chrome這些過程中使用的是同一個event //1、統(tǒng)一事件觸發(fā)順序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); }; function fixListener(listener) { return function(evt){ //每次調(diào)用listenser之前都會調(diào)用fixEvent debugger; var e = _fixEvent(evt, this);//this作為currentTarget if (e && e.cancelBubble && (e.currentTarget !== e.target)){ return; } var results = listener.call(this, e); if (e && e.modified) { // 在整個函數(shù)鏈執(zhí)行完成后將lastEvent回歸到原始狀態(tài), //利用異步隊列,在主程序執(zhí)行完后再執(zhí)行事件隊列中的程序代碼 //常規(guī)的做法是在emit中判斷l(xiāng)astEvent并設(shè)為null //這充分體現(xiàn)了js異步編程的優(yōu)勢,把變量賦值跟清除代碼放在一起,避免邏輯分散,缺點是不符合程序員正常思維方式 if(!lastEvent){ setTimeout(function(){ lastEvent = null; }); } lastEvent = e; } return results; } } function _fixEvent(evt, sender){ if (!evt) { evt = window.event; } if (!evt) { // emit沒有傳遞事件參數(shù),或者通過input.onclick方式調(diào)用 return evt; } if(lastEvent && lastEvent.type && evt.type == lastEvent.type){ //使用一個全局對象來保證在冒泡過程中訪問的是同一個event對象 //chrome中整個事件處理過程event是***的 evt = lastEvent; } var fixEvent = evt; // bubbles 和cancelable根據(jù)每次emit時手動傳入?yún)?shù)設(shè)置 fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false; fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true; fixEvent.currentTarget = sender; if (!fixEvent.target){ // 多次綁定統(tǒng)一事件,只fix一次 fixEventfixEvent.target = fixEvent.srcElement || sender; fixEventfixEvent.eventPhase = fixEvent.target === sender ? 2 : 3; if (!fixEvent.preventDefault) { fixEvent.preventDefault = _preventDefault; fixEvent.stopPropagation = _stopPropagation; fixEvent.stopImmediatePropagation = _stopImmediatePropagation; } //參考:http://www.nowamagic.net/javascript/js_EventMechanismInDetail.php if( fixEvent.pageX == null && fixEvent.clientX != null ) { var doc = document.documentElement, body = document.body; fixEventfixEvent.pageX = fixEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); fixEventfixEvent.pageY = fixEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); } if (!fixEvent.relatedTarget && fixEvent.fromEvent) { fixEventfixEvent.relatedTarget = fixEvent.fromEvent === fixEvent.target ? fixEvent.toElement : fixEvent.fromElement; } // 參考: http://www.cnblogs.com/hsapphire/archive/2009/12/18/1627047.html if (!fixEvent.which && fixEvent.keyCode) { fixEventfixEvent.which = fixEvent.keyCode; } } return fixEvent; } function _preventDefault(){ this.defaultPrevented = true; this.returnValue = false; this.modified = true; } function _stopPropagation(){ this.cancelBubble = true; this.modified = true; } function _stopImmediatePropagation(){ this.isStopImmediatePropagation = true; this.modified = true; } function _addStopImmediate(listener) { return function(evt) { // 除了包裝listener外,還要保證所有的事件函數(shù)共用一個evt對象 if (!evt.isStopImmediatePropagation) { //evt.stopImmediatePropagation = _stopImmediateProgation; return listener.apply(this, arguments); } } } // 模擬冒泡事件 var sythenticBubble = function(target, type, evt){ var method = 'on' + type; var args = Array.prototype.slice.call(arguments, 2); // 保證使用emit觸發(fā)dom事件時,event的有效性 if ('parentNode' in target) { var newEvent = args[0] = {}; for (var p in evt) { newEvent[p] = evt[p]; } newEvent.preventDefault = _preventDefault; newEvent.stopPropagation = _stopPropagation; newEvent.stopImmediatePropagation = _stopImmediatePropagation; newEvent.target = target; newEvent.type = type; } do{ if (target && target[method]) { target[method].apply(target, args); } }while(target && (targettarget = target.parentNode) && target[method] && newEvent && newEvent.bubbles); } var emit = function(target, type, evt){ if (target.dispatchEvent && document.createEvent){ var newEvent = document.createEvent('HTMLEvents'); newEvent.initEvent(type, evt && !!evt.bubbles, evt && !!evt.cancelable); if (evt) { for (var p in evt){ if (!(p in newEvent)){ newEvent[p] = evt[p]; } } } target.dispatchEvent(newEvent); } /*else if (target.fireEvent) { target.fireEvent('on' + type);// 使用fireEvent在evt參數(shù)中設(shè)置bubbles:false無效,所以棄用 } */else { return sythenticBubble.apply(on, arguments); } } return { on: on, emit: emit }; })() </script> <style type="text/css"></style> </head> <body> <form> <input type="button" value="Replace Content" > </form> </body> </html>
到此,相信大家對“Javascript事件機制兼容性解決方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
網(wǎng)頁標題:Javascript事件機制兼容性解決方法是什么
鏈接URL:http://jinyejixie.com/article18/igogdp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號、網(wǎng)站建設(shè)、網(wǎng)站內(nèi)鏈、虛擬主機、企業(yè)建站、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)