javascript事件基礎知識

什麼是事件

javascript與HTML之間交互就是經過事件實現的,事件就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。事件在瀏覽器中是以對象的形式存在的,即event,觸發一個事件,就會產生一個事件對象event,該對象包含着全部與事件有關的信息,包括致使事件的元素、事件的類型以及其餘與特定事件相關的信息。javascript

DOM元素添加事件

1.DOM元素事件屬性

<button onclick="clickFun()">addEvent</button>

2.javascript腳本添加事件

a. element.onclick=function(e){}
b. element.addEventListener(event,function(e),useCapture)java

  • 適用範圍:現代瀏覽器(IE9+, Firefox , chorme, safari, opera)
  • 參數介紹:event必須,事件名稱。(注:不含「on」,好比「click」、「mouseover」等)。function必須,指定事件觸發時執行的函數。useCapture可選,布爾值,指定事件是否在捕獲或冒泡階段執行,默認false(冒泡)。

c. element.attachEvent(event,function(e))node

  • 適用範圍:IE 六、七、8
  • 參數介紹:event 必須,字符串,事件名稱。(注:含「on」,好比「onclick」、「onmouseover」等)。function必須,指定事件觸發時執行的函數。
<button id="myBtn">點擊這裏</button>
<script>
  document.getElementById("myBtn").onclick=function(){clickFun()};

  document.getElementById("myBtn").addEventListener("click",function(event){
    clickFun();
   },false);  //採用事件冒泡方式給dom元素註冊click事件

  document.getElementById("myBtn").addEventListener("click",function(event){
     clickFun();
   },true);  //採用事件捕獲方式給dom元素註冊click事件

  document.getElementById("myBtn").attachEvent("click",function(event){
     clickFun();
   });

</script>

擴展:原生Javascript寫法,跨瀏覽器添加事件瀏覽器

var EventUtil = {
     // 添加事件監聽
      add: function(element, type, callback){
          if(element.addEventListener){
             element.addEventListener(type, callback, false);
          } else if(element.attachEvent){
              element.attachEvent('on' + type, callback);
         } else {
             element['on' + type] = callback;
         }
     }
     // 移除事件監聽
    remove: function(element, type, callback){
         if(element.removeEventListener){
             element.removeEventListener(type, callback, false);
         } else if(element.detachEvent){
             element.detachEvent('on' + type, callback);
         } else {
             element['on' + type] = null;
         }
     }
 }
<button id="myBtn">點擊這裏</button>
 <script>
    var myBtn = document.getElementById("myBtn");
    EventUtil.add(myBtn, 'click', function(){
      console.log('被點擊了');
 });

事件捕獲、事件冒泡

事件捕獲

事件捕獲(event capturing):當鼠標點擊或者觸發dom事件時,瀏覽器會從根節點開始由外到內進行事件傳播,即點擊了子元素,若是父元素經過事件捕獲方式註冊了對應的事件的話,會先觸發父元素綁定的事件。性能優化

clipboard.png

事件冒泡

事件冒泡(dubbed bubbling):與事件捕獲偏偏相反,事件冒泡順序是由內到外進行事件傳播,直到根節點。app

clipboard.png

注:不管是事件捕獲仍是事件冒泡,它們都有一個共同的行爲,就是事件傳播dom

阻止事件捕獲/事件冒泡

event.stopPropagation()   
 event.stopImmediatePropagation()
  • event.stopPropagation()
    會阻止事件繼續分發到其餘document節點,可是當前節點綁定的多個事件會繼續按註冊的順序執行
  • event.stopImmediatePropagation()
    不只阻止事件繼續分發到其餘document,還會將事件分發就地中止,在當前事件以後註冊的其餘事件,都不會執行

舉個栗子:此栗子是阻止事件捕獲,阻止事件冒泡原理同樣函數

<div id="div1">
   <button id="button1">測試</button>
</div>

<script type="application/javascript">
    var div1 = document.getElementById("div1");
    var btn = document.getElementById("button1");
    div1.addEventListener("click", function () {
        alert("div1第一次執行");
        event.stopPropagation();
    }, true);
    div1.addEventListener("click", function () {
        alert("div1第二次執行");
    }, true);
    btn.addEventListener("click", function (event) {
        alert("button 執行");
    }, true);
</script>

//執行結果 alert("div1第一次執行"); alert("div1第二次執行");
<div id="div1">
   <button id="button1">測試</button>
</div>

<script type="application/javascript">
    var div1 = document.getElementById("div1");
    var btn = document.getElementById("button1");
    div1.addEventListener("click", function () {
        alert("div1第一次執行");
        event.stopImmediatePropagation();
    }, true);
    div1.addEventListener("click", function () {
        alert("div1第二次執行");
    }, true);
    btn.addEventListener("click", function (event) {
        alert("button 執行");
    }, true);
</script>

//執行結果 alert("div1第一次執行");

事件代理

什麼是事件代理

事件代理也能夠稱之爲事件委託,事件代理就是在祖先級DOM元素綁定一個事件,當觸發子孫級DOM元素的事件時,利用事件冒泡的原理來觸發綁定在祖先級DOM的事件。性能

那這是什麼意思呢?網上的各位大牛們講事件代理基本上都用了同一個例子,就是取快遞來解釋這個現象,借花獻佛,你們認真領會一下事件代理究竟是一個什麼原理:
有三個同事預計會在週一收到快遞。爲簽收快遞,有兩種辦法:一是三我的在公司門口等快遞;二是委託給前臺MM代爲簽收。現實當中,咱們大都採用委託的方案。前臺MM收到快遞後,她會判斷收件人是誰,而後按照收件人的要求籤收,甚至代爲付款。這種方案還有一個優點,那就是即便公司裏來了新員工,前臺MM也會在收到寄給新員工的快遞後覈實並代爲簽收。測試

這裏其實有2層意思的:
第一,如今委託前臺的同事是能夠代爲簽收的,即程序中的現有的dom節點是有事件的;
第二,新員工也是能夠被前臺MM代爲簽收的,即程序中新添加的dom節點也是有事件的。

爲何要用事件代理

通常來講,dom須要有事件處理程序,咱們都會直接給它設事件處理程序就行了,那若是是不少的dom須要添加事件處理呢?好比咱們有100個li,每一個li都有相同的click點擊事件,可能咱們會用for循環的方法,來遍歷全部的li,而後給它們添加事件,那這麼作會存在什麼影響呢?

在JavaScript中,添加到頁面上的事件處理程序數量將直接關係到頁面的總體運行性能,由於須要不斷的與dom節點進行交互,訪問dom的次數越多,引發瀏覽器重繪與重排的次數也就越多,就會延長整個頁面的交互就緒時間,這就是爲何性能優化的主要思想之一就是減小DOM操做的緣由;若是要用事件代理,就會將全部的操做放到js程序裏面,與dom的操做就只須要交互一次,這樣就能大大的減小與dom的交互次數,提升性能;

每一個函數都是一個對象,是對象就會佔用內存,對象越多,內存佔用率就越大,天然性能就越差了(內存不夠用,是硬傷),好比上面的100個li,就要佔用100個內存空間,若是是1000個,10000個呢,那隻能呵呵了,若是用事件代理,那麼咱們就能夠只對它的父級(若是隻有一個父級)這一個對象進行操做,這樣咱們就須要一個內存空間就夠了,是否是省了不少,天然性能就會更好。

事件代理的實現

在介紹事件代理的方法以前,先來看一段通常方法的栗子:

<ul id="ulDelegate">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

<script>
  window.onload = function(){
     var ulDelegate= document.getElementById("ulDelegate");
     var liDelegate= ulDelegate.getElementsByTagName('li');
     for(var i=0;i<liDelegate.length;i++){
         liDelegate[i].onclick = function(){
             alert('noDelegate');
         }
     }
  }
</script>

上面的代碼很簡單,咱們看看有多少次的dom操做,首先要找到ul,而後遍歷li,而後點擊li的時候,又要找一次目標的li的位置,才能執行最後的操做,每次點擊都要找一次li。

用事件代理的方式來實現,上栗子:

window.onload = function(){
    var ulDelegate= document.getElementById("ulDelegate");
    ulDelegate.onclick = function(){
         alert('Delegate');
    }
}

這裏用父級ul作事件處理,當li被點擊時,因爲冒泡原理,事件就會冒泡到ul上,由於ul上有點擊事件,因此事件就會觸發,固然,這裏當點擊ul的時候,也是會觸發的。

