搜尋
首頁web前端js教程詳解JavaScript事件機制相容性解決方案的程式碼圖文

本文的解決方案可以用在Javascript native物件和宿主物件(dom元素),透過以下的方式來綁定和觸發事件

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函數

為native物件新增事件的過程主要在after函數中完成,這個函數主要做了以下幾件事:

  1. 如果obj中已有回應函數,將其替換成dispatcher函數

  2. 使用鍊式結構,保證多次綁定事件函數的順序執行

  3. #傳回一個handle對象,呼叫remove方法可以移除本次事件綁定

#下圖為after函數呼叫前後onlog函數的參考

    (呼叫前)
  1. (呼叫後)

    詳細解釋請看
  2. 註釋
  3. ,希望讀者能夠跟著運行一遍

    var after = function(target, method, cb, originalArgs){
        var existing = target[method];
        var dispatcher = existing;
        if (!existing || existing.target !== target) {
            //如果target中没有method方法,则为他添加一个方法method方法
            //如果target已经拥有method方法,但target[method]中target不符合要求则将method方法他替换
            dispatcher = target[method] = function(){
                //由于js是此法作用域:通过阅读包括变量定义在内的数行源码就能知道变量的作用域。
                //局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的
                //所以在这个函数中可以访问到dispatcher变量
                var results = null;
                var args = arguments;
                if (dispatcher.around) {//如果原先拥有method方法,先调用原始method方法
                    //此时this关键字指向target所以不用target
                    results = dispatcher.around.advice.apply(this, args);
                }
    
                if (dispatcher.after) {//如果存在after链则依次访问其中的advice方法
                    var _after = dispatcher.after;
                    while(_after && _after.advice) {
                        //如果需要原始参数则传入arguments否则使用上次执行结果作为参数
                        args = _after.originalArgs ? arguments : results;
                        results = _after.advice.apply(this, args);
                        _after = _after.next;
                    }
                }
            }
    
            if (existing) {
            //函数也是对象,也可以拥有属性跟方法
            //这里将原有的method方法放到dispatcher中
                dispatcher.around = {
                    advice: function(){
                        return existing.apply(target, arguments);
                    }
                }
            }
            dispatcher.target = target;
        }
    
        var signal = {
            originalArgs: originalArgs,//对于每个cb的参数是否使用最初的arguments
            advice: cb,
            remove: function() {
                if (!signal.advice) {
                    return;
                }
                //remove的本质是将cb从函数链中移除,删除所有指向他的链接
                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加入到链式结构中,处理指针关系
            while(previous && previous.next && (previous = previous.next)){};
            previous.next = signal;
            signal.previous = previous;
        } else {//如果是第一次使用调用after方法,则dispatcher的after属性指向signal
            dispatcher.after = signal;
        }
    
        cb = null;//防止内存泄露
        return signal;
    }

    解決兼容性

IE瀏覽器

從IE9開始已經支援DOM2

事件處理

程序,但是對於舊版的ie瀏覽器,任然使用attachEvent方式來為dom元素添加事件。在曙光來臨之前,仍然需要對那些不支援

DOM2級事件處理程序的瀏覽器進行相容性處理,通常需要處理以下幾點:

多次綁定一個事件,事件處理函數的呼叫順序問題

事件處理函數中的this關鍵字指向問題

標準化event事件對象,支援常用的事件屬性

由於使用attachEvent方法添加事件處理函數無法保證事件處理函數的呼叫順序,所以我們棄用attachEvent,轉而用上文中的after生成的正序鍊式結構來解決這個問題。

本文也是透過這種方式解決此問題

//1、统一事件触发顺序
    function fixAttach(target, type, listener) {
    debugger;
        var listener = fixListener(listener);
        var method = 'on' + type;
        return after(target, method, listener, true);
    };

對於事件物件的標準化,我們需要將ie提供給我們的現有屬性轉換為標準的事件屬性。

在_preventDefault、_stopPropagation、_stopImmediatePropagation三個函數中我們,如果被呼叫則listener執行完後使用一個變數保存event物件(見fixListener),以便後序事件處理程序根據event物件屬性進行下一步處理。 stopImmediatePropagation函數,對於這個函數的模擬,我們同樣透過閉包來解決。

注意這裡不能直接寫成這個形式,上文中fixListener也是同樣道理。

需要注意一點,我們將event標準化目的還有一點,可以在emit方法中設定參數來控制事件過程,例如:
Evt. emit(input, 'click');//不冒泡

###Evt.emit(input, 'click', {bubbles: true});//冒泡#######根據我的測試使用fireEvent方式觸發事件,無法設定{bubbles:false}來阻止冒泡,所以這裡我們用Javascript來模擬冒泡過程。同時在這個過程中也要保證event物件的唯一性。 ###
//1、统一事件触发顺序
    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){
            //每次调用listenser之前都会调用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) {
                // 在整个函数链执行完成后将lastEvent回归到原始状态,
                //利用异步队列,在主程序执行完后再执行事件队列中的程序代码
                //常规的做法是在emit中判断lastEvent并设为null
                //这充分体现了js异步编程的优势,把变量赋值跟清除代码放在一起,避免逻辑分散,缺点是不符合程序员正常思维方式
                if(!lastEvent){
                    setTimeout(function(){
                        lastEvent = null;
                    });
                }
                lastEvent = e;
            }
            return results;
        }
    }
###附上完整代碼:###
function _fixEvent(evt, sender){
        if (!evt) {
            evt = window.event;
        }
        if (!evt) { // emit没有传递事件参数,或者通过input.onclick方式调用
            return evt;
        }
        if(lastEvent && lastEvent.type && evt.type == lastEvent.type){
        //使用一个全局对象来保证在冒泡过程中访问的是同一个event对象
        //chrome中整个事件处理过程event是唯一的
            evt = lastEvent;
        }
        var fixEvent = evt;
        // bubbles 和cancelable根据每次emit时手动传入参数设置
        fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false;
        fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true;
        fixEvent.currentTarget = sender;
        if (!fixEvent.target){ // 多次绑定统一事件,只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.php.cn/
            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.php.cn/
            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;
    }
###腦圖:######KityMinder under BSD License . Powered by f-cube, FEX | Source Bug | Contact Us#### ## 以上就是######詳解JavaScript事件機制相容性解決方案的程式碼圖文的內容,更多相關內容請關注PHP中文網(www.php.cn)! ######
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。