JS中事件綁定函數,事件捕獲,事件冒泡


1 事件綁定:事件與函數綁定以及怎么取消綁定

1.1 元素.onclick這種形式,如下:

1         <div id="div1">aaa</div>
2         
3         <script type="text/javascript">
4             var oDiv1 = document.getElementById('div1');
5             oDiv1.onclick = function(){
6                 alert(this.innerHTML); //將會彈出aaa
7             }
8         </script>

這種綁定不存在什么兼容性問題,但是如果想綁定2個事件,就麻煩了。上面的js代碼改成如下:

1             var oDiv1 = document.getElementById('div1');
2             oDiv1.onclick = function(){
3                 alert(this.innerHTML); //將會彈出aaa
4             }
5             oDiv1.onclick = function(){
6                 alert(this.nodeType);
7             }

我們給oDiv1后面又綁定了onclick,前面的那個alert(this.innerHTML)就會失效。

這種方式綁定了,后面的代碼需要取消這個綁定,哪怎么取消呢

1 //取消剛才的綁定
2 oDiv1.onclick = null;

當然,這里只是很簡單的兩個函數例子,可以很容易將2個onclick合並,但是如果是多人合作,代碼多,結構復雜,還是看看其他的事件和函數綁定的方式吧。 

 

1.2 元素.attachEvent 和 元素.addEventListener進行事件和函數綁定和取消,如下:

這種方式存在瀏覽器兼容性問題

/* 總結
ie6/7/8 :obj.attachEvent(事件名稱,事件函數);
    1.沒有捕獲(事件捕獲在后面講,是和冒泡相反的,IE不支持)
    2.事件名稱帶有on,如這里是onclick 
    3.事件函數執行的順序是反着的,后綁定的那個函數先執行
    4.事件函數中this指向window,這不是本意,帶來了不方便
標准瀏覽器及高版本IE:obj.addEventListener(事件名稱,事件函數,是否捕獲);
    1.有捕獲
    2.事件名稱不帶on
    3.事件執行的順序是正常的
    4.this觸發該事件的對象
*/

上面的代碼繼續改,如下:

下面的代碼只能在IE6.7.8.9下運行,但是ie9也有addEventListener方法,就是IE9下attachEvent和addEventListener都行

 1         <div id="div1">aaa</div>
 2         
 3         <script type="text/javascript">
 4             var oDiv1 = document.getElementById('div1');
 5             //改的是這里,事件和函數進行綁定
 6             oDiv1.attachEvent('onclick',function(){
 7                 //這里的this變成了window,也不能用this.innerHTML了
 8                 alert(oDiv1.innerHTML);                
 9             });
10             oDiv1.attachEvent('onclick',function(){
11                 alert(oDiv1.nodeType);                
12             });            
13         </script>

 

再看看標准瀏覽器,如谷歌火狐瀏覽下怎么寫

 1         <div id="div1">aaa</div>
 2         
 3         <script type="text/javascript">
 4             var oDiv1 = document.getElementById('div1');
 5             //改的是這里,事件和函數進行綁定,IE.6.7.8不支持addEventListener
 6             oDiv1.addEventListener('click',function(){
 7                 //在標准瀏覽器下,this還是只oDiv1
 8                 alert(this.innerHTML);                
 9             });//addEventListener第三個參數沒寫,默認:冒泡
10             oDiv1.addEventListener('click',function(){
11                 alert(this.nodeType);                
12             });            
13         </script>

 

存在兼容性問題,我們封裝一個函數進行事件綁定操作

 1         <div id="div1">aaa</div>
 2         
 3         <script type="text/javascript">
 4             var oDiv1 = document.getElementById('div1');
 5             
 6             //改的是這里,封裝了一個bind函數來解決
 7             function bind(obj,evname,fn){
 8                 if(obj.addEventListener){ //IE9+、谷歌、火狐等
 9                     obj.addEventListener(evname,fn);
10                 }else{//IE6.7.8,雖然IE9也支持attachEvent,但是IE9用上面的addEventListener了
11                     obj.attachEvent('on'+evname,function(){
12                         fn.call(obj);
13                     });
14                 }
15             }
16 
17             //使用
18             bind(oDiv1, 'click', function(){
19                 alert(this.innerHTML);    
20             });
21             bind(oDiv1, 'click', function(){
22                 alert(this.nodeType);    
23             });
24         </script>

 

那怎么取消掉這種方式綁定呢?

/*
            老ie : obj.detachEvent(事件名稱,事件函數);
            標准    : obj.removeEventListener(事件名稱,事件函數,是否捕獲);
            */
/**

上面事件綁定的時候,我沒有給函數命名,是個匿名函數。那匿名函數看着是一樣的代碼,確是不同的對象,就沒有辦法取消的了。
*/ oDiv1.addEventListener('click', fn1, true); // 綁定的時候是true,取消的時候,最后那個參數也需要為true,為false則取消不掉 // 下面fn1改為fn2不報錯,雖然document沒有綁定fn2,但是改為fn3就不行了,報錯:不存在fn3這個函數 oDiv1.removeEventListener('click', fn1, true);
            var oDiv1 = document.getElementById('div1');
            
            oDiv1.addEventListener('click',function(){
                alert(this.innerHTML);
            });
    //取消不了,因為匿名函數沒有抓手,得用有名字的函數
            oDiv1.removeEventListener('click',function(){
                alert(this.innerHTML);
            });

