原生js實(shí)現(xiàn)on與off 方法

@拭目以待原生js實(shí)現(xiàn)on與off 方法

使用過jQuery的同學(xué)上沐,應(yīng)該對(duì)事件綁定方法 .on() .off() 有一定的了解参咙。 在個(gè)人類庫 jTool 中實(shí)現(xiàn)了這兩個(gè)方法硫眯,這里就來細(xì)說下原生實(shí)現(xiàn)方式。

**注意: **

以下為個(gè)人類庫 jToolEvent 實(shí)現(xiàn)方式净宵。
代碼中使用到一個(gè)基礎(chǔ)方法對(duì)象 utilities, 該對(duì)象為 jTool 的基礎(chǔ)類紧武。 如果想了解更多脏里,可以通過點(diǎn)擊進(jìn)入查看原碼虹曙。


首先通過一個(gè)空架子來了解下實(shí)現(xiàn)邏輯番舆,核心實(shí)現(xiàn)在getEventObject() 方法內(nèi)的包裝函數(shù):

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 對(duì)象
    getEventObject: function (event, querySelector, callback, useCapture){
        // 事件代理實(shí)現(xiàn)核心
        // 注意: 這個(gè)方法為包裝函數(shù),此處的this為觸發(fā)事件的Element
        var fn = callback;
            callback = function(e){
                // 驗(yàn)證子選擇器所匹配的nodeList中是否包含當(dāng)前事件源 或 事件源的父級(jí)
                // 注意: 這個(gè)方法為包裝函數(shù),此處的this為觸發(fā)事件的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
            };
    },
    // 增加事件,并將事件對(duì)象存儲(chǔ)至DOM節(jié)點(diǎn)
    addEvent: function (eventList){
    }禾怠,
    // 刪除事件,并將事件對(duì)象移除出DOM節(jié)點(diǎn)
    removeEvent: function (eventList){
 
    }
}

參數(shù)說明

  1. event: 事件名, 如 'click', 并支持 'click.scope1' 事件域的方法(雖然支持芽偏,但由于暫時(shí)沒有使用場景弦讽。所以僅對(duì)參數(shù)進(jìn)行了處理往产,功能上沒有實(shí)現(xiàn))

  2. querySelector: 子選擇器

  3. callback: 事件觸發(fā)后執(zhí)行的函數(shù)

  4. useCapture: 指定事件是否在捕獲或冒泡階段執(zhí)行.true - 事件句柄在捕獲階段執(zhí)行 false- 默認(rèn)。事件句柄在冒泡階段執(zhí)行

  5. eventList: 由 .getEventObject() 方法生成的 Event 對(duì)象

on的實(shí)現(xiàn)邏輯

  • 通過調(diào)用jTool實(shí)例的on方法锐朴,傳入?yún)?shù)

  • 調(diào)用getEventObject() 獲取事件對(duì)象

  • 調(diào)用addEvent() 方法進(jìn)行事件綁定

off的實(shí)現(xiàn)邏輯

  • 通過調(diào)用jTool實(shí)例的off方法蔼囊,傳入?yún)?shù)

  • 調(diào)用getEventObject() 獲取事件對(duì)象

  • 調(diào)用removeEvent() 方法解除事件綁定

完整Event對(duì)象

var _Event = {
    on: function(event, querySelector, callback, useCapture) {
        // 將事件觸發(fā)執(zhí)行的函數(shù)存儲(chǔ)于DOM上, 在清除事件時(shí)使用
    },
 
    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 對(duì)象
    getEventObject: function(event, querySelector, callback, useCapture) {
        // $(dom).on(event, callback);
        if (typeof querySelector === 'function') {
            useCapture = callback || false;
            callback = querySelector;
            querySelector = undefined;
        }
        // event callback 為必要參數(shù)
        if (!event) {
            utilities.error('事件綁定失敗,原因: 參數(shù)中缺失事件類型');
            return this;
        }
 
        // 子選擇器不存在 或 當(dāng)前DOM對(duì)象包含Window Document 則將子選擇器置空
        if(!querySelector || utilities.type(this.DOMList[0]) !== 'element'){
            querySelector = '';
        }
        // #Event003 存在子選擇器 -> 包裝回調(diào)函數(shù), 回調(diào)函數(shù)的參數(shù)
        // 預(yù)綁定功能實(shí)現(xiàn)
        if(querySelector !== ''){
            var fn = callback;
            callback = function(e){
                // 驗(yàn)證子選擇器所匹配的nodeList中是否包含當(dāng)前事件源 或 事件源的父級(jí)
                // 注意: 這個(gè)方法為包裝函數(shù),此處的this為觸發(fā)事件的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暫時(shí)不用
                nameScope: eventScopeSplit[1] || undefined
            };
            eventList.push(eventObj);
        });
        return eventList;
    },
 
    // 增加事件,并將事件對(duì)象存儲(chǔ)至DOM節(jié)點(diǎn)
    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;
    },
 
    // 刪除事件,并將事件對(duì)象移除出DOM節(jié)點(diǎn)
    removeEvent: function(eventList) {
        var _this = this;
        var eventFnList; //事件執(zhí)行函數(shù)隊(duì)列
        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() 都可以進(jìn)行事件綁定滴肿, 區(qū)別在于 .on() 使用事件代理。 事件代理是一種事件預(yù)綁定機(jī)制贵少, 該機(jī)制可以在事件節(jié)點(diǎn)不存在的情況下, 將事件綁定至已存在父級(jí)節(jié)點(diǎn)之上的方式滔灶。


源碼鏈接

jTool.Event 源碼

jTool類庫

使用jTool類庫開發(fā)的表格組件


隨筆一行

這是前端最好的時(shí)代录平, 這也是前端最壞的時(shí)代。 眾多前端框架滿天飛动猬,隨著 jQuery 在前端行業(yè)的慢慢弱化表箭,總是會(huì)有一種斯人遠(yuǎn)去,何者慰籍的感覺彼水〖颍互勉吧,各位叛赚。

@拭目以待

個(gè)人站點(diǎn):www.lovejavascript.com
表格管理插件:gridmanager.lovejavascript.com && github地址
QQ交流群 (452781895):How To Make Love

《野生前端工程師》專輯中所有文章均為@拭目以待原創(chuàng)稽揭,轉(zhuǎn)載請(qǐng)注明出處溪掀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市璃哟,隨后出現(xiàn)的幾起案子喊递,更是在濱河造成了極大的恐慌骚勘,老刑警劉巖撮奏,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畜吊,死亡現(xiàn)場離奇詭異户矢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捌年,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門驱证,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事伙单」ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵布疼,是天一觀的道長游两。 經(jīng)常有香客問我漩绵,道長,這世上最難降的妖魔是什么宝踪? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任碍扔,我火速辦了婚禮,結(jié)果婚禮上不同,老公的妹妹穿的比我還像新娘厉膀。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布站蝠。 她就那樣靜靜地躺著汰具,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菱魔。 梳的紋絲不亂的頭發(fā)上留荔,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音澜倦,去河邊找鬼聚蝶。 笑死藻治,一個(gè)胖子當(dāng)著我的面吹牛碘勉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桩卵,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼验靡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雏节?” 一聲冷哼從身側(cè)響起胜嗓,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钩乍,沒想到半個(gè)月后辞州,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寥粹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年变过,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝涤。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡媚狰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妄痪,到底是詐尸還是另有隱情哈雏,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布衫生,位于F島的核電站裳瘪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏罪针。R本人自食惡果不足惜彭羹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泪酱。 院中可真熱鬧派殷,春花似錦还最、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至经伙,卻和暖如春扶叉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帕膜。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工枣氧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垮刹。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓达吞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荒典。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酪劫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,129評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)寺董,斷路器契耿,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法螃征,內(nèi)部類的語法,繼承相關(guān)的語法透敌,異常的語法盯滚,線程的語...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • 早上起來看了關(guān)于江歌事件的新聞,心中難免有一絲感傷酗电,兩個(gè)人都消失在塵世魄藕,一個(gè)化為塵土,另一個(gè)死了靈魂撵术。 我沒有資...
    劉吳敵閱讀 238評(píng)論 1 0
  • 敲代碼敲幾年背率,加了好多技術(shù)討論群,一開始自己也是經(jīng)常問人嫩与,現(xiàn)在一般都是解答問題寝姿。不過最煩的就是這樣一種人。張嘴就問...
    九號(hào)咖啡屋閱讀 250評(píng)論 0 0