【追尋javascript高手之路05】理解事件流

前言

新的一天又開始了,咱們對今天對將來抱有很大期待,因此開始咱們今天的學習吧,在此以前來點題外話,仍是愛好問題。javascript

週三的面試雖然失敗,可是也是頗有啓迪的,好比以前我就歷來沒有想過愛好問題,我發現個人愛好以下:css

① 霹靂布袋戲html

完了就米有什麼愛好了,其實我徹底能夠說本身愛好旅遊什麼的,我也確實愛好旅遊,只不過比較窮,走的地方很少罷了,這裏又扯出一個話題:java

學習工做與生活jquery

咱們爲了理想須要學習須要工做,工做完了須要生活,如今是前者份量掩蓋了後者,因此之後的世界要注意了。面試

好了進入今天的學習吧,咱們今天來看看javascript裏面的事件吧。瀏覽器

事件交互

javascript與html之間的交互是經過事件實現的,咱們使用偵聽器註冊事件,
在事件發生時候便執行相關的代碼,這就是傳說中的觀察者模式

事件流

事件捕獲與冒泡的出現十分有意思,當瀏覽器發展到第四代時(IE4),出現了一個小意思的問題:框架

頁面的哪一部分有什麼事件?瀏覽器開發團隊也不知道了,想象咱們頁面的元素:dom

1 <div>
2     <a>刀狂劍癡<span>葉小釵</span> </a>
3 </div>

咱們點擊span時候是否是也點擊了a呢?點擊a是否是也點擊了div呢,依次上升到body與html函數

事件流描述的是從頁面中接收事件的順序
IE採用事件冒泡流
標準採用事件捕獲流
最後addEventLister給出了第三個參數同時支持冒泡與捕獲

事件冒泡

由span開始一直傳播至document

事件捕獲

由document向下傳播至span

事件處理程序

事件就是用戶或者瀏覽器自身執行某種動做,好比click、load、mouseover。
而響應某個事件的函數就叫作事件處理程序(事件監聽器),事件處理程序的名字以on開頭,click=>onclick、load=>onload
1 el.addEventListener(eventType, function () {
2 
3 }, false);
4 
5 該方法應用至dom節點
6 第一個參數爲事件名
7 第二個爲事件處理程序
8 第三個爲布爾值,true即是事件捕獲,false爲事件冒泡

既然都說到了addEventLister,咱們來看一看這段代碼吧:

 1 <html xmlns="http://www.w3.org/1999/xhtml">
 2 <head>
 3     <title></title>
 4 </head>
 5 <body>
 6     <div>
 7         <a>刀狂劍癡<span id="el">葉小釵</span> </a>
 8     </div>
 9     <script type="text/javascript">
10         var el = document.getElementById('el');
11         el.addEventListener('click', function () {
12             alert('冒泡');
13         }, false);
14         el.addEventListener('click', function () {
15             alert('捕獲');
16         }, true);
17     </script>
18 </body>
19 </html>

咱們同時給一個dom綁定了click事件,而且一個是冒泡,一個是捕獲,咱們的執行順序是怎麼樣的呢?

答案一:先冒泡再捕獲

可是答案真的是這樣的嗎?咱們將代碼改下:

1 var el = document.getElementById('el');
2 el.addEventListener('click', function () {
3     alert('捕獲');
4 }, true);
5 el.addEventListener('click', function () {
6     alert('冒泡');
7 }, false);

如此一來即是先捕獲再冒泡了,我想說這道題真的好耍賴啊。。。。

那麼如今咱們是離真相近了仍是遠了呢?其實真相依舊撲所迷離:

 1 <html xmlns="http://www.w3.org/1999/xhtml">
 2 <head>
 3     <title></title>
 4 </head>
 5 <body>
 6     <div>
 7         <a id="p">刀狂劍癡<span id="el">葉小釵</span> </a>
 8     </div>
 9     <script type="text/javascript">
10         var el = document.getElementById('el');
11         var p = document.getElementById('p');
12 //el.addEventListener('click', function () {
13 //    alert('捕獲');
14 //}, true);
15 //el.addEventListener('click', function () {
16 //    alert('冒泡');
17 //}, false);
18 p.addEventListener('click', function () {
19     alert('父級 冒泡');
20 }, false);
21 p.addEventListener('click', function () {
22     alert('父級 捕獲');
23 }, true);
24     </script>
25 </body>
26 </html>

此處會出現兩種狀況:

① 直接點擊a標籤,便與以前邏輯一致,哪一個定義到前面哪一個先執行

② 點擊span標籤狀況會有所不一樣!!!

咱們來理一理:

由於咱們span標籤沒有註冊事件,咱們點擊時候他雖然沒有反應,可是他也會向上傳遞的:

 1 <html xmlns="http://www.w3.org/1999/xhtml">
 2 <head>
 3     <title></title>
 4 </head>
 5 <body>
 6     <div>
 7         <a id="p">刀狂劍癡<span id="el" onclick="alert('點擊');">葉小釵</span> </a>
 8     </div>
 9     <script type="text/javascript">
10         var el = document.getElementById('el');
11         var p = document.getElementById('p');
12 
13         p.addEventListener('click', function () {
14             alert('父級 冒泡');
15         }, false);
16         p.addEventListener('click', function () {
17             alert('父級 捕獲');
18         }, true);
19 
20         //el.addEventListener('click', function () {
21         //    alert('捕獲');
22         //}, true);
23         //el.addEventListener('click', function () {
24         //    alert('冒泡');
25         //}, false);
26     </script>
27 </body>
28 </html>

咱們這個代碼執行的順序是:父級捕獲=>點擊=>父級冒泡

說明,點擊span引起的事件便與事件註冊程序的前後無關了,我這裏斗膽的猜想一下整個邏輯

標準瀏覽器默認採用捕獲方式,因此:
咱們點擊span時候,事實上市先點擊document而後到span,中間通過了a標籤便先執行其邏輯(將事件寫到標籤中不合適)
 1 var el = document.getElementById('el');
 2 var p = document.getElementById('p');
 3 
 4 p.addEventListener('click', function () {
 5     alert('父級 冒泡');
 6 }, false);
 7 p.addEventListener('click', function () {
 8     alert('父級 捕獲');
 9 }, true);
10 el.addEventListener('click', function () {
11     alert('冒泡');
12 }, false);
13 el.addEventListener('click', function () {
14     alert('捕獲');
15 }, true);

這段javascript的執行順序是(點擊span):父級捕獲=>冒泡=>捕獲=>父級冒泡

各位看看這段東西有問題沒有,上次要答好寒冬老師這個問題是不可能的。。。

這個問題暫時到這裏,如果後面討論的朋友多咱們再研究吧。

【初窺javascript奧祕之事件冒泡】那些年咱們一塊兒冒的泡

請使用高版本瀏覽器

兼容性處理

咱們知道這塊的兼容性有一點問題,因此咱們通常都會封裝下的,可是裏面的event對象已經target的獲取各位就本身想辦法處理下吧:

 1 var EventUtil = {
 2     addHandler: function (el, type, handler) {
 3         if (el.addEventListener) {
 4             el.addEventListener(type, handler, false);
 5         } else {
 6             el.attachEvent('on' + type, handler);
 7         }
 8     },
 9     removeHandler: function (el, type, handler) {
10         if (el.removeEventListener) {
11             el.removeEventListerner(type, handler, false);
12         } else {
13             el.detachEvent('on' + type, handler);
14         }
15     }
16 };

按道理說,咱們原本可使用call方法將this指向以及event傳進去,完全解決這塊兼容問題,可是又涉及到刪除時候刪不掉而致使性能問題,咱們仍是老老實實用jquery吧。

真的能寫本身框架的話,這塊到能夠好好研究一番!

事件對象

咱們前面常常提到事件對象event,那麼他是什麼呢?

觸發dom上某個事件時,會產生一個事件對象event,這個對象包含着與事件有關的信息
① 致使事件的元素
② 事件類型
③ 特定信息(鼠標信息,鍵盤信息)
......

這個對象包含太多信息,我這裏也記不住,因而挑幾個重要的來講吧:

① currentTarget dom 事件處理程序當前正在處理事件的那個元素(始終等於this)

② preventDefault function 取消事件默認行爲

③ stopPropagation function 取消事件冒泡

④ target dom 事件的目標(這個與currentTarget不是很好區分哦,currentTarget可能發生變化target卻不會)

這裏關於currentTarget與target可能有些朋友沒有分清楚,咱們上個代碼吧:

1 EventUtil.addHandler(document, 'click', function (e) {
2     e = window.event || e;
3     var target = e.target || e.srcElement;
4     //操做
5     //此處currentTarget便等於this
6     //target即是咱們點擊的元素
7     alert(e.currentTarget); //HTMLDocument
8     alert(target); //HTMLSpanElement
9 });

來吧搞點兼容的代碼,反正不吃虧:

 1 var EventUtil = {
 2     addHandler: function (el, type, handler) {
 3         if (el.addEventListener) {
 4             el.addEventListener(type, handler, false);
 5         } else if (el.attachEvent) {
 6             el.attachEvent('on' + type, handler);
 7         } else {
 8             el['on' + type] = handler;
 9         }
10     },
11     removeHandler: function (el, type, handler) {
12         if (el.removeEventListener) {
13             el.removeEventListerner(type, handler, false);
14         } else if (el.detachEvent) {
15             el.detachEvent('on' + type, handler);
16         } else {
17             el['on' + type] = null;
18         }
19     },
20     getEvent: function (e) {
21         return e ? e : window.event;
22     },
23     getTarget: function (e) {
24         return e.target ? e.target : e.srcElement;
25     },
26     preventDefault: function (e) {
27         if (e.preventDefault) {
28             e.preventDefault();
29         } else {
30             e.returnValue = false;
31         }
32     },
33     stopPropagation: function (e) {
34         if (e.stopPropagation) {
35             e.stopPropagation();
36         } else {
37             e.cancelBubble = true;
38         }
39     }
40 };

事件類型

咱們通常有這幾類事件:

① UI用戶界面事件
當用戶與頁面上元素髮生交互時觸發

② 焦點事件
當元素獲取/失去焦點時觸發

③ 鼠標事件
鼠標操做引起的事件

④ 滾輪事件
鼠標滾輪引發的事件,相似的也算

⑤ 文本事件
text類型的操做

⑥ 鍵盤事件
......

咱們下面來看看咱們的各個事件吧。

UI事件

load事件

當頁面所有加載後在window上觸發,當全部框架都加載完畢時在框架集上觸發,當img加載結束後在img上觸發。

javascript中這個傢伙很是常見,頁面徹底加載後(圖片、js、css)就會觸發,來一個圖片加載結束後的提示吧:

1 EventUtil.addHandler(img, 'load', function (e) {
2     e = EventUtil.getEvent(e);
3     alert(EventUtil.getTarget(e).src + ' 加載結束');
4 });

PS:此處的加載與咱們jquery的document.ready有所不一樣,一個是圖片所有加載結束執行,一個是dom與css造成render樹便開始執行,後者是咱們但願的。

unload

這個事件與load對應,咱們卻用到很少,在文檔徹底卸載後觸發(頁面切換也會觸發)。

這個傢伙通常用於釋放資源的,咱們通常不須要關注。

resize

這個事件咱們也用得不少,當瀏覽器高度或者寬度變化時就會觸發(這個時候也會引發頁面迴流reflow哦),事件在window上觸發。

這個事件中的代碼可能會被頻繁執行,因此定義時候必定要注意。

scroll

滾動條滾動時候觸發

焦點事件

① blur: 事情焦點時候觸發,不會冒泡

② focus: 獲取焦點時候觸發,不會冒泡

其它的略去

鼠標事件

事件不少......

後面還有不少新增的事件,我這裏先無論啦。。。等接觸到移動開發再研究吧,今天暫時這樣。

結語

今天咱們一塊兒學習了javascript的事件流,各位有所得麼?

相關文章
相關標籤/搜索