而上面這個換成下面這樣就起效果。

            var oDiv1 = document.getElementById('div1');
            
            oDiv1.addEventListener('click',fn1});
            //這樣點擊就沒有效果,因為取消掉了
            oDiv1.removeEventListener('click',fn1);
            
            function fn1(){
                alert(this.innerHTML);
            }    

2. 事件冒泡和取消冒泡

2.1 事件冒泡

比如:div1里面有div2,div2里又有div3。那么當點擊div3時,系統會認為div2也點了,div1也點了,body也點了,document也點了,一直到window。一層一層往外冒泡。具體內容如下:

 1     <style>
 2         div   {padding: 40px;}
 3         #div1 {background:red}
 4         #div2 {background:green}
 5         #div3 {background:blue; position: absolute; top: 300px;}
 6     </style>
 7     <div id="div1">
 8         <div id="div2">
 9             <div id="div3"></div>
10         </div>
11     </div>
12     <script>
13         window.onload = function() {
14     
15             //事件冒泡 : 當一個元素接收到事件的時候,會把他接收到的所有傳播給他的父級,一直到頂層window.事件冒泡機制
16             /*
17              實例來說:
18              下面的例子,從html中來看,div3是div2的子元素,div2又是div1的子元素,div1是body的子元素
19             當div3發生事件的時候(這里是onclick),瀏覽器會認為div2也發生了該事件,div1也發生了該事件
20             就是事件從子級朝父級冒泡
21             同時,注意,div3加上絕對定位,視覺上在div1中也不行,點擊div3,div1還是會認為被點擊了
22              
23             */
24     
25             var oDiv1 = document.getElementById('div1');
26             var oDiv2 = document.getElementById('div2');
27             var oDiv3 = document.getElementById('div3');
28     
29             function fn1() {
30                 alert(this.id);
31             }
32     
33             //事件函數綁定
34             oDiv1.onclick = fn1; //告訴div1,如果他接收到了一個點擊事件,那么他就去執行fn1
35             oDiv2.onclick = fn1;
36             oDiv3.onclick = fn1;
37     
38         }
39     </script>

 

那有個問題,在不知道事件冒泡這個事情的時候,也正常用,沒出什么問題呀?

答:因為大多數時候,子級點擊發生事件就是我們最終的效果,會傳到父級身上產生事件,但是父級沒綁定函數,也就沒有反應,我們也就感覺不到干嘛要學到用到這一塊的東西。但是,子級和父級產生沖突的時候,就需要了解原因,取消冒泡了。

子級和父級同一事件沖突舉例:

點擊某按鈕,希望彈出一個div層,而點擊頁面中任何除去div層一部分,希望div層隱藏。這里,點擊按鈕和div層都會冒泡到document,就等同於告訴document,點擊后彈出div層,而后面點擊頁面中任何一部分,又希望隱藏div層,document接到完全矛盾的命令,怎么辦?只能是點擊按鈕和div層的時候,取消冒泡,這樣,document就不覺得自己被點擊了。怎么取消冒泡?如下:

 

2.2 取消冒泡:事件綁定函數中加上ev.cancelBubble = true;

上面的代碼中,fn1修改下就可以了,如下:

1             function fn1(ev) {
2                 var ev = ev || window.event;
3                 ev.cancelBubble = true;
4                 alert(this.id);
5             }

 

fn1更換過后,點擊div3時,由原來的彈出3次,分別是div3,div2,div1,變成了彈出1次,div3,為何?因為取消冒泡了,div2和div1不認為自己被點擊了,也就不調用函數了,也就不彈了。

3 事件捕獲

3.1 事件捕獲

事件捕獲是一個和事件冒泡反着的過程。

冒泡是:點了兒子,兒子覺得被點了(如果綁定了函數,函數執行),然后老子也覺得被點了(如果綁定了函數,函數執行)。

捕獲是:點了兒子,老子先覺得被點了(如果綁定了函數,函數執行),然后兒子也覺得被點了(如果綁定了函數,函數執行)。

順序不同,老舊的IE瀏覽器沒有事件捕獲的概念,只有冒泡。元素.onclick這種形式的綁定時,就是事件冒泡。

在火狐,谷歌,IE9+瀏覽器下:

//第三個參數,默認false,也就是冒泡。如果第三個參數為true,則為事件捕獲
obj.removeEventListener(事件名稱,事件函數,是否捕獲);

 

 

3.2 取消捕獲

我認為捕獲沒有取消的概念,

事件冒泡時候沖突是這樣的。子級點擊要往東,父級點擊要往西。點擊子級的時候,往東,但是父級認為自己也點擊了,結果是往西。而點擊父級非子級區域時,還是往西。於是矛盾了,只能往西,實現不了往東。

事件是捕獲的話,點擊,父級要往西,子級要往東,結局:往東。而點擊父級的非子級區域時候,往西。往東往西都可以實現。

所以,捕獲沒有向冒泡那樣,還需要取消冒泡,這個概念。這地方是我自己的理解,不一定到位,暫時存疑吧!

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM