国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

理解JavaScript中的事件路由冒泡過(guò)程及委托代理機(jī)制

瀏覽:17日期:2023-11-23 08:51:46

當(dāng)我用純CSS實(shí)現(xiàn)這個(gè)以后。我開始用JavaScript和樣式類來(lái)完善功能。

然后,我有一些想法,我想使用Delegated Events (事件委托)但是我不想有任何依賴,插入任何庫(kù),包括jQuery。我需要自己實(shí)現(xiàn)事件委托了。

我們先來(lái)看看事件委托到底是什么?他們是怎么工作的,怎么去實(shí)現(xiàn)這種機(jī)制。

好,它解決了什么問(wèn)題?

我們先看個(gè)簡(jiǎn)單的例子。

先假設(shè)我們有一組按鈕,我一次點(diǎn)擊一個(gè)按鈕,然后我希望被點(diǎn)中的狀態(tài)設(shè)為"active"。再次點(diǎn)擊時(shí)取消active。

然后,我們可以寫一些HTML:

<ul class="toolbar"> <li><button class="btn">Pencil</button></li> <li><button class="btn">Pen</button></li> <li><button class="btn">Eraser</button></li></ul>

我可以用一些標(biāo)準(zhǔn)的Javascript事件處理上面的邏輯:

var buttons = document.querySelectorAll(".toolbar .btn");for(var i = 0; i < buttons.length; i++) { var button = buttons[i]; button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); });}

看上去不錯(cuò),但是它其實(shí)不能像你期望的那樣工作。

閉包的陷阱

如果你有一定的JavaScript開發(fā)經(jīng)驗(yàn),這個(gè)問(wèn)題就很明顯了。

對(duì)于外行來(lái)說(shuō)button變量是被封閉的,每次都會(huì)找到對(duì)應(yīng)的button……但是其實(shí)這里只有一個(gè)button;每次循環(huán)都會(huì)被重新分配。

第一個(gè)循環(huán)它指向第一個(gè)button,接下來(lái)是第二個(gè)。但當(dāng)你點(diǎn)擊時(shí)button變量永遠(yuǎn)只指向最后一個(gè)button元素,問(wèn)題出在這。

我們需要的是一個(gè)穩(wěn)定的作用域;讓我們重構(gòu)一下。

var buttons = document.querySelectorAll(".toolbar button");var createToolbarButtonHandler = function(button) { return function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); };};for(var i = 0; i < buttons.length; i++) { buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i]));}

注* 上面這段代碼結(jié)構(gòu)有點(diǎn)復(fù)雜,也可以簡(jiǎn)單直接地使用一個(gè)閉包,封閉保存當(dāng)前的button變量,如下所示:

var buttons = document.querySelectorAll(".toolbar .btn");for(var i = 0; i < buttons.length; i++) { (function(button) { button.addEventListener("click", function() { if(!button.classList.contains("active"))button.classList.add("active"); elsebutton.classList.remove("active"); }); })(buttons[i])}

現(xiàn)在它能正常工作了。指向永遠(yuǎn)是正確的button

那么這個(gè)方案有什么問(wèn)題?

這個(gè)方案看上去還可以,然而我們確實(shí)可以做得更好。

首先我們創(chuàng)建了太多的處理函數(shù)。為每一個(gè)匹配的.toolbar button綁定了一個(gè)事件偵聽和一個(gè)回調(diào)處理。假如只有三個(gè)按鈕這種資源分配是可以忽略的。

然而,如果我們有1000個(gè)呢?

<ul class="toolbar"> <li><button id="button_0001">Foo</button></li> <li><button id="button_0002">Bar</button></li> // ... 997 more elements ... <li><button id="button_1000">baz</button></li></ul>

它也不會(huì)崩潰,但是這并不是最佳的方案。我們分配了大量不必要的函數(shù)。讓我們重構(gòu)一下,僅附加一次,即僅綁定一個(gè)函數(shù)(function),去處理這種有可能的數(shù)千次調(diào)用。

相對(duì)于封閉button變量去存儲(chǔ)當(dāng)時(shí)我們點(diǎn)擊的對(duì)象,我們可以使用event對(duì)象去獲取當(dāng)時(shí)點(diǎn)擊的對(duì)象。

event對(duì)象有一些元數(shù)據(jù),在多次綁定的種情況下,我們可以使用currentTarget獲取當(dāng)前綁定的對(duì)象,如上例的代碼就可以改成:

var buttons = document.querySelectorAll(".toolbar button");var toolbarButtonHandler = function(e) { var button = e.currentTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active");};for(var i = 0; i < buttons.length; i++) { button.addEventListener("click", toolbarButtonHandler);}

不錯(cuò)!不過(guò)這只是簡(jiǎn)化了單個(gè)函數(shù),讓它得更具可讀性,然而它還是被綁定了多次。

但是,我們還可以做得更好。

讓我們假設(shè)一下,我們?cè)谶@個(gè)列表里動(dòng)態(tài)地添加了一些按鈕。然后我們還要為這些動(dòng)態(tài)元素添加和移除事件綁定。然后我們還要持久化這些處理函數(shù)和當(dāng)前上下文要用到的變量,這事聽上去就不靠譜。

也許還有其他方法。

讓我們先全面理解一下事件的工作原理,以及他們?cè)贒OM里是怎樣傳遞的。

事件的工作原理

當(dāng)用戶點(diǎn)擊一個(gè)元素時(shí),一個(gè)事件就會(huì)被產(chǎn)生去通知用戶當(dāng)前的行為。事件在分發(fā)派遣時(shí)會(huì)有三個(gè)階段:

捕獲階段: Capturing 觸發(fā)階段: Target冒泡階段: Bubbling

這個(gè)事件起始從document之前然后一路向下找到當(dāng)前事件點(diǎn)擊到的對(duì)象。當(dāng)事件達(dá)到點(diǎn)擊到的對(duì)象之后,它會(huì)按原路返回(冒泡過(guò)程),直到退出整個(gè)DOM樹。

這里是一個(gè)HTML的例子:

<html><body> <ul> <li id="li_1"><button id="button_1">Button A</button></li> <li id="li_2"><button id="button_2">Button B</button></li> <li id="li_3"><button id="button_3">Button C</button></li> </ul></body></html>

當(dāng)你單擊Button A時(shí),事件經(jīng)過(guò)的路徑會(huì)向下面這樣:

START| #document | HTML|| BODY } CAPTURE PHASE| UL || LI#li_1 /| BUTTON <-- TARGET PHASE| LI#li_1 | UL || BODY } BUBBLING PHASE | HTML|v #document /END

注意,這意思著你可以在事件的經(jīng)過(guò)路徑上捕獲到你單擊所產(chǎn)生的事件,我們非常確定這個(gè)事件一定會(huì)經(jīng)過(guò)他們的父元素ul元素。我們可以將我們的事件處理綁定到父元素上面,然后簡(jiǎn)化我們的解決方案,這個(gè)就叫事件的委托及代理(Delegated Events)。

注* 其實(shí)Flash/Silverlight/WPF開發(fā)的事件機(jī)制是非常近似的,這里有一張他們的事件流程圖。 除了Silverlight 3使用了舊版IE的僅有冒泡階段的事件模型外,基本上也都有這三個(gè)階段。(舊版IE和SL3的事件處理只有一個(gè)從觸發(fā)對(duì)象冒泡到根對(duì)象的過(guò)程,可能是為了簡(jiǎn)化事件的處理機(jī)制。)

理解JavaScript中的事件路由冒泡過(guò)程及委托代理機(jī)制

事件委托代理

委托(代理)事件是那些被綁定到父級(jí)元素的事件,但是只有當(dāng)滿足一定匹配條件時(shí)才會(huì)被挪。

讓我們看一個(gè)具體的例子,我們看看上文的那個(gè)工具欄的例子:

<ul class="toolbar"> <li><button class="btn">Pencil</button></li> <li><button class="btn">Pen</button></li> <li><button class="btn">Eraser</button></li></ul>

因?yàn)槲覀冎绬螕鬮utton元素會(huì)冒泡到UL.toolbar元素,讓我們將事件處理放到這里試試。我們需要稍微調(diào)整一下:

var toolbar = document.querySelector(".toolbar");toolbar.addEventListener("click", function(e) { var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active");});

這樣我們清理了大量的代碼,再也沒(méi)有循環(huán)了。注意我們使用了e.target代替了之前的e.currentTarget。這是因?yàn)槲覀冊(cè)谝粋€(gè)不同的層次上面進(jìn)行了事件偵聽。

e.target 是當(dāng)前觸發(fā)事件的對(duì)象,即用戶真正單擊到的對(duì)象。e.currentTarget 是當(dāng)前處理事件的對(duì)象,即事件綁定的對(duì)象。

在我們的例子中e.currentTarget就是UL.toolbar。

注* 其實(shí)不止事件機(jī)制,在整個(gè)UI構(gòu)架上FLEX(不是Flash) /Silverlight /WPF /Android的實(shí)現(xiàn)跟WEB也非常相似,都使用XML(HTML)實(shí)現(xiàn)模板及元素結(jié)構(gòu)組織,Style(CSS)實(shí)現(xiàn)顯示樣式及UI,腳本(AS3,C#,Java,JS)實(shí)現(xiàn)控制。不過(guò)Web相對(duì)其他平臺(tái)更加開放,不過(guò)歷史遺留問(wèn)題也更多。但是幾乎所有的平臺(tái)都支持Web標(biāo)準(zhǔn),都內(nèi)嵌有類似WebView這樣的內(nèi)嵌Web渲染機(jī)制,相對(duì)各大平臺(tái)復(fù)雜的前端UI框架和學(xué)習(xí)曲線來(lái)說(shuō),使用Web技術(shù)實(shí)現(xiàn)Native APP的前端UI是非常低成本的一項(xiàng)選擇。

原文地址: codepen.io

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: www中文字幕 | 免费人成在线观看视频不卡 | 国产一区二区三区欧美精品 | 看真人视频一级毛片 | 免费观看性欧美毛片 | 三级亚洲| 国产视频二区在线观看 | 久草网视频在线观看 | 天天澡天天碰天天狠伊人五月 | 国产欧美视频综合二区 | 国产女厕偷窥系列在线视频 | 亚洲免费专区 | 国内精品福利在线视频 | 香港经典a毛片免费观看看 香港经典a毛片免费观看爽爽影院 | 成人精品一区二区三区 | 久久精品视频一区二区三区 | 日韩精品一区二区三区在线观看l | 日韩a一级欧美一级 | 欧美 日韩 国产在线 | 久久福利资源站免费观看i 久久高清精品 | 欧美大片毛片aaa免费看 | 成人免费大片黄在线观看com | 99在线观看巨臀大臀视频 | 国产亚洲一区二区在线观看 | 在线欧美色 | 综合视频在线 | 99久久免费视频在线观看 | 国产美女视频做爰 | 国产一区二区亚洲精品天堂 | 亚洲午夜片 | 精品久久久中文字幕一区 | 国产亚洲欧美在线人成aaaa | 国产精品欧美一区二区三区不卡 | 国产精品亚洲欧美日韩区 | 久久青草免费线观最新 | 99视频在线免费 | 国产毛片基地 | 末成年娇小性色xxxxx | 伊人婷婷色香五月综合缴激情 | 绝对真实偷拍盗摄高清在线视频 | 亚洲国产成人久久综合野外 |