理解javascript中的事件模型

  javascript中有兩種事件模型:DOM0,DOM2。而對於這兩種的時間模型,我一直不是很是的清楚,如今經過網上查閱資料終於明白了一些。javascript

  

  一.  DOM0級事件模型html

  DOM0級事件模型是早期的事件模型,全部的瀏覽器都是支持的,並且其實現也是比較簡單。代碼以下:java

<p id = 'click'>click me</p>
<script>
    document.getElementById('click').onclick = function(event){
        alert(event.target);
    }
</script>

  這種事件模型就是直接在dom對象上註冊事件名稱,這段代碼就是在p標籤上註冊了一個onclick事件,在這個事件函數內部輸出點擊的目標。而解除事件則更加簡單,就是將null複製給事件函數,以下:瀏覽器

document.getElementById('click'_).onclick = null;

  由此咱們能夠知道dom0中,一個dom對象只能註冊一個同類型的函數,由於註冊多個同類型的函數的話,就會發生覆蓋,以前註冊的函數就會無效。dom

var click = document.getElementById('click');
click.onclick = function(){
    alert('you click the first function');
};
click.onclick = function(){
    alert('you click the second function')
}

  在這段代碼中,咱們爲dom對象註冊了兩個onclick函數,可是結果是隻執行了第二個註冊的函數,前面所註冊的函數被覆蓋了。函數

 

  二.   DOM2級事件模型spa

  1.  事件捕獲和事件冒泡(capture,bubble)code

  首先,IE8及如下是不支持這種事件模型的。事件捕獲和事件冒泡的機制以下圖:htm

  如上圖所示,123表明事件捕獲,4567表明事件冒泡。首先咱們使用下面的代碼:對象

<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>

  假設咱們點擊了ID爲inner的div,那麼此時的事件流程就是,首先執行捕獲階段:document-html-body-div(outer)。而後執行冒泡階段:div(inner)-div(outer)-body-html-document。

  2.   DOM2級的註冊事件和解除事件

  在DOM2級中使用addEventListener和removeEventListener來註冊和解除事件(IE8及以前版本不支持)。這種函數較之以前的方法好處是一個dom對象能夠註冊多個相同類型的事件,不會發生事件的覆蓋,會依次的執行各個事件函數。

  addEventListener('事件名稱','事件回調','捕獲/冒泡')。示例以下:

<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
    var click = document.getElementById('inner');
    click.addEventListener('click',function(){
        alert('click one');
    },false);
    click.addEventListener('click',function(){
        alert('click two');
    },false);
</script>

  首先咱們要知道addEventListenr的第一個參數是事件名稱,與DOM0級不一樣的是沒有」on「,另外第三個參數表明捕獲仍是冒泡,true表明捕獲事件,false表明冒泡事件。

  而在這段代碼中,咱們爲inner的div註冊了兩個click事件函數,結果是瀏覽器會依次執行這兩個函數。

  下面咱們演示如何使用事件流的發生機制。

<div id = 'outer' style = 'margin: 100px 0 0 100px; width: 200px;height: 200px; background: red;'>
    <div id="inner" style = 'margin-left:20px; width: 50px;height:50px; background: green;'></div>
</div>
<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(){
        alert('inner show');
    },true);
    clickouter.addEventListener('click',function(){
        alert('outer show');
    },true);
</script>

  這段代碼,咱們使用了捕獲事件,因爲inner是嵌套在outer中的,因此咱們知道當使用捕獲的時候outer是應該首先捕獲到這個事件的,其次inner才能捕獲到這個事件。那麼結果就是outer首先執行,其次是inner執行。

  那麼我把outer的執行時機改成冒泡的階段呢?

alickouter.addEventListener('click',function(){
  alert('outer show');  
},false);

  這種狀況下,就是先執行inner後執行outer了。同理咱們把兩者的事件執行時機都改成冒泡階段的話,依舊是先執行inner後執行outer。那麼還有個問題,就是若是咱們把inner註冊兩個click事件,一個是在捕獲階段,另外一個是在冒泡階段,也就是說把addEventListenter的第三個參數分別設置爲false和true,那麼執行的順序又是怎樣的呢。

<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(){
        alert('capture show');
    },true);
    click.addEventListener('click',function(){
        alert('bubble show');
    },false);
</script>

  這種狀況下首先這些的是capture show,其次是bubble show。可是這種結果是與註冊的順序有關係的,先註冊就先執行。由於咱們在看事件捕獲和事件冒泡示意圖,發現最後具體的dom對象是隻有一個的。

  那麼 若是咱們給outer和inner都註冊了click事件可是我不但願outer執行怎麼辦呢?這個時候咱們就須要用到stopPropagation函數了,這個函數是用來阻止冒泡,言下之意是讓事件再也不繼續冒泡下去,這樣接下來註冊一樣類型事件的dom對象就不會執行了。好比在自制下拉框的時候,咱們點擊瀏覽器的其餘位置,咱們須要下拉框的options隱藏,這時咱們就要用到stopPropagation了。以下:

<script>
    var click = document.getElementById('inner');
    var clickouter = document.getElementById('outer');
    click.addEventListener('click',function(event){
        alert('inner show');
        event.stopPropagation();
    },false);
    clickouter.addEventListener('click',function(){
        alert('outer show');
    },false);
</script>

  正常的狀況下,咱們在不添加stopPropagation函數時,首先應該執行inner,而後執行outer,可是當咱們在inner的事件函數中添加了stopPropagation函數以後,執行完inner的事件函數以後,就不會在執行outer的事件函數了,也能夠理解爲事件冒泡到inner以後就消失了,所以也就不會在執行接下來的事件函數了。

  因爲事件捕獲階段沒有能夠阻止事件的函數,因此通常都是設置爲事件冒泡。

相關文章
相關標籤/搜索