JQuery這種Write Less Do More的框架,用多了難免會對原生js眼高手低。
小菜其實不想寫這篇博客,貌似很初級的樣子,但是看到網絡上連原生js事件綁定和解除都說不明白,還是決定科普一下了。
先聲明,小菜懂的也不是很多,只是把我的思路和大家分享一下。
DOM0事件模型
事件模型不斷發展,早期的事件模型稱為DOM0等級。
DOM0事件模型,所有的瀏覽器都支援。
直接在dom物件上註冊事件名稱,就是DOM0寫法,例如:
document.getElementById("test").onclick = function(e){};
意思是註冊一個onclick事件。當然,它和這種寫法是一個意思:
document.getElementById("test")["onmousemove"] = function(e){};
這沒什麼,只不過是兩種存取js物件屬性的方法,[]的形式主要是為了解決屬性名不是合法的標識符,例如:object.123肯定報錯,但是object["123"]就避免了這個問題,同時,[]的寫法,也把js寫活了,用字串表示屬性名稱,可以在運行時動態綁定事件。
言歸正傳,事件被觸發時,會預設傳入一個參數e,表示事件對象,透過e,我們可以獲得許多有用的信息,例如點擊的座標、具體觸發該事件的dom元素等等。
以DOM0為基礎的事件,對於同一個dom節點而言,只能註冊一個,後邊註冊的同種事件會涵蓋先前註冊的事件。例如:
var btn = document.getElementById("test");
btn.onmousemove = function(e){
alert("ok");
};
btn["onmousemove"] = function(e){
alert("ok1");
};
結果會輸出ok1。
接下來再說一次this。事件觸發時,this就是指該事件在哪個dom物件上觸發。例如:
var btn = document.getElementById("test");
btn.onmousemove = function(e){
alert(this.id);
};
結果輸出test。因為事件就是在id為test的dom節點上註冊的,所以事件觸發時,this當然代表這個dom節點,可以理解為事件是被這個dom節點呼叫的。
所以,想解除事件就相當簡單了,只需要再註冊一次事件,把數值設為null,例如:
var btn = document.getElementById("test");
btn.onclick = function(e){
alert("ok");
};
btn.onclick = null;
原則是最後註冊的事件要覆蓋之前的,最後一次註冊事件設定成null,也就解除了事件綁定。
事情還沒結束,DOM0事件模型也牽涉到直接寫在html中的事件。例如:
以這種方式註冊的事件,同樣遵循覆蓋原則,同樣只能註冊一個,最後一個生效。
差異就是,這樣註冊的事件,相當於動態呼叫函數(有點eval的意思),因此不會傳入event對象,同時,this指向的是window,不再是觸發事件的dom對象。
DOM2事件模型
DOM2事件模型相對於DOM0,小菜僅了解以下兩點:
· DOM2支援同一dom元素註冊多個同種事件。
· DOM2新增了捕捉與冒泡的概念。
DOM2事件透過addEventListener和removeEventListener管理,當然,這是標準。
但IE8及其以下版本瀏覽器,自娛自樂,搞出了對應的attachEvent和detachEvent,由於小菜才疏學淺,本文不做討論。
addEventListener當然就是註冊事件,她有三個參數,分別為:"事件名稱", "事件回呼", "捕捉/冒泡"。舉例:
var btn = document.getElementById("test");
btn.addEventListener("click", function(e){
alert("ok");
}, false);
事件名稱就不用多說了,相較於DOM0,去掉了前邊的on而已。
事件回呼也很容易理解,事件觸發了總得通知你吧!回呼時和DOM0一樣,也會預設傳入一個event參數,同時this是指觸發該事件的dom節點。
最後一個參數是布林型,true代表擷取事件,false代表冒泡事件。其實很好理解,先來個示意圖:
意思是說,某個元素觸發了某個事件,最先得到通知的是window,然後是document,依次而入,直到真正觸發事件的那個元素(目標元素)為止,這個過程就是捕獲。接下來,事件會從目標元素開始起泡,再依序而出,直到window物件為止,這個過程就是冒泡。
為什麼要這樣設計?這似乎是由於深厚的歷史淵源,小菜也不怎麼了解,就不亂說了。
由此可看出,捕捉事件比冒泡事件先觸發。
假設有這樣的html結構:
然後我們在外層div上註冊兩個click事件,分別是捕捉事件和冒泡事件,程式碼如下:
var btn = document.getElementById("test");
//捕獲事件
btn.addEventListener("click", function(e){
alert("ok1");
}, true);
//冒泡事件
btn.addEventListener("click", function(e){
alert("ok");
}, false);
最後,點選內層的div,先彈出ok1,然後彈出ok。結合上邊的原理圖,外層div相當於圖中的body,內層div相當於圖中最下邊的div,證明了捕獲事件先執行,然後執行冒泡事件。
為什麼要強調點選內層的div呢?因為真正觸發事件的dom元素,必須是內層的,外層dom元素才有機會模擬捕捉事件和冒泡事件,從原理圖上就看出了。
如果在真正觸發事件的dom元素上註冊捕獲事件和冒泡事件呢?
html結構同上,js程式碼如下:
var btnInner = document.getElementById("testInner");
//冒泡事件
btnInner.addEventListener("click", function(e){
alert("ok");
}, false);
//捕獲事件
btnInner.addEventListener("click", function(e){
alert("ok1");
}, true);
當然還是點選內層div,結果是先彈出ok,再彈出ok1。理論上應該先觸發捕獲事件,也就是先彈出ok1,但這裡比較特殊,因為我們是在真正觸發事件的dom元素上註冊的事件,相當於在圖中的div上註冊,由圖可以看出真正觸發事件的dom元素,是捕捉事件的終點,是冒泡事件的起點,所以這裡就不區分事件了,哪個先註冊,就先執行哪個。本例中,冒泡事件先註冊,所以先執行。
這個道理適用於多個同種事件,比如說一下子註冊了3個冒泡事件,那麼執行順序就按照註冊的順序來,先註冊先執行。例如:
var btnInner = document.getElementById("testInner");
btnInner.addEventListener("click", function(e){
alert("ok");
}, false);
btnInner.addEventListener("click", function(e){
alert("ok1");
}, false);
btnInner.addEventListener("click", function(e){
alert("ok2");
}, false);
結果當然依序彈出ok、ok1、ok2。
為了進一步理解事件模型,還有一種場景,假如說外層div和內層div同時註冊了捕獲事件,那麼點擊內層div時,外層div的事件一定是先觸發的,代碼如下:
var btn = document.getElementById("test");
var btnInner = document.getElementById("testInner");
btnInner.addEventListener("click", function(e){
alert("ok");
}, true);
btn.addEventListener("click", function(e){
alert("ok1");
}, true);
結果是先彈出ok1。
假如外層div和內層div都是註冊的冒泡事件,點選內層div時,一定是內層div事件先執行,原理相同。
細心的讀者會發現,對於div嵌套的情況,如果點擊內層的div,外層的div也會觸發事件,這貌似會有問題!
點選的明明是內層div,但外層div的事件也觸發了,這的確是個問題。
其實,事件觸發時,會預設傳入一個event對象,前邊提過了,這個event對像上有一個方法:stopPropagation,透過此方法,可以阻止冒泡,這樣外層div就接收不到事件了。程式碼如下:
var btn = document.getElementById("test");
var btnInner = document.getElementById("testInner");
btn.addEventListener("click", function(e){
alert("ok1");
}, false);
btnInner.addEventListener("click", function(e){
//阻止冒泡
e.stopPropagation();
alert("ok");
}, false);
終於要說怎麼解除事件了。解除事件語法:btn.removeEventListener("事件名稱", "事件回呼", "擷取/冒泡");
這和綁定事件的參數一樣,詳細說明下:
· 事件名稱,即說明解除哪個事件唄。
· 事件回呼,且是一個函數,而這個函數必須與註冊事件的函數是同一個。
· 事件類型,布林值,此必須與註冊事件時的類型一致。
也就是說,名稱、回呼、類型,三者共同決定解除哪個事件,缺一不可。舉例:
var btn = document.getElementById("test");
//將回呼儲存在變數中
var fn = function(e){
alert("ok");
};
//綁定
btn.addEventListener("click", fn, false);
//解除
btn.removeEventListener("click", fn, false);
若要註冊過的事件能夠解除,必須將回呼函數儲存起來,否則無法解除。
DOM0與DOM2混用
事情本來就很亂了,這又來個混合使用,還讓不讓人活了。 。 。
別怕,混合使用完全沒問題,DOM0模型和DOM2模型各自遵循自己的規則,互不影響。
整體來說,依然是哪個先註冊,哪個先執行,其他就沒什麼了。
後記
至此,原生js事件已經講的差不多了,小菜僅知道這些而已,歡迎讀者補充其他知識點。
在實際應用中,真正的行家不會傻傻的真的註冊這麼多事件,一般情況下,只需在最外層dom元素註冊一次事件,然後通過捕獲、冒泡機制去找到真正觸發事件的dom元素,最後根據觸發事件的dom元素提供的資訊去呼叫回呼。
也就是說,行家會自己管理事件,而不依賴瀏覽器去管理,這樣即可以提高效率,又保證了兼容性,JQuery不就是這麼做的嘛~
好了,教學到此結束,希望對讀者有幫助!

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

Atom編輯器mac版下載
最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1
強大的PHP整合開發環境

WebStorm Mac版
好用的JavaScript開發工具