JavaScript 自定義事件(二)——Dom事件

上一次,了解了JS自定義事件隔节。今天在DOM上進(jìn)行事件方法擴(kuò)展。

1.基于DOM擴(kuò)展自定義方法(了解即可)

我們一起來(lái)添加一個(gè)addEvent方法

if (window.HTMLElement) {
    // 使用原型擴(kuò)展DOM自定義事件
    
    HTMLElement.prototype.addEvent = function(type, fn, capture) {
        var el = this;
        if (window.addEventListener) {
            el.addEventListener(type, function(e) {
                fn.call(el, e);
            }, capture);
        } else if (window.attachEvent) {
            el.attachEvent("on" + type, function(e) {
                fn.call(el, e);
            });
        } 
    };
} else {
    // 如果是不支持HTMLElement擴(kuò)展的瀏覽器
    // 通過(guò)遍歷所有元素?cái)U(kuò)展DOM事件
    
    var elAll = document.all, lenAll = elAll.length;
    for (var iAll=0; iAll<lenAll; iAll+=1) {
        elAll[iAll].addEvent = function(type, fn) {
            var el = this;
            el.attachEvent("on" + type, function(e) {
                fn.call(el, e);
            });
        };
    }
    
}

HTMLElement 接口表示所有的 HTML 元素(nodeType==1)。以一個(gè)<p>標(biāo)簽元素舉例奉芦,其向上尋找原型對(duì)象用過(guò)會(huì)是這樣:HTMLParagraphElement.prototype → HTMLElement.prototype → Element.prototype → Node.prototype → Object.prototype → null。上述代碼HTMLElement直接換成Element也是可以的剧蹂,但是會(huì)讓其他元素(例如文本元素等)也擴(kuò)展addEvent方法声功,有些浪費(fèi)了。

通過(guò)上面的擴(kuò)展宠叼,element上就有了addEvent()方法先巴。我們可以像下面展示的那樣使用

<div id="content-wrap" class="content-wrap">這是pattyzzh的領(lǐng)地</div>
document.getElementById("content-wrap").addEvent("click", function() {
         alert("歡迎光臨pattyzzh");    
});

基于DOM擴(kuò)展缺點(diǎn)有:缺少標(biāo)準(zhǔn)無(wú)規(guī)律、提高沖突可能性冒冬、性能以及瀏覽器支持伸蚯。擴(kuò)展名字任意命,很有可能就會(huì)與未來(lái)DOM瀏覽器本身支持的方法相互沖突简烤;擴(kuò)展無(wú)規(guī)律剂邮,很有可能出現(xiàn)A和B同名不同功能的擴(kuò)展而造成沖突;IE6-7瀏覽器下所有擴(kuò)展都要通過(guò)遍歷支持横侦,其性能開(kāi)銷可想而知挥萌;另外IE8對(duì)DOM擴(kuò)展的支持并不完整,例如其支持Element.prototype枉侧,卻沒(méi)有HTMLElement.prototype.


2.偽DOM自定義事件

這里的“偽DOM自定義事件”是自己定義的一個(gè)名詞引瀑,用來(lái)區(qū)分DOM自定義事件的。例如jQuery庫(kù)榨馁,其是基于包裝器(一個(gè)包含DOM元素的中間層)擴(kuò)展事件的憨栽,既與DOM相關(guān),又不直接是DOM翼虫,因此屑柔,稱之為“偽DOM自定義事件”悴晰。

如果只考慮事件添加笛丙,我們的工作其實(shí)很簡(jiǎn)單,根據(jù)支持情況馅精,addEventListener與attachEvent方法分別添加事件即可:

addEvent: function(type, fn,  capture) {
    var el = this.el;
    if (window.addEventListener) {
        el.addEventListener(type, fn, capture);        
    } else if (window.attachEvent) {
        el.attachEvent("on" + type, fn);
    }
    return this;
}

自定義事件添加容易次慢,但是如何觸發(fā)它們呢旁涤?——考慮到自定義事件與瀏覽器行為無(wú)關(guān)翔曲,同時(shí)瀏覽器沒(méi)有直接的觸發(fā)事件的方法。

自定義事件的觸發(fā)
  1. 對(duì)于標(biāo)準(zhǔn)瀏覽器劈愚,其提供了可供元素觸發(fā)的方法:element.dispatchEvent()瞳遍。不過(guò),在使用該方法之前菌羽,我們還需要做其他兩件事掠械,及創(chuàng)建和初始化。因此注祖,總結(jié)說(shuō)來(lái)就是:
document.createEvent()
event.initEvent()
element.dispatchEvent()

createEvent()方法返回新創(chuàng)建的Event對(duì)象猾蒂,支持一個(gè)參數(shù),表示事件類型

para.png

initEvent()方法用于初始化通過(guò)DocumentEvent接口創(chuàng)建的Event的值是晨。支持三個(gè)參數(shù):initEvent(eventName, canBubble, preventDefault). 分別表示事件名稱肚菠,是否可以冒泡是否阻止事件的默認(rèn)操作罩缴。
dispatchEvent(eventObj)就是觸發(fā)執(zhí)行了.
舉個(gè)例子

$(dom).addEvent("sayHello", function() {
alert("Hello");
});

// 創(chuàng)建
var event = document.createEvent("HTMLEvents");
// 初始化
event.initEvent("sayHello",true, false);
// 觸發(fā), 即彈出文字
dom.dispatchEvent(event);
  1. 對(duì)于IE瀏覽器蚊逢,由于向下很多版本的瀏覽器都不支持document.createEvent()方法)。IE瀏覽器有不少自給自足的東西箫章,例如下面要說(shuō)的這個(gè)"propertychange"事件烙荷,顧名思義,就是屬性改變即觸發(fā)的事件檬寂。例如文本框value值改變终抽,或是元素id改變,或是綁定的事件改變等等焰薄。
    當(dāng)我們添加自定義事件的時(shí)候拿诸,順便給元素添加一個(gè)自定義屬性即可。例如塞茅,我們添加自定義名為"sayHello"的自定義事件,順便我們可以對(duì)元素做點(diǎn)小手腳:
    dom.listener = 0;
    再順便把自定義事件fn塞到"propertychange"事件中:
dom.attachEvent("onpropertychange", function(e) {
    if (e.propertyName == "listener") {
        fn.call(this);   //fn是事件處理函數(shù)
    }
});

這個(gè)季率,當(dāng)我們需要觸發(fā)自定義事件的時(shí)候野瘦,只要修改DOM上自定義的listener屬性的值即可:
dom.listener = Math.random(); // 值變成隨機(jī)數(shù)
此時(shí)就會(huì)觸發(fā)dom上綁定的onpropertychange事件,又因?yàn)樾薷牡膶傩悦檬?listener", 于是自定義的fn就會(huì)被執(zhí)行飒泻。這就是IE瀏覽器下事件觸發(fā)實(shí)現(xiàn)的完整機(jī)制鞭光。

自定義事件的刪除

與觸發(fā)事件不同,事件刪除泞遗,各個(gè)瀏覽器都提供了對(duì)于的時(shí)間刪除方法惰许,如removeEventListener和detachEvent。不過(guò)呢史辙,對(duì)于IE瀏覽器汹买,還要多刪除一個(gè)事件佩伤,就是為了實(shí)現(xiàn)觸發(fā)功能額外增加的onpropertychange事件:
dom.detachEvent("onpropertychange", event);

綜合
var $ = function(el) {
    return new _$(el);    
};
var _$ = function(el) {
    this.el = (el && el.nodeType == 1)? el: document;
};
_$.prototype = {
    constructor: _$,
    addEvent: function(type, fn, capture) {
        var el = this.el;
        if (window.addEventListener) {
            el.addEventListener(type, fn, capture);
            var ev = document.createEvent("HTMLEvents");
            ev.initEvent(type, capture || false, false);
            
            if (!el["ev" + type]) { 
                el["ev" + type] = ev; //將自定義事件存儲(chǔ)在該元素屬性下,觸發(fā)時(shí)使用
            }  
        } else if (window.attachEvent) {
            el.attachEvent("on" + type, fn);    
            if (isNaN(el["cu" + type])) {
                // 自定義屬性晦毙,用來(lái)間接觸發(fā)觸發(fā)自定義事件
                el["cu" + type] = 0; 
            }   
            var fnEv = function(event) {
                if (event.propertyName == "cu" + type) { fn.call(el); }
            };
            el.attachEvent("onpropertychange", fnEv);     
            if (!el["ev" + type]) {
                el["ev" + type] = [fnEv];
            } else {
                el["ev" + type].push(fnEv);    //同一事件的多個(gè)處理函數(shù)
            }
        }
        return this;
    },
    fireEvent: function(type) {
        var el = this.el;
        if (typeof type === "string") {
            if (document.dispatchEvent) {
                if (el["ev" + type]) {
                    el.dispatchEvent(el["ev" + type]);
                }
            } else if (document.attachEvent) {
                el["cu" + type]++;
            }    
        }    
        return this;
    },
    removeEvent: function(type, fn, capture) {
        var el = this.el;
        if (window.removeEventListener) {
            el.removeEventListener(type, fn, capture || false);
        } else if (document.attachEvent) {
            el.detachEvent("on" + type, fn);
            var arrEv = el["ev" + type];
            if (arrEv instanceof Array) {
                for (var i=0; i<arrEv.length; i+=1) {
                     //這里還需要再次過(guò)濾生巡,否則會(huì)刪除該事件所有處理函數(shù)(ie下)
                     //條件?這里提示:1.對(duì)象的比較是比較引用  2.el["ev" + type]里裝的fnEv而不是fn 這里就難辦了  
                    el.detachEvent("onpropertychange", arrEv[i]);
                }
            }
        }
        return this;    
    }
};

測(cè)試

var fnClick = function(e) {
    e = e || window.event;
    var target = e.target || e.srcElement;
    if (target.nodeType === 1) {
        alert("點(diǎn)擊類型:" +  e.type);
        $(target).fireEvent("sayHello");  //觸發(fā)自定義事件
    }
}, sayHello1 = function() {
    alert("Hello 1");    
}, sayHello2 = function() {
    alert("Hello 2");    
};

//  梅西圖片
var elImage = document.getElementById("myImg");
$(elImage)
    .addEvent("click", fnClick)
    .addEvent("sayHello", sayHello1)
    .addEvent("sayHello", sayHello2);

// 刪除自定義事件按鈕
var elButton = document.getElementById("myBut");
$(elButton).addEvent("click", function() {
    $(elImage)
        .removeEvent("sayHello", sayHello1)
        .removeEvent("sayHello", sayHello2);    

       alert("清除成功见妒!");
});

html:
     <div class="box" id="box">
        ![](./meixi.jpg)
        <input type="button" value="點(diǎn)擊清除自定義事件 " id="myBut">
     </div>  

運(yùn)行結(jié)果如下:
當(dāng)我們點(diǎn)擊圖片孤荣,會(huì)出現(xiàn)下面三個(gè)彈窗


alert1.png

alert2.png

alert3.png

當(dāng)我們點(diǎn)擊按鈕:


del.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市须揣,隨后出現(xiàn)的幾起案子盐股,更是在濱河造成了極大的恐慌,老刑警劉巖耻卡,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遂庄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡劲赠,警方通過(guò)查閱死者的電腦和手機(jī)涛目,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凛澎,“玉大人霹肝,你說(shuō)我怎么就攤上這事∷芗澹” “怎么了沫换?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)最铁。 經(jīng)常有香客問(wèn)我讯赏,道長(zhǎng),這世上最難降的妖魔是什么冷尉? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任漱挎,我火速辦了婚禮,結(jié)果婚禮上雀哨,老公的妹妹穿的比我還像新娘磕谅。我一直安慰自己,他們只是感情好雾棺,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布膊夹。 她就那樣靜靜地躺著,像睡著了一般捌浩。 火紅的嫁衣襯著肌膚如雪放刨。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天尸饺,我揣著相機(jī)與錄音进统,去河邊找鬼助币。 笑死,一個(gè)胖子當(dāng)著我的面吹牛麻昼,可吹牛的內(nèi)容都是我干的奠支。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼抚芦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼倍谜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叉抡,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤尔崔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后褥民,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體季春,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年消返,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了载弄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撵颊,死狀恐怖宇攻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倡勇,我是刑警寧澤逞刷,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站妻熊,受9級(jí)特大地震影響夸浅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扔役,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一帆喇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厅目,春花似錦番枚、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)深啤。三九已至拗馒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間溯街,已是汗流浹背诱桂。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工洋丐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挥等。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓友绝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肝劲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迁客,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)辞槐,斷路器掷漱,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 之前通過(guò)深入學(xué)習(xí)DOM的相關(guān)知識(shí),看了慕課網(wǎng)DOM探索之基礎(chǔ)詳解篇這個(gè)視頻(在最近看第三遍的時(shí)候榄檬,準(zhǔn)備記錄一點(diǎn)東西...
    微醺歲月閱讀 4,452評(píng)論 2 62
  • (續(xù)jQuery基礎(chǔ)(1)) 第5章 DOM節(jié)點(diǎn)的復(fù)制與替換 (1)DOM拷貝clone() 克隆節(jié)點(diǎn)是DOM的常...
    凜0_0閱讀 1,318評(píng)論 0 8
  • 真不明白為什么你們這么喜歡玩曖昧鹿榜,說(shuō)騷話海雪,按我說(shuō)就是賤………平時(shí)就不要口嘴不對(duì)…說(shuō)出來(lái)的都是道理,沒(méi)意思舱殿!
    fbad912d56d6閱讀 1,027評(píng)論 0 1
  • 干燥紙味的被子包裹熱氣蜷曲靜棲,身體在被卷的庇護(hù)里幾乎觸不到凜冽的空氣枝恋,頭腦線路失連创倔。躥隙晨光鉆開(kāi)眼皮擦拭瞳...
    聶臻閱讀 246評(píng)論 0 0