關(guān)于原生dom事件添加、刪除方法的一些拓展

引言

這是一個(gè)簡(jiǎn)單的需求夭问,衍生的思考哮缺。

昨天QA提了一個(gè)富文本優(yōu)化的需求,當(dāng)插入視頻和音頻時(shí)甲喝,如果用戶(hù)同時(shí)點(diǎn)擊了視頻尝苇、音頻,那么其他的視頻或者音頻應(yīng)該停止播放埠胖,只有最后一個(gè)點(diǎn)擊的才會(huì)是播放狀態(tài)糠溜。

需求很簡(jiǎn)單(天真的想法),稍微理清下思路:

  • 多媒體文件插入之后直撤,獲取dom內(nèi)所有的多媒體元素
  • 循環(huán)給他們添加事件非竿,在添加事件前,remove掉之前的事件
  • 當(dāng)任何一個(gè)元素點(diǎn)擊之后红柱,遍歷多媒體文件列表,如何不是自己蓖乘,其他的所有的都暫停就好了

三步搞定锤悄。

上代碼

 onEmbed() {
      const item = document.querySelectorAll('video,audio')
      item.forEach((element, kindex) => {
        const clickFun = e => {
          item.forEach((i, index) => {
            // 內(nèi)容中包含音頻和視頻時(shí),音頻和視頻能同時(shí)播放
            if (index !== kindex) {
              i.pause()
            }
          })
        }
        element.removeEventListener('play', clickFun, false)
        element.addEventListener('play', clickFun, false)
      })
    },

代碼看上去完美嘉抒,運(yùn)行下零聚,發(fā)現(xiàn)沒(méi)有問(wèn)題,當(dāng)多個(gè)視頻點(diǎn)擊時(shí)些侍,永遠(yuǎn)只有最后一個(gè)視頻點(diǎn)擊會(huì)播放隶症,其他的只會(huì)暫停,

如果事情這么簡(jiǎn)單岗宣,那就不會(huì)有這次blog記錄了蚂会,這個(gè)代碼有嚴(yán)重的性能問(wèn)題,有興趣的耗式,可以停下來(lái)思考一分鐘

console.log("思考是不可能的胁住,我只想立馬知道答案!")

好了纽什,現(xiàn)在揭露措嵌,如果在clickFun事件中,打印下console.log(e)芦缰,就會(huì)發(fā)現(xiàn)每多次添加一個(gè)視頻,e就會(huì)輸出多次让蕾,類(lèi)似下圖浪规,這樣隨著添加的元素變多探孝,那么就會(huì)造成這個(gè)事件調(diào)用多次,所以 還需要進(jìn)一步改進(jìn)缸濒,同時(shí)也得查清楚removeEventListener為啥沒(méi)有效果。

事件觸發(fā)了多次

removeEventListener介紹

仔細(xì)閱讀了下代碼庇配,應(yīng)該是clickFun這個(gè)事件绍些,每次生成,都會(huì)new一個(gè)柬批,而removeEventListener要remove掉addEventListener添加的事件,function必須是一個(gè)引用對(duì)象

https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/removeEventListener

addEventListener方法將指定的監(jiān)聽(tīng)器注冊(cè)到 EventTarget 上 , 然后removeEventListener內(nèi)會(huì)按照嗅虏,typelistener 參數(shù)旋恼、useCapture這三個(gè)參數(shù)來(lái)刪除 EventTarget 內(nèi)的Event,如果有一處不一樣冰更,就會(huì)失敗昂勒。

官網(wǎng)提供的例子

https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener

現(xiàn)在就是listener因?yàn)槊看味紩?huì)觸發(fā),所以?xún)?nèi)存中一直都是new一個(gè) 戈盈,所以導(dǎo)致無(wú)法remove

填坑

方法一: 找到目標(biāo)元素,遍歷所有的event归斤,然后remove掉就好了,比如說(shuō)chrome內(nèi)的Event Listeners工具脏里,監(jiān)測(cè)元素的事件,然后remove掉就好了

chrome的Event Listeners工具

可惜這個(gè)方法迫横,目前只有在chrome瀏覽器Console內(nèi)才有效,它還沒(méi)有成為標(biāo)準(zhǔn)矾踱。

2015年W3C官方博客宣布成立WICG小組(Web Platform Incubator Community Group),號(hào)召開(kāi)發(fā)者把可行的Web平臺(tái)新特性都提交到這個(gè)小組內(nèi)呛讲,其中就有getEventListeners,雖然已經(jīng)提了2年了贝搁,但是好像還沒(méi)有什么進(jìn)展。

另外在github上也有人向WHATWG組織(很牛的一個(gè)組織徘公,不比W3C差)提了一個(gè)issue,有更詳細(xì)的建議坦袍,也有好多人在討論,個(gè)人覺(jué)得WHATWG會(huì)更快的把它設(shè)為新特性捂齐。

方法二: 按照removeEventListener的原理缩抡,只要保證typelistener 參數(shù)瞻想、useCapture這三個(gè)參數(shù)一致,肯定就可以刪除了蘑险,那么把clickFun移出循環(huán)內(nèi)應(yīng)該就可以了。

看代碼

// clickFun放置到最外層佃迄,保證只生成一次
const clickFun = e => {
  const item = document.querySelectorAll('video,audio')
  item.forEach((i, index) => {
    // 內(nèi)容中包含音頻和視頻時(shí),音頻和視頻能同時(shí)播放
    if (index !== Number(e.target.getAttribute('kindex'))) {
      i.pause()
    }
  })
}

 onEmbed() {
      const item = document.querySelectorAll('video,audio')
      item.forEach((element, kindex) => {
        element.setAttribute('kindex', kindex)
        element.removeEventListener('play', clickFun, false)
        element.addEventListener('play', clickFun, false)
      })
    }

代碼運(yùn)行后堆缘,完美,一切都按照最初的設(shè)想運(yùn)行下去吼肥。

事情到這里結(jié)束了嗎?并沒(méi)有潜沦,現(xiàn)在回想方法一和removeEventListener的介紹绪氛,我們前端如果需要remove掉某個(gè)dom節(jié)點(diǎn)的某個(gè)類(lèi)型所有的事件,應(yīng)該如何處理枣察?如果需要查看某個(gè)節(jié)點(diǎn)所有事件應(yīng)該如何處理?而且目前 現(xiàn)在原生add序目、remove事件真的好難用 ,有沒(méi)有更簡(jiǎn)單的寫(xiě)法猿涨。

這就是新的需求,后面一個(gè)問(wèn)題叛赚,還可以通過(guò)chrome Event Listeners工具查詢(xún),但是通過(guò)編碼操作就不行了俺附。

其實(shí)仔細(xì)想想,關(guān)于第一個(gè)問(wèn)題也很好解決 事镣,如果我們拿不到節(jié)點(diǎn)的Event,那每次添加Event前氛琢,把Event存儲(chǔ)到一個(gè)列表內(nèi),如果要remove阳似,只要遍歷這個(gè)列表就可以了,這樣所有的的Event都可以管理了障般,以后要操作也方便了。

好了挽荡,思想已經(jīng)有了,接下來(lái)就是擼代碼了定拟。

type Capture = boolean | AddEventListenerOptions;
interface EventHandler {
  [handlers: string]: [[EventListenerOrEventListenerObject, Capture]] | [];
}

const isProperty = (obj, property) =>
  Object.prototype.hasOwnProperty.call(obj, property);
const dom = {
  eventMap: new Map<Element, EventHandler>(),
  addListener(
    node: Element,
    event: string,
    handler: EventListenerOrEventListenerObject,
    capture: Capture
  ) {
    if (!this.eventMap.has(node)) {
      this.eventMap.set(node, Object.create(null));
    }

    // @ts-ignore
    const targetHandlers: EventHandler = this.eventMap.get(node);

    if (!isProperty(targetHandlers, event)) {
      targetHandlers[event] = [];
    }
    // @ts-ignore
    targetHandlers[event].push([handler, capture]);

    node.addEventListener(event, handler, capture);
  },
  removeListeners(node, event) {
    if (!this.eventMap.has(node)) {
      return false;
    }
    // @ts-ignore
    const targetHandlers: EventHandler = this.eventMap.get(node);
    if (!isProperty(targetHandlers, event)) {
      return false;
    }

    targetHandlers[event].forEach(th =>
      node.removeEventListener(event, th[0], th[1])
    );
  }
};
export default dom;


代碼很簡(jiǎn)單,不到50行株依,但是基本上實(shí)現(xiàn)了,簡(jiǎn)單的添加恋腕、刪除、羅列已有監(jiān)聽(tīng)事件列表荠藤。

刪除事件也不用傳 handler获高,比之前方便了很多。

dom.addListener(evn, "click", clickfun, false);
dom.removeListeners(evn, "click");

通過(guò)導(dǎo)出的api可以很方便的查看念秧,某個(gè)node下有多少類(lèi)型事件


最后把代碼整理了下,做了一個(gè)demo摊趾,放在了codesandebox,所有的代碼都在里面

點(diǎn)擊演示

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末总寻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子渐行,更是在濱河造成了極大的恐慌,老刑警劉巖祟印,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粟害,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡悲幅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)汰具,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人留荔,你說(shuō)我怎么就攤上這事。” “怎么了藻治?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵巷挥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我句各,道長(zhǎng)晴叨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任兼蕊,我火速辦了婚禮,結(jié)果婚禮上孙技,老公的妹妹穿的比我還像新娘。我一直安慰自己牵啦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布哈雏。 她就那樣靜靜地躺著,像睡著了一般裳瘪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彭羹,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音还最,去河邊找鬼。 笑死拓轻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悦即。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辜梳,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了作瞄?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宗挥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后契耿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡透敌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年踢械,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片内列。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖话瞧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情移稳,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布个粱,位于F島的核電站,受9級(jí)特大地震影響都许,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胶征,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睛低。 院中可真熱鬧服傍,春花似錦、人聲如沸骂铁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至钞支,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烁挟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工信夫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人静稻。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓匈辱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亡脸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的垂谢。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,490評(píng)論 1 11
  • 目錄 概述 EventTarget接口EventTarget.addEventListener()EventTar...
    許驍Charles閱讀 549評(píng)論 4 0
  • 事件是一種異步編程的實(shí)現(xiàn)方式滥朱,本質(zhì)上是程序各個(gè)組成部分之間的通信根暑。DOM支持大量的事件徙邻,本節(jié)介紹DOM的事件編程。...
    許先生__閱讀 938評(píng)論 0 3
  • 事件是一種異步編程的實(shí)現(xiàn)方式缰犁,本質(zhì)上是程序各個(gè)組成部分之間的通信怖糊。DOM支持大量的事件,本節(jié)介紹DOM的事件編程颇象。...
    周花花啊閱讀 593評(píng)論 0 3
  • 以下文章為轉(zhuǎn)載,對(duì)理解JavaScript中的事件處理機(jī)制很有幫助夯到,淺顯易懂,特分享于此耍贾。 什么是事件阅爽? 事件(E...
    jxyjxy閱讀 3,035評(píng)論 1 10