javascript中的事件

一.簡介

  javascript中的事件,能夠理解就是在HTML文檔或者瀏覽器中發生的一種交互操做。javascript

  本篇文章會從如下幾個點去詳細介紹javascript中的事件:html

    >> 事件處理程序前端

    >> 事件流機制java

    >> 事件對象api

    >> 實踐應用——事件委託瀏覽器

  

二.事件處理程序

  事件處理程序指的是處理事件的函數,也能夠稱其爲事件偵聽器。微信

  因此瞭解事件的第一步就是如何去添加或者刪除事件處理程序,下面會總結幾種常見的事件處理程序的添加/刪除方法。app

1.HTML事件處理程序函數

  HTML事件處理程序,即直接將事件處理程序添加在HTML元素中。工具

  通常在代碼中比較常見,直接看示例。

<button onclick="submit()">提交</button>
<script type="text/javascript">
  function submit(){
    //事件處理邏輯
  }
</script>

  這種直接在HTML中指定事件處理程序會有一個比較明顯的缺點:有可能存在用戶在點擊按鈕時,事件處理函數submit還未解析到,此時就會報錯。

   

2.DOM0級事件處理程序

  DOM0級規定的事件處理程序是在javascript中經過給事件處理程序定義一個函數實現的。

<button id="submit">提交</button>
<script type="text/javascript">
  var submitEle = document.getElementById('submit');
  submitEle.onclick = function submit(){
    //事件處理邏輯
  }
</script>

  這種方式能夠爲button元素添加多個事件處理函數,只不過只有最後一個會生效。

 

  移除事件的方式也比較簡單,即將事件處理程序的值設置爲null。

<button id="submit">提交</button>
<script type="text/javascript">
  var submitEle = document.getElementById('submit');
  submitEle.onclick = function submit(){
    //事件處理邏輯
  }
  // 移除事件
  submitELe.onclick = null;
</script>

  

3.DOM2級事件處理程序

  DOM2級事件中爲處理事件新增了兩個api,一個是添加事件api:addEventListenter;一個是移除事件api:removeEventListener。

  這個兩個api均可以接收三個參數:

    eventName      事件名稱

    fn           事件處理函數

    booleanVal       一個布爾值,默認爲false。用於指示事件處理函數在那個階段執行(這個參數的做用會在事件流機制中說明)。

  

  api使用示例:

<button id="submit">提交</button>
<script type="text/javascript">
    var submitEle = document.getElementById('submit');
    function submit(){
        //事件處理邏輯
    }
    // 添加事件
    submitEle.addEventListener('click',submit);
   
    // 移除事件
    submitEle.removeEventListener('click',submit);
    
</script>

  關於這兩個api的使用,咱們須要注意幾點。

  首先第一點就是使用addEventListener能夠添加多個事件處理程序,並且事件處理程序會按照代碼添加順序依次執行。

  第二點是使用addEventListener添加的事件處理程序,只能經過removeEventListener來移除。

  第三個須要注意的是使用addEventListener添加事件處理程序時,若是傳遞的第二個參數fn是一個匿名函數,則沒法使用removeEventListener去移除。

  對於第三個點中說起到的匿名函數,這個寫一個簡單的示例。

<button id="submit">提交</button>
<script type="text/javascript">
    var submitEle = document.getElementById('submit');
   
    // 添加事件
    submitEle.addEventListener('click',function(){
       console.log("這是一個匿名函數");
     
    });
   
   // 移除事件:這種狀況下使用removeEventListener是沒法移除前面添加的事件處理程序
   submitEle.removeEventListener('click',function(){

   });
    
</script>

  

4.IE事件處理程序

     對於IE8以及更早版本的IE瀏覽器來講,它不支持addEventListener、removeEventListener這兩個api。

  它支持和這兩個相似的api分別爲:attachEvent和deleteEvent。

  這兩個api也須要接收兩個參數:

    eventName     事件名稱

    fn                    事件處理函數

  那話很少說,寫一個示例:

<button id="submit">提交</button>
<script type="text/javascript">
    var submitEle = document.getElementById('submit');
    function submit(){
        //事件處理邏輯
    }
    // 添加事件
    submitEle.attachEvent('onclick',submit);
   
    // 移除事件
    submitEle.deleteEvent('onclick',submit);
    
</script>

  使用這兩個api的時候也有幾個注意的地方。

  第一點是api的eventName參數傳遞,注意是onclick而不是click

  第二點就是使用attachEvent能夠添加多個事件處理程序,並且事件處理程序會按照代碼添加的相反順序依次執行

  第三點是使用attachEvent添加的事件處理程序,只能經過deleteEvent來移除。

  第四個須要注意的是使用attachEvent添加事件處理程序時,若是傳遞的第二個參數fn是一個匿名函數,則沒法使用deleteEvent去移除。

  其中最後兩點和addEventListener/removeEventListener這兩個api用法一致。

  和addEventListener/removeEventListener這兩個api用法有區別的就是第一點和第二點,已經用紅色標記出來,使用的時候須要注意。

     注意:IE11目前已經不支持attachEvent和deleteEvent,直接在腳本中調用方法會報錯。

 

5.通用事件處理程序

  前面介紹了多種不一樣添加/移除事件處理程序的方式。

  考慮到瀏覽器的兼容性,咱們須要有一個通用的事件處理程序,以便咱們進行快速的項目開發。

  所以,下面就寫一個簡單的通用事件處理程序。 

var EventUtils = {
    //添加事件
    addEvent: function(element, eventName, fn){
        if(element.addEventListener){
            element.addEventListener(eventName, fn, false);
        }else if(element.attachEvent){
            element.attachEvent('on' + eventName, fn)
        }else{
            element.eventName = fn;
        }
    },

    //移除事件
    removeEvent: function(element, eventName, fn){
        if(element.removeEventListener){
            element.removeEventListener(eventName, fn, false);
        }else if (element.deleteEvent){
            element.deleteEvent('on' + eventName, fn);
        }else{
            element.eventName = null;
        }
    }
};

  (這個事件處理程序比較簡單,後面內容總結完成後會將這段代碼進行補充完善。)

三.事件流機制

  對於事件流專業的解釋爲:頁面接收事件的順序。

  那對於頁面接收事件的順序,分爲兩種,分別是:事件冒泡(event bubbling)和事件捕獲(event capturing)。

1.事件冒泡

  事件冒泡最先是IE提出來的事件流順序。那麼它指的是事件從子元素向父元素傳播。

  如今咱們有這樣一段html文檔:

  

 

  當咱們點擊觸發button元素的click事件後,會依次觸發button元素、p元素、div元素、body元素和html元素的click事件。

  

 

 2.事件捕獲

  事件捕獲和事件冒泡是相反的順序,即事件由父元素向子元素傳播。

  那仍是前面的那段html文檔

  

  對於捕獲型的事件流,當咱們觸發了button的click事件後,會依次觸發html元素、body元素、div元素、p元素和button元素的clcik事件。

  

 

 

   

  前面DOM2級的事件處理程序中,咱們介紹了兩個api:addEventListener和removeEventListener。

  這兩個api的接收的第三個boolean參數就是用來指定事件處理程序在那個階段執行。

  其中false值,即指定事件在冒泡階段執行;相反指定true值,表示事件在捕獲階段執行。

  而對於IE提供的兩個api:attachEvent和deleteEvent,它們並不支持第三個參數,所以IE8之前的版本只支持事件冒泡的機制。

  

四.事件對象

  當咱們觸發html文檔中元素的某個事件時,事件處理程序的內部就會有一個事件對象event產生,這個對象會包含和事件相關的一些信息。

  (下面說的事件對象僅限於DOM0級和DOM2級事件處理程序,IE會單獨說明)

屬性/方法 類型 說明
event.type String

被觸發事件的類型。

好比觸發button的click事件,那event.type的值就爲"click"。

event.target Element

本次事件中的目標元素。

由於事件流機制的存在,當點擊button時,會按照不一樣的順序觸發其餘元素的事件,在這個過程當中,被點擊的button元素就是事件中的目標元素。

event.currentTarget Element

本次事件中,當前正在處理的元素。

按照事件冒泡或者捕獲的順序處理到那個元素的事件,那個元素就是當前正在處理的元素。

event.cancelable Boolean

表示是否能夠取消事件的默認行爲。

true:表示能夠取消事件的默認行爲。

event.preventDefault() Function

調用該方法能夠取消事件的默認行爲,可是前提是event.cancelable的值爲true。

event.bubbles Boolean

代表事件是否冒泡

event.stopPropagation() Function

調用該方法能夠取消事件的下一步冒泡,但前提是event.bubbles的值爲true。

 

   關於事件對象event的信息,咱們一個一個來實踐一下。

1.event.type

<button id='btn'>點擊</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
    //事件處理程序
    console.log(event.type);
}
</script>

 

  

 

 

 

2.event.target和event.currentTarget

  

<body>
    <div id='box'>
        <p id='pbox'>
            <button id='btn'>點擊</button>
        </p>
    </div>
</body>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
    //事件處理程序
    console.log("btn click");
    console.log("event.target:");
    console.log(event.target);
    console.log("event.currentTarget");
    console.log(event.currentTarget);
}

var box = document.getElementById('box');
box.onclick = function(){
    //事件處理程序
    console.log("box click")
    console.log("event.target:");
    console.log(event.target);
    console.log("event.currentTarget");
    console.log(event.currentTarget);
}
</script>

  

   

  

  能夠看到使用DOM0級方式添加的事件處理程序,默認的事件流機制是事件冒泡。

  即事件觸發順序有子元素button傳播到父元素div。

  那這個操做中,咱們點擊的目標元素是button,所以event.target一直是button元素。

  而event.currentTarget隨着事件觸發的順序一直在變化,觸發到那個元素的事件event.currentTarget就是那個元素。

 

3.event.cancelable和event.preventDefault

  前面咱們說在event.cancelable爲true的前提下,能夠調用event.preventDefault來阻止事件的默認行爲。

  那什麼是事件的默認行爲呢?

  好比:

    瀏覽器中單擊右鍵,出現瀏覽器默認的菜單欄選項;

    點擊a標籤會發生跳轉行爲;

    form表單中點擊提交按鈕會提交表單數據;

  接下來寫一個示例。

<a id='link' href='https://www.baidu.com'>跳轉</a>
<script>
var link = document.getElementById('link');
link.onclick = function(){
    //事件處理程序
    console.log("link click");
    console.log(event.cancelable);
    event.preventDefault();
}
</script>

  在這個示例中,正常狀況下點擊"跳轉"會跳轉到百度首頁,可是咱們在a標籤的onclick事件中調用了event.preventDefault()方法阻止了a標籤默認的跳轉行爲。

  因此咱們點擊"跳轉"後,只會在控制檯打印"link click"和event.cancelable的值,頁面並不會發生跳轉行爲。

  

 

 4.event.bubbles和event.Propagation()

  前面第2小節中關於event.target和event.currentTarget的示例,由於默認的事件冒泡機制,致使click事件從button元素傳播到了父元素div

 

  那如今咱們在事件處理程序中調用event.Propagation方法就能夠阻止事件冒泡。

<body>
    <div id='box'>
        <p id='pbox'>
            <button id='btn'>點擊</button>
        </p>
    </div>
</body>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
    //事件處理程序
    console.log("btn click");
    console.log(event.bubbles);
    event.stopPropagation();
}

var box = document.getElementById('box');
box.onclick = function(){
    //事件處理程序
    console.log("box click")
}
</script>

  

  

  從結果能夠看到,調用event.stopPropagation()後,父元素的click方法已經成功被阻止。

  

  那這裏事件對象的信息基本就總結完了,上面的示例均使用DOM0級的事件處理程序實現的,那實際上對於DOM2級的事件處理程序也是適用的。

  因此DOM2事件處理程序就不寫在示例。 

 

5.IE中的事件對象

  IE中的事件對象和DOM0、DOM2中的事件對象仍是有很大區別的。

  它包含的屬性和方法以下:

屬性/方法 類型 說明
type String 被觸發事件的類型
cancelBubble Boolean

設置事件是否冒泡。

默認值爲false,將其設置爲true就能夠取消事件冒泡。

returnValue Boolean

設置事件的默認行爲。

默認值爲true,將其設置爲false就能夠取消事件的默認行爲。

srcElement Element

本次事件中的目標元素。

(同DOM一、DOM2級中的target)

  IE事件對象的這些屬性和方法就不代碼演示了,也比較簡單。

  只是IE中使用事件對象時,須要注意下面的幾點。

  (下面示例的演示,不一樣版本的結果使用的是IE瀏覽器自帶的仿真工具進行的)

  首先第一點是使用DOM0級添加事件處理程序時,事件處理程序內部的事件對象event是做爲window的一個屬性存在的。

<button id='btn'>點擊</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
    alert(window.event == event);
    // 單獨打印window.event和event,結果均是同一個對象
    // 可是隻有在IE11中alert出來的結果纔是true,其餘版本alert均爲false
}
</script>
     

 

  第二點就是在IE事件處理程序內部的this不必定等於目標元素,所以在事件處理程序內部,最好用event.srcElement來代替this。

  先測試一下DOM0級事件處理程序的結果。

<button id='btn'>點擊</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){
    alert(this); 
    // IE8-IE11 打印 [object HTMLButtonElement]
    alert(event.srcElement);  
    // IE8-IE11 打印 [object HTMLButtonElement]
}

  再測試一下attachEvent和deleteEvent這兩個api(IE11已經不支持這兩個api了,前面已經提過)

<button id='btn'>點擊</button>
<script>
var btn = document.getElementById('btn');
btn.attachEvent('onclick',function(){
    alert(this);
    // IE8-IE10 結果爲 [object Window]
    alert(event.srcElement);
    // IE8-IE10 結果爲 [object HTMLButtonElement]
})
</script>

  

  關於事件對象這裏就介紹完了,一整個關於IE瀏覽器的測試代碼寫下來仍是有些難受。

  到這裏,有關javascript中的事件內容基本總結完了,這裏呢,咱們須要把前面那個通用的事件處理程序完善一下。

<script>
    var EventUtils = {
        // 添加事件
        addEvent: function(element, eventName, fn){
            if(element.addEventListener){
                element.addEventListener(eventName, fn, false);
            }else if(element.attachEvent){
                element.attachEvent('on' + eventName, fn)
            }else{
                element.eventName = fn;
            }
        },

        // 移除事件
        removeEvent: function(element, eventName, fn){
            if(element.removeEventListener){
                element.removeEventListener(eventName, fn, false);
            }else if (element.deleteEvent){
                element.deleteEvent('on' + eventName, fn);
            }else{
                element.eventName = null;
            }
        },

        // 獲取事件對象
        getEvent: function(event){
            return event?event:window.event;
        },

        // 獲取事件類型
        getEventType: function(event){
            return event.type;
        },

        // 獲取被執行事件的目標元素
        getEventTarget: function(event){
            return event.target | event.srcElement;
        },

        // 禁用元素的默認行爲
        preventDefault: function(event){
            if(event.preventDefault){
                event.preventDefault();
            }else{ //IE8以及更低版本
                event.returnValue = false;
            }
        },

        // 阻止元素冒泡
        stopPropagation: function(event){
            if(event.stopPropagation){                    
                event.stopPropagation();
            }else{ //IE8以及更s低版本
                event.cancelable = true;
            }
        }
    }; 
</script>

 

五.實踐應用——事件委託

  最後這一部分,主要是針對事件冒泡的一個應用。

  咱們假設有這樣一個場景:有一個商品列表,點擊其中某一個商品,彈框顯示商品標題。同時用戶能夠在商品列表添加商品。

  如今咱們實現一下這個需求。

<html>
    <head>
        <meta charset="utf-8"/>
        <title>事件委託</title>
        <style>
            li{
                cursor: pointer;
                padding: 20px;
            }
        </style>
    </head>
    <body>
        <h1>事件委託</h1>
        <div class='box'>
            <button onclick="add()">添加商品</button>
            <div class='list'>
                <h3>商品列表</h3>
                <ul>
                    <li>商品1</li>
                    <li>商品2</li>
                    <li>商品3</li>
                    <li>商品4</li>
                </ul>
            </div>
        </div>
        <script type="text/javascript">
            // 遍歷商品列表添加點擊事件
            var liEle = document.getElementsByTagName('li');
            for(var i = 0; i<liEle.length; i++){
                var element = liEle[i];
                element.addEventListener('click', function(){
                    alert(this.innerHTML);
                })
            }

            // 添加商品
            function add(){
                var ulEle = document.getElementsByTagName('ul')[0];
                var liLength = document.getElementsByTagName('li').length;
                var newLiEle = document.createElement('li');
                newLiEle.innerHTML = "商品"+(liLength+1);
                ulEle.appendChild(newLiEle);
            }
        </script>
    </body>
</html>

  咱們看一下瀏覽器效果。

  

  能夠看到,循環li元素添加的點擊事件都可以正常執行,而點擊【添加商品】按鈕添加的商品5,點擊以後並無click事件,所以沒有彈窗顯示。

  這個需求實際上能夠轉化爲這樣的應用場景:事件添加個數不肯定。

  那麼根據前面提說的事件流、事件冒泡和事件對象,咱們很容易就能想到解決辦法:

    將click事件添加到父元素ul元素上,利用事件冒泡和event.target實現彈窗。

  這個辦法咱們被稱爲事件委託機制。

  下面咱們代碼實現一下。

  (修改代碼:27行-32行)

 1 <html>
 2     <head>
 3         <meta charset="utf-8"/>
 4         <title>事件委託</title>
 5         <style>
 6             li{
 7                 cursor: pointer;
 8                 padding: 20px;
 9             }
10         </style>
11     </head>
12     <body>
13         <h1>事件委託</h1>
14         <div class='box'>
15             <button onclick="add()">添加商品</button>
16             <div class='list'>
17                 <h3>商品列表</h3>
18                 <ul>
19                     <li>商品1</li>
20                     <li>商品2</li>
21                     <li>商品3</li>
22                     <li>商品4</li>
23                 </ul>
24             </div>
25         </div>
26         <script type="text/javascript">
27             // 將click事件添加到父元素ul元素上,利用事件冒泡和event.target實現彈窗
28             var ulEle = document.getElementsByTagName('ul')[0];
29             ulEle.addEventListener('click', function(){
30                 var target = event.target;
31                 alert(target.innerHTML);
32             })
33            
34             // 添加商品
35             function add(){
36                 var ulEle = document.getElementsByTagName('ul')[0];
37                 var liLength = document.getElementsByTagName('li').length;
38                 var newLiEle = document.createElement('li');
39                 newLiEle.innerHTML = "商品"+(liLength+1);
40                 ulEle.appendChild(newLiEle);
41             }
42         </script>
43     </body>
44 </html>

  

  

  

  完結!!!

  若是以爲有用的話,點個贊鼓勵一下~

  轉載請先註明出處~

 

 最近做者新開通了一個微信公衆號。

 微信公衆號會分享一些本身平常的東西,包括我的總結呀,吸貓平常呀,同時也會分享一些博客上的前端技術文章。

  

 

 歡迎你們掃碼關注~

相關文章
相關標籤/搜索