觀察者模式又叫作發佈訂閱者模式(Publish/Subscribe),它可讓多個觀察者對象同時監聽某一個主題對象,這個主題對象的狀態變化時會通知全部的訂閱者,使得它們可以作出反應。JS的事件模型就是一種觀察者模式的體現,當對應的事件被觸發時,監聽該事件的全部監聽函數都會被調用。jquery
下面是用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; } } } })();
使用事例:app
//主題監聽函數 var handler = function(info) { console.log(info); } //訂閱hello主題 events.subscribe('hello', handler); //發佈hello主題 events.publish('hello', 'hello world');
事件是與瀏覽器或文檔交互的瞬間,如點擊按鈕,填寫表格等,它是JS與HTML之間交互的橋樑。DOM是樹形結構,若是同時給父子節點都綁定事件時,當觸發子節點的時候,這兩個事件的發生順序如何決定?這就涉及到事件流的概念,它描述的是頁面中接受事件的順序。dom
事件流有兩種:函數
事件冒泡(Event Capturing):spa
是一種從下往上的傳播方式。事件最開始由最具體的元素(文檔中嵌套層次最深的那個節點接受, 也就是DOM最低層的子節點), 而後逐漸向上傳播到最不具體的那個節點,也就是DOM中最高層的父節點。代理
事件捕獲(Event Bubbling):code
與事件冒泡相反。事件最開始由不太具體的節點最先接受事件, 而最具體的節點最後接受事件。
事件模型
DOM0級模型
又稱爲原始事件模型,在該模型中,事件不會傳播,即沒有事件流的概念。事件綁定監聽函數比較簡單, 有兩種方式:對象
HTML代碼中直接綁定:接口
<input type="button" onclick="fun()">
經過JS代碼指定屬性值:
var btn = document.getElementById('.btn'); btn.onclick = fun;
移除監聽函數:
btn.onclick = null;
這種方式全部瀏覽器都兼容,可是邏輯與顯示並無分離。
IE事件模型共有兩個過程:
attachEvent(eventType, handler)
事件移除監聽函數的方式以下:
detachEvent(eventType, handler)
參數說明:
eventType指定事件類型(注意加on)
handler是事件處理函數
Example:
var btn = document.getElementById('.btn'); btn.attachEvent(‘onclick’, showMessage); btn.detachEvent(‘onclick’, showMessage);
屬於W3C標準模型,現代瀏覽器(除IE6-8以外的瀏覽器)都支持該模型。在該事件模型中,一次事件共有三個過程:
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), 這個對象裏面包含了與該事件相關的屬性或者方法。該對象會做爲第一個參數傳遞給監聽函數。
經常使用屬性:
IE事件模型中的事件對象經常使用屬性:
因爲事件模型的差別以及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 } });
JS中已經內置了不少事件,如click, mouseover等等,可是內置事件畢竟有限,有時候咱們想本身定義一些事件,例如三連擊,threeclick。如何實現自定義事件呢?
var event = new Event('threeclick', {"bubbles":true, "cancelable":false});
target.addEventListener('threeclick', hello, false);
target.dispatchEvent(event);
JQuery解決的一個主要問題就是瀏覽器的兼容問題,它有本身的事件模型實現方式。它主要有如下特性: