JS 事件模型

MasonBest 8年前發布 | 9K 次閱讀 JavaScript開發 JavaScript

觀察者模式

觀察者模式又叫做發布訂閱者模式(Publish/Subscribe),它可以讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態變化時會通知所有的訂閱者,使得它們能夠做出反應。

JS的事件模型就是一種觀察者模式的體現,當對應的事件被觸發時,監聽該事件的所有監聽函數都會被調用。

下面是用JS實現的一個觀察者模式的代碼:

var events = (function() {
  var topics = {};

return { publish: function(topic, info) { console.log('publish a topic:' + topic); if (topics.hasOwnProperty(topic)) { topics[topic].forEach(function(handler) { handler(info ? info : {}); }) } }, subscribe: function(topic, handler) { console.log('subscribe an topic:' + topic); if (!topics.hasOwnProperty(topic)) { topics[topic] = []; }

  topics[topic].push(handler);
},
remove: function(topic, handler) {
  if (!topics.hasOwnProperty(topic)) {
    return;
  }

  var handlerIndex = -1;
  topics[topic].forEach(function(element, index) {
    if (element === handler) {
      handlerIndex = index;
    }
  });

  if (handlerIndex >= 0) {
    topics[topic].splice(handlerIndex, 1);
  }
},
removeAll: function(topic) {
  console.log('remove all the handler on the topic:' + topic);
  if (topics.hasOwnProperty(topic)) {
    topics[topic].length = 0;
  }
}

} })();</code></pre>

使用事例:

//主題監聽函數
var handler = function(info) {
    console.log(info);
}
//訂閱hello主題
events.subscribe('hello', handler);

//發布hello主題 events.publish('hello', 'hello world');</code></pre>

事件與事件流

事件是與瀏覽器或文檔交互的瞬間,如點擊按鈕,填寫表格等,它是JS與HTML之間交互的橋梁。

DOM是樹形結構,如果同時給父子節點都綁定事件時,當觸發子節點的時候,這兩個事件的發生順序如何決定?這就涉及到事件流的概念,它描述的是頁面中接受事件的順序。

事件流有兩種:

  • 事件冒泡(Event Capturing): 是一種從下往上的傳播方式。事件最開始由最具體的元素(文檔中嵌套層次最深的那個節點接受, 也就是DOM最低層的子節點), 然后逐漸向上傳播到最不具體的那個節點,也就是DOM中最高層的父節點。

  • 事件捕獲(Event Bubbling): 與事件冒泡相反。事件最開始由不太具體的節點最早接受事件, 而最具體的節點最后接受事件。

事件模型

DOM0級模型

又稱為原始事件模型,在該模型中,事件不會傳播,即沒有事件流的概念。事件綁定監聽函數比較簡單, 有兩種方式:

  • HTML代碼中直接綁定:

    <input type="button" onclick="fun()">
  • 通過JS代碼指定屬性值:

    var btn = document.getElementById('.btn');
    btn.onclick = fun;

    移除監聽函數:

    btn.onclick = null;

這種方式所有瀏覽器都兼容,但是邏輯與顯示并沒有分離。

IE事件模型

IE事件模型共有兩個過程:

  • 事件處理階段(target phase)。事件到達目標元素, 觸發目標元素的監聽函數。

  • 事件冒泡階段(bubbling phase)。事件從目標元素冒泡到document, 依次檢查經過的節點是否綁定了事件監聽函數,如果有則執行。

事件綁定監聽函數的方式如下:

attachEvent(eventType, handler)

事件移除監聽函數的方式如下:

detachEvent(eventType, handler)

參數說明:

  • eventType指定事件類型(注意加on)

  • handler是事件處理函數

Example:

var btn = document.getElementById('.btn');
btn.attachEvent(‘onclick’, showMessage);
btn.detachEvent(‘onclick’, showMessage);

DOM2級模型

屬于W3C標準模型,現代瀏覽器(除IE6-8之外的瀏覽器)都支持該模型。在該事件模型中,一次事件共有三個過程:

  • 事件捕獲階段(capturing phase)。事件從document一直向下傳播到目標元素, 依次檢查經過的節點是否綁定了事件監聽函數,如果有則執行。

  • 事件處理階段(target phase)。事件到達目標元素, 觸發目標元素的監聽函數。

  • 事件冒泡階段(bubbling phase)。事件從目標元素冒泡到document, 依次檢查經過的節點是否綁定了事件監聽函數,如果有則執行。

事件綁定監聽函數的方式如下:

addEventListener(eventType, handler, useCapture)

事件移除監聽函數的方式如下:

removeEventListener(eventType, handler, useCapture)

Example:

var btn = document.getElementById('.btn');
btn.addEventListener(‘click’, showMessage, false);
btn.removeEventListener(‘click’, showMessage, false);

參數說明:

  • eventType指定事件類型(不要加on)

  • handler是事件處理函數

  • useCapture是一個boolean用于指定是否在捕獲階段進行處理,一般設置為false與IE瀏覽器保持一致。

事件對象

當一個事件被觸發時,會創建一個事件對象(Event Object), 這個對象里面包含了與該事件相關的屬性或者方法。該對象會作為第一個參數傳遞給監聽函數。

  • DOM事件模型中的事件對象常用屬性:

    • type用于獲取事件類型

    • target獲取事件目標

    • stopPropagation()阻止事件冒泡

    • preventDefault()阻止事件默認行為

    </li>
  • IE事件模型中的事件對象常用屬性:

    • type用于獲取事件類型

    • srcElement獲取事件目標

    • cancelBubble阻止事件冒泡

    • returnValue阻止事件默認行為

    • </ul> </li> </ul>

      Event Wrapper

      由于事件模型的差異以及Event對象的不同,為了達到兼容各個瀏覽器的目的,我們可以增加一個Event Wrapper, 它對各個瀏覽器應當提供一致的事件操作接口。

      var eventUtils={
           // 添加句柄
           addHandler:function(element,type,handler){
             if(element.addEventListener){
               element.addEventListener(type,handler,false);
             }else if(element.attachEvent){
               element.attachEvent('on'+type,handler);
             }else{
               element['on'+type]=handler;
             }
           },
           // 刪除句柄
           removeHandler:function(element,type,handler){
             if(element.removeEventListener){
               element.removeEventListener(type,handler,false);
             }else if(element.detachEvent){
               element.detachEvent('on'+type,handler);
             }else{
               element['on'+type]=null;
             }
           },
          //獲取事件對象
          //IE模型中event是一個全局唯一的對象綁定在window對象上
          getEvent:function(event){
             return event?event:window.event;
          },
          //獲取類型
          getType:function(event){
           return event.type;
          },
          getElement:function(event){
           return event.target || event.srcElement;
          },
          //阻止默認事件
          preventDefault:function(event){
           if(event.preventDefault){
            event.preventDefault();
           }else{
            event.returnValue=false;
           }
          },
          //阻止冒泡
         stopPropagation:function(event){
         if(event.stopPropagation){
           event.stopPropagation();
         }else{
           event.cancelBubble=true;
          }
         }
        }

      事件代理

      事件在冒泡過程中會上傳到父節點,因此可以把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件,這種方式稱為事件代理(Event delegation)。

      我們有一個div元素,它包含三個按鈕:

      <div id="box">
          <input type="button" value="按鈕" id="btn">
          <input type="button" value="按鈕2" id="btn2">
          <input type="button" value="按鈕3" id="btn3">
      </div>

      我們可以在父節點上一次性的為所有子節點注冊監聽函數:

      var box = document.getElementById('box');

      box.addEventListener('click', function(event) { if (event.target.tagName.toLowerCase() === 'input') { // some code } });</code></pre>

      自定義事件

      JS中已經內置了很多事件,如click, mouseover等等,但是內置事件畢竟有限,有時候我們想自己定義一些事件,例如三連擊,threeclick。如何實現自定義事件呢?

      • 首先要創建一個事件。可以使用以下方式:

      var event = new Event('threeclick', {"bubbles":true, "cancelable":false});
      • 然后我們需要為事件注冊監聽函數:

      target.addEventListener('threeclick', hello, false);
      • 最后我們要在合適的時機觸發該事件,我們可以使用dispatchEvent函數。該方法在當前節點觸發指定事件,從而觸發監聽函數執行。該方法返回一個布爾值,只要有一個監聽函數調用了Event.preventDefault(), 則返回false, 否則返回true。

      target.dispatchEvent(event);

      JQuery Event模型

      JQuery解決的一個主要問題就是瀏覽器的兼容問題,它有自己的事件模型實現方式。它主要有以下特性:

      • 重定義了JQuery.Event對象, 統一了事件屬性和方法, 統一了事件模型

      • 可以在一個事件類型上添加多個事件處理函數, 可以一次添加多個事件類型的事件處理函數

      • 支持自定義事件(事件命名空間)

      • 提供了toggle, hover組合事件

      • 提供了one, live-die, delegate-undelegate

      • 提供了統一的事件封裝, 綁定, 執行, 銷毀機制

      • $(document).ready();

      • ....

       

       

      來自:https://segmentfault.com/a/1190000006934031

       

 本文由用戶 MasonBest 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!