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 取消捕獲
我認為捕獲沒有取消的概念,
事件冒泡時候沖突是這樣的。子級點擊要往東,父級點擊要往西。點擊子級的時候,往東,但是父級認為自己也點擊了,結果是往西。而點擊父級非子級區域時,還是往西。於是矛盾了,只能往西,實現不了往東。
事件是捕獲的話,點擊,父級要往西,子級要往東,結局:往東。而點擊父級的非子級區域時候,往西。往東往西都可以實現。
所以,捕獲沒有向冒泡那樣,還需要取消冒泡,這個概念。這地方是我自己的理解,不一定到位,暫時存疑吧!
