據說每個大牛、小牛都應該有自己的庫——Event處理


今天抽時間寫了一部分Event處理方面的函數愈發的覺得jQuery的優秀,自己前期的想法太粗糙,造成后面這些函數參數很多,操作很很不直觀,看樣子是要重構的節奏,還好小伙兒伴們安慰,架構都是改出來的。繼續探索吧

瀏覽器兼容性

寫Event處理的庫函數一個難點就在於瀏覽器兼容性問題,在IE低版本瀏覽器中事件對象始終在window.event屬性中,而在其它瀏覽器中event會作為事件處理程序中作為第一個參數傳入。而且其Event對象的屬性和方法也有諸多差異,在JavaScript與HTML交互——事件中基本有所總結,不過還是抄一段關於事件處理程序綁定方面的差異

1. 參數個數不相同,這個最直觀,addEventListener有三個參數,attachEvent只有兩個,attachEvent添加的事件處理程序只能發生在冒泡階段,addEventListener第三個參數可以決定添加的事件處理程序是在捕獲階段還是冒泡階段處理(我們一般為了瀏覽器兼容性都設置為冒泡階段)

2. 第一個參數意義不同,addEventListener第一個參數是事件類型(比如click,load),而attachEvent第一個參數指明的是事件處理函數名稱(onclick,onload)

3. 事件處理程序的作用域不相同,addEventListener得作用域是元素本身,this是指的觸發元素,而attachEvent事件處理程序會在全局變量內運行,this是window

4. 為一個事件添加多個事件處理程序時,執行順序不同,addEventListener添加會按照添加順序執行,而attachEvent添加多個事件處理程序時順序無規律(添加的方法少的時候大多是按添加順序的反順序執行的,但是添加的多了就無規律了),所以添加多個的時候,不依賴執行順序的還好,若是依賴於函數執行順序,最好自己處理,不要指望瀏覽器

最簡單的四個

先寫四個最簡單的

  • getEvent:獲取事件對象
  • getTarget:獲取事件源對象
  • preventDefault:阻止事件默認行為
  • stopPropagation:阻止事件冒泡

 

(function (window) {
            var ssLib = {
                getEvent: function (e) {
                    return e ? e : window.event;
                },

                getTarget: function (e) {
                    var e = this.getEvent(e);
                    return e.target || e.srcElement;
                },

                preventDefault: function (e) {
                    var e = this.getEvent(e);
                    if (e.preventDefault) {
                        e.preventDefault();
                    } else {
                        e.returnValue = false;
                    }
                },

                stopPropagation: function (e) {
                    var e = this.getEvent(e);
                    if (e.stopPropagation) {
                        e.stopPropagation();
                    } else {
                        e.cancelBubble = true;
                    }
                }
            };
            window.ssLib = window.ss = ssLib;
        })(window);

代碼很簡單,相信聰明的小伙兒伴們一看就懂,就不多做解釋了

addEvent/removeEvent

  • addEvent:為元素綁定事件處理程序
  • removeEvent:移除元素事件處理程序
addEvent: function (element, type, handler, key) {
                    var key = key || handler;
                    if (element[type + key])
                        return false;
                    if (typeof element.addEventListener != "undefined") {
                        element[type + key] = handler;
                        element.addEventListener(type, handler, false);
                    }
                    else {
                        element['e' + type + key] = handler;
                        element[type + key] = function () {
                            element['e' + type + key](window.event);
                        };
                        element.attachEvent('on' + type, element[type + key]);
                    }
                    return true;
                },

                removeEvent: function (element, type, key) {
                    if (!element[type + key])
                        return false;

                    if (typeof element.removeEventListener != "undefined") {
                        element.removeEventListener(type, element[type + key], false);
                    }
                    else {
                        element.detachEvent("on" + type, element[type + key]);
                        element['e' + type + key] = null;
                    }

                    element[type + key] = null;
                    return true;
                },

這兩個函數兼容性寫法有很多,結合了很多大牛的寫法后我用的上面版本,這么些看似很復雜,實際上主要解決了上面提到的、除了多個事件處理程序執行順序問題的瀏覽器兼容性問題,比較難看懂的IE綁定部分就是為了處理this而寫的。

在使用的時候,可以顯示傳入一個key用於內部識別綁定函數,如果不綁定則使用handler本身作為key,所以可以這么用

ssLib.addEvent(element,'click',function(){},'test');
        ssLib.removeEvent(element,'click','test');
        
        function handler(){}
        
        ssLib.addEvent(element,'click',handler);
        ssLib.removeEvent(element,'click',handler);

on/off

這個是看到jQuery的on/delegate和YUI 的delegate后萌發的想法,不過平時老用人家的沒自己寫過,倉促寫了一個,感慨頗多,還是jQuery好使

on: function (parent, type, handler,validater,key) {
                    var _key=key || handler;
                    parent['on'+type+_key]=function (e) {
                        var target = ssLib.getTarget(e);
                        var isFire = validater(target);
                        if (isFire) {
                            target[type + _key] = handler;
                            target[type + _key](e);
                        }
                    };
                    
                    ssLib.addEvent(parent, type,parent['on'+type+_key] , key);
                },

                off: function (parent, type, key) {
                    if(typeof key=='function'){
                        ssLib.removeEvent(parent, type, parent['on'+type+key]);
                    }else{
                        ssLib.removeEvent(parent, type, key);
                    }
                    parent['on'+type+key]=null;
                }

寫法和剛才類似,不停的繞來繞去也是解決this問題,可以這么用

<div id="test">
        <div id="t1">T1</div>
        <div id="t2">T2</div>
    </div>

 

var parent = document.getElementById('test');
        
        var handler=function () {
            alert(this.id);
        }
        
        var validater=function (obj) {
            if(obj.parentNode.id=='test'){
                return true;
            }else{
                return false;
            }
        }
        
        ss.on(parent, 'click', handler, validater);
        
        ss.off(parent,'click',handler);
        
        ss.on(parent, 'click', handler, validater,'delegate');
        
        ss.off(parent,'click','delegate');

ready

用過jQuery的同學肯定知道這個函數,寫自己庫的時候是各種驚羡啊,於是乎加到了自己的庫里

ready: function (fn) {
                    if (document.addEventListener) {
                        document.addEventListener("DOMContentLoaded", function () {
                            document.removeEventListener("DOMContentLoaded", arguments.callee, false);// 防止多次調用
                            fn();
                        }, false);
                    } else if (document.addEvent) {
                        var doc = window.document, done = false;
                        // only fire once
                        var init = function () {
                            if (!done) {
                                done = true;
                                fn();
                            }
                        };
                        // polling for no errors
                        (function () {
                            try {
                                // throws errors until after ondocumentready
                                doc.documentElement.doScroll('left');// 文檔加載完成后此方法可用
                            } catch (e) {
                                setTimeout(arguments.callee, 50);
                                return;
                            }
                            // no errors, fire
                            init();
                        })();
                        // trying to always fire before onload
                        doc.onreadystatechange = function () {
                            if (doc.readyState == 'complete') {
                                doc.onreadystatechange = null;
                                init();
                            }
                        };
                    }
                }

要想看懂上面代碼最好看看An alternative for DOMContentLoaded on Internet Explorer

在現代瀏覽器中文檔加載完后會觸發“DOMContentLoaded”事件,而在底版本IE中文檔加載完成后會doScroll方法會生效

Event部分源代碼

(function (window) {
            var ssLib = {
                getEvent: function (e) {
                    return e ? e : window.event;
                },

                getTarget: function (e) {
                    var e = this.getEvent(e);
                    return e.target || e.srcElement;
                },

                preventDefault: function (e) {
                    var e = this.getEvent(e);
                    if (e.preventDefault) {
                        e.preventDefault();
                    } else {
                        e.returnValue = false;
                    }
                },

                stopPropagation: function (e) {
                    var e = this.getEvent(e);
                    if (e.stopPropagation) {
                        e.stopPropagation();
                    } else {
                        e.cancelBubble = true;
                    }
                },

                addEvent: function (element, type, handler, key) {
                    var key = key || handler;
                    if (element[type + key])
                        return false;
                    if (typeof element.addEventListener != "undefined") {
                        element[type + key] = handler;
                        element.addEventListener(type, handler, false);
                    }
                    else {
                        element['e' + type + key] = handler;
                        element[type + key] = function () {
                            element['e' + type + key](window.event);
                        };
                        element.attachEvent('on' + type, element[type + key]);
                    }
                    return true;
                },

                removeEvent: function (element, type, key) {
                    if (!element[type + key])
                        return false;

                    if (typeof element.removeEventListener != "undefined") {
                        element.removeEventListener(type, element[type + key], false);
                    }
                    else {
                        element.detachEvent("on" + type, element[type + key]);
                        element['e' + type + key] = null;
                    }

                    element[type + key] = null;
                    return true;
                },

                ready: function (fn) {
                    if (document.addEventListener) {
                        document.addEventListener("DOMContentLoaded", function () {
                            document.removeEventListener("DOMContentLoaded", arguments.callee, false);
                            fn();
                        }, false);
                    } else if (document.attachEvent) {
                        var doc = window.document, done = false;
                        // only fire once
                        var init = function () {
                            if (!done) {
                                done = true;
                                fn();
                            }
                        };
                        // polling for no errors
                        (function () {
                            try {
                                // throws errors until after ondocumentready
                                doc.documentElement.doScroll('left');
                            } catch (e) {
                                setTimeout(arguments.callee, 50);
                                return;
                            }
                            // no errors, fire
                            init();
                        })();
                        // trying to always fire before onload
                        doc.onreadystatechange = function () {
                            if (doc.readyState == 'complete') {
                                doc.onreadystatechange = null;
                                init();
                            }
                        };
                    }
                },

                on: function (parent, type, handler, validater, key) {
                    var _key = key || handler;
                    parent['on' + type + _key] = function (e) {
                        var target = ssLib.getTarget(e);
                        var isFire = validater(target);
                        if (isFire) {
                            target[type + _key] = handler;
                            target[type + _key](e);
                        }
                    };

                    ssLib.addEvent(parent, type, parent['on' + type + _key], key);
                },

                off: function (parent, type, key) {
                    if (typeof key == 'function') {
                        ssLib.removeEvent(parent, type, parent['on' + type + key]);
                    } else {
                        ssLib.removeEvent(parent, type, key);
                    }
                    parent['on' + type + key] = null;
                }
            };
            window.ssLib = window.ss = ssLib;
        })(window);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM