實(shí)現(xiàn)通用的事件注冊(cè)方法


title: 實(shí)現(xiàn)通用的事件注冊(cè)方法
date: 2017-07-15
tags: [原生js]
categories: js


初稿:2017-07-15

更新: 2018-07-29

實(shí)現(xiàn)通用的事件注冊(cè)方法

為什么要討論事件注冊(cè)的兼容性袜匿?

由于歷史原因,不同的瀏覽器對(duì)事件注冊(cè)的實(shí)現(xiàn)方式有些許差異送讲。

事件注冊(cè)有哪些方法弄砍?

on+"event"

例如:onclick/onmouseover/onmouseenter/...支持最廣,筆者用的最多姑子,倘若要在一個(gè)元素上添加多次同一事件,此時(shí)就顯得無(wú)能為力了,以最后一次綁定的事件為準(zhǔn)怪与。

addEventListener

W3C 標(biāo)準(zhǔn)方法,功能也最強(qiáng)大缅疟,支持添加多個(gè)事件

//element.addEventListener(type,listener,useCapture);
obj.addEventListener('click', method1, false)
obj.addEventListener('click', method2, false)
obj.addEventListener('click', method3, false)

執(zhí)行順序?yàn)?method1->method2->method3分别,第三個(gè)參數(shù)是指以“冒泡”還是“捕獲”的標(biāo)準(zhǔn)綁定事件,一般為 false(冒泡).

并且可以使用 removeEventListener() 方法移除由 addEventListener()方法添加的事件句柄存淫。

注意: 如果要移除事件句柄耘斩,addEventListener() 的執(zhí)行函數(shù)必須使用外部函數(shù),如上實(shí)例所示 (method1/2/3)桅咆。
匿名函數(shù)括授,類似 "window.removeEventListener("event", function(){ myScript });" 該事件是無(wú)法移除的。

如果瀏覽器不支持 removeEventListener() 方法岩饼,你可以使用 detachEvent() 方法實(shí)現(xiàn)荚虚。

var x = window.getElementById('myDIV')
if (x.removeEventListener) {
  // // 所有瀏覽器,除了 IE 8 及更早IE版本
  x.removeEventListener('mousemove', myFunction)
} else if (x.detachEvent) {
  // IE 8 及更早IE版本
  x.detachEvent('onmousemove', myFunction)
}

attachEvent

IE 家的方法籍茧,火狐與其他家瀏覽器都不支持,attachEvent——兼容:IE7版述、IE8;不兼容 firefox寞冯、chrome渴析、IE9、IE10吮龄、IE11俭茧、safari、opera.盡量不要用螟蝙,支持綁定多個(gè)事件恢恼,與 addEventListener()執(zhí)行順序相反,即 method3->method2->method1

進(jìn)行兼容性處理

1.簡(jiǎn)單通過 if 判斷

if (window.addEventListener) {
  //功能最強(qiáng)大
  div.addEventListener('click', function() {
    alert('hello,world')
  })
} else if (window.attachEvent) {
  //非標(biāo)準(zhǔn)特性 盡量不要使用
  div.attachEvent('click', function() {
    alert('hello,world')
  })
} else {
  //支持最好
  div['onclick'] = function() {
    alert('hello,world')
  }
}

2.封裝成函數(shù)

function registeEvent(elem, type, handler, useCapture) {
  if (window.addEventListener) {
    //功能最強(qiáng)大
    elem.addEventListener(type, handler, useCapture)
  } else if (window.attachEvent) {
    //非標(biāo)準(zhǔn)特性 盡量不要使用
    elem.attachEvent(type, handler)
  } else {
    //支持最好
    elem['on' + type] = handler
  }
}

到這里胰默,我們每次注冊(cè)事件時(shí)都通過 registeEvent 注冊(cè)场斑,很明顯漓踢,每次注冊(cè)都要判斷瀏覽器的能力是否支持,每一次都要檢測(cè)漏隐,這不是我們想要的喧半。

3. 使用立即執(zhí)行函數(shù)進(jìn)行優(yōu)化

var registeEvent = (function createEventRegister() {
  if (window.addEventListener) {
    return function(elem, type, handler, useCapture) {
      elem.addEventListener(type, handler, useCapture)
    }
  } else if (window.attachEvent) {
    return function(elem, type, handler) {
      elem.attachEvent(type, function() {
        handler.call(elem, window.event) //注:attachEvent內(nèi)部this指向window而不是觸發(fā)對(duì)象,使用call方法修改this
      })
    }
  } else {
    return function(elem, type, handler) {
      elem['on' + type] = handler
    }
  }
})()

此后只用通過 registeEvent 方法注冊(cè)事件即可,且只在最開始的時(shí)候進(jìn)行一次能力檢測(cè).

registeEvent(div, 'click', function() {
  alert('hello,world')
})

但是此時(shí)依舊存在一個(gè)缺點(diǎn)青责,如果我們從頭到尾沒有綁定過事件挺据,即使用 registeEvent 函數(shù),那么立即執(zhí)行函數(shù)白白執(zhí)行了一次完全是多余的(或許有些吹毛求疵)脖隶,我們可以使用惰性載入函數(shù)來(lái)進(jìn)行優(yōu)化扁耐。

4. 惰性載入函數(shù)方案

此時(shí) registerEvent 依然被聲明為一個(gè)普通函數(shù),在函數(shù)里依然有一些分支判斷产阱。但是在第一次進(jìn)入條件分支之后婉称,在函數(shù)內(nèi)部會(huì)重寫這個(gè)函數(shù),重寫之后的函數(shù)就是我們期望的 registerEvent 函數(shù)构蹬,在下一次進(jìn)入 registerEvent registerEvent 函數(shù)里不再存在條件分支語(yǔ)句:

var registeEvent = function createEventRegister() {
  if (window.addEventListener) {
    registeEvent = function(elem, type, handler, useCapture) {
      elem.addEventListener(type, handler, useCapture)
    }
  } else if (window.attachEvent) {
    registeEvent = function(elem, type, handler) {
      elem.attachEvent(type, function() {
        handler.call(elem, window.event) //注:attachEvent內(nèi)部this指向window而不是觸發(fā)對(duì)象,使用call方法修改this
      })
    }
  } else {
    registeEvent = function(elem, type, handler) {
      elem['on' + type] = handler
    }
  }
}

附上一些兼容性解決方案 EventUtil:

var EventUtil = {
  addEvent: function(element, type, handler) {
    // 添加綁定
    if (element.addEventListener) {
      // 使用DOM2級(jí)方法添加事件
      element.addEventListener(type, handler, false)
    } else if (element.attachEvent) {
      // 使用IE方法添加事件
      element.attachEvent('on' + type, handler)
    } else {
      // 使用DOM0級(jí)方法添加事件
      element['on' + type] = handler
    }
  },
  // 移除事件
  removeEvent: function(element, type, handler) {
    if (element.removeEventListener) {
      element.removeEventListener(type, handler, false)
    } else if (element.datachEvent) {
      element.detachEvent('on' + type, handler)
    } else {
      element['on' + type] = null
    }
  },
  getEvent: function(event) {
    // 返回事件對(duì)象引用
    return event ? event : window.event
  },
  // 獲取mouseover和mouseout相關(guān)元素
  getRelatedTarget: function(event) {
    if (event.relatedTarget) {
      return event.relatedTarget
    } else if (event.toElement) {
      // 兼容IE8-
      return event.toElement
    } else if (event.formElement) {
      return event.formElement
    } else {
      return null
    }
  },
  getTarget: function(event) {
    //返回事件源目標(biāo)
    return event.target || event.srcElement
  },
  preventDefault: function(event) {
    //取消默認(rèn)事件
    if (event.preventDefault) {
      event.preventDefault()
    } else {
      event.returnValue = false
    }
  },
  stoppropagation: function(event) {
    //阻止事件流
    if (event.stoppropagation) {
      event.stoppropagation()
    } else {
      event.canceBubble = false
    }
  },
  // 獲取mousedown或mouseup按下或釋放的按鈕是鼠標(biāo)中的哪一個(gè)
  getButton: function(event) {
    if (document.implementation.hasFeature('MouseEvents', '2.0')) {
      return event.button
    } else {
      //將IE模型下的button屬性映射為DOM模型下的button屬性
      switch (event.button) {
        case 0:
        case 1:
        case 3:
        case 5:
        case 7:
          //按下的是鼠標(biāo)主按鈕(一般是左鍵)
          return 0
        case 2:
        case 6:
          //按下的是中間的鼠標(biāo)按鈕
          return 2
        case 4:
          //鼠標(biāo)次按鈕(一般是右鍵)
          return 1
      }
    }
  },
  //獲取表示鼠標(biāo)滾輪滾動(dòng)方向的數(shù)值
  getWheelDelta: function(event) {
    if (event.wheelDelta) {
      return event.wheelDelta
    } else {
      return -event.detail * 40
    }
  },
  // 以跨瀏覽器取得相同的字符編碼王暗,需在keypress事件中使用
  getCharCode: function(event) {
    if (typeof event.charCode == 'number') {
      return event.charCode
    } else {
      return event.keyCode
    }
  }
}

參考書籍:《JavaScript 高級(jí)程序設(shè)計(jì)第三版》《JavaScript 設(shè)計(jì)模式與開發(fā)實(shí)踐》
參考文章:手把手教你用原生 JavaScript 造輪子(1)——分頁(yè)器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市庄敛,隨后出現(xiàn)的幾起案子俗壹,更是在濱河造成了極大的恐慌,老刑警劉巖藻烤,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绷雏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡隐绵,警方通過查閱死者的電腦和手機(jī)之众,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)依许,“玉大人棺禾,你說(shuō)我怎么就攤上這事∏吞” “怎么了膘婶?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蛀醉。 經(jīng)常有香客問我悬襟,道長(zhǎng),這世上最難降的妖魔是什么拯刁? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任脊岳,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘割捅。我一直安慰自己奶躯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布亿驾。 她就那樣靜靜地躺著嘹黔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莫瞬。 梳的紋絲不亂的頭發(fā)上儡蔓,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音疼邀,去河邊找鬼喂江。 笑死,一個(gè)胖子當(dāng)著我的面吹牛檩小,可吹牛的內(nèi)容都是我干的开呐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼规求,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卵惦?” 一聲冷哼從身側(cè)響起阻肿,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沮尿,沒想到半個(gè)月后丛塌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜疾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年赴邻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啡捶。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姥敛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞎暑,到底是詐尸還是另有隱情彤敛,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布了赌,位于F島的核電站墨榄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏勿她。R本人自食惡果不足惜袄秩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧之剧,春花似錦贮喧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至雇庙,卻和暖如春谓形,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疆前。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工寒跳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人竹椒。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓童太,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胸完。 傳聞我的和親對(duì)象是個(gè)殘疾皇子书释,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • 本文主要談及問題: 關(guān)于編寫跨瀏覽器的事件處理函數(shù)和事件對(duì)象 關(guān)于編寫跨瀏覽器的事件處理函數(shù)和事件對(duì)象 為什么要編...
    JimmyChung閱讀 2,125評(píng)論 0 4
  • 什么是事件: 我們可以簡(jiǎn)單的把事件理解為瀏覽器的感知系統(tǒng)。比如說(shuō):他可以感覺到用戶是否點(diǎn)擊(click)了頁(yè)面赊窥、鼠...
    張松1366閱讀 6,790評(píng)論 1 6
  • dom對(duì)象的innerText和innerHTML有什么區(qū)別锨能? innerHTML指的是從對(duì)象的起始位置到終止位置...
    coolheadedY閱讀 488評(píng)論 0 0
  • 什么是事件: 事件是交互體驗(yàn)的核心功能 一.事件冒泡: 當(dāng)一個(gè)事件發(fā)生時(shí)扯再,這個(gè)事件會(huì)從內(nèi)向外逐層傳遞。 二.為什么...
    輕描淡寫mua閱讀 515評(píng)論 0 0
  • 以下文章為轉(zhuǎn)載址遇,對(duì)理解JavaScript中的事件處理機(jī)制很有幫助熄阻,淺顯易懂,特分享于此倔约。 什么是事件秃殉? 事件(E...
    jxyjxy閱讀 3,035評(píng)論 1 10