那麼問題就來了,若是我想讓事件代理的效果跟直接給節點的事件效果同樣怎麼辦,好比說只有點擊li纔會觸發,這時,Event對象就派上用場了,Event提供了一個屬性叫target,能夠返回事件的目標節點,咱們稱爲事件源,也就是說,target就能夠表示爲當前的事件操做的dom,可是不是真正操做dom,這個是有兼容性的,標準瀏覽器用ev.target,IE瀏覽器用event.srcElement,此時只是獲取了當前節點的位置,並不知道是什麼節點名稱,這裏咱們用nodeName來獲取具體是什麼標籤名,這個返回的是一個大寫的,咱們能夠轉成小寫再作比較,或者不轉。

window.onload = function(){
  var ulDelegate= document.getElementById("ulDelegate");
  ulDelegate.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
       alert('Delegate');
      alert(target.innerHTML);
    }
  }
}

這樣就只有點擊li會觸發事件了,且每次只執行一次dom操做,若是li數量不少的話,將大大減小dom的操做,優化的性能可想而知!

上面的栗子是說li操做的是一樣的效果,若是每一個li被點擊的效果都不同,上栗子:

<ul id="ulDelegate">
    <li id='add'>添加</li>
    <li id='delete'>刪除</li>
    <li id='edit'>修改</li>
</ul>
<script>
    window.onload = function(){
            var ulDelegate= document.getElementById("ulDelegate");
            ulDelegate.onclick = function (ev) {
                var ev = ev || window.event;
                var target = ev.target || ev.srcElement;
                if(target.nodeName.toLocaleLowerCase() == 'li'){
                    switch(target.id){
                        case 'add' :
                            alert('添加');
                            break;
                        case 'delete' :
                            alert('刪除');
                            break;
                        case 'edit' :
                            alert('移動');
                            break;
                    }
                }
            }
            
        }
</script>

再來最後一個事件代理的栗子,當咱們新增一個li元素的時候,一樣先看一下通常寫法:

<ul id="ulDelegate">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
<button id="addLi">新增li</button>

<script>
     var ulDelegate= document.getElementById("ulDelegate");
     var liDelegate= ulDelegate.getElementsByTagName('li');
     var addLi = document.getElementById("addLi");
     
     function addClick() {
         for(var i=0;i<liDelegate.length;i++){
             liDelegate[i].onclick = function(){
                alert('noDelegate');
             }
         }
     }
     
     addClick();
     
     addLi.onclick = function(){        
         var addLiElement = document.createElement('li');
         addLiElement .innerHTML = 10000*Math.random();
         ulDelegate.appendChild(addLiElement );
         addClick();
     }
</script>

再來看一下事件代理的寫法:

window.onload = function(){
        var ulDelegate = document.getElementById("ulDelegate");
        var addLi = document.getElementById("addLi");
        
        ulDelegate.onclick = function(ev){
            var ev = ev || window.event;
            var target = ev.target || ev.srcElement;
            if(target.nodeName.toLowerCase() == 'li'){
                alert('Delegate');
                alert(target.innerHTML);
            }
        };

        addLi.onclick = function(){
            var addLiElement = document.createElement('li');
            addLiElement .innerHTML = 10000*Math.random();
            ulDelegate.appendChild(addLiElement );
        }

    }

看,上面是用事件單例的方式,新添加的子元素是帶有事件效果的,咱們不須要去遍歷元素的子節點,只須要給父級元素添加事件就行了,其餘的都是在js裏面的執行,這樣能夠大大的減小dom操做,這纔是事件代理的精髓所在。

總結

事件是將javascript與網頁聯繫在一塊兒的主要方式,在使用事件時,須要考慮以下一些內存與性能方面的問題。

  1. 有必要限制夜歌頁面中事件處理程序的數量,數量太多會致使佔用大量內存,並且也會讓用戶頁面反應不夠靈敏
  2. 創建在事件冒泡機制之上的時間委託技術,能夠有效的減小時間處理程序的數量
  3. 建議在瀏覽器卸載頁面以前移除頁面中的全部事件處理程序

事件是javascript中最重要的主題之一,深刻理解事件的工做機制以及它們對性能的影響是相當重要的

(總結摘自javascript高級程序設計第三版)

相關文章
相關標籤/搜索