JavaScript事件模型
在各種瀏覽器中存在三種事件模型: 原始事件模型 , DOM2事件模型 , IE事件模型。
其中原始的事件模型被所有瀏覽器所支持,而DOM2中所定義的事件模型目前被除了IE以外的所有主流瀏覽器支持.
原始事件模型:
在原始事件模型中(也有說DOM0級),事件發生后沒有傳播的概念,沒有事件流。事件發生,處理,結束,就這么簡單。監聽函數只是元素的一個屬性值,通過指定元素的屬性值來綁定監聽器。書寫方式有兩種:
(1) HTML代碼中指定屬性值:<input type=”button” onclick=”func()” />
(2) 在js代碼中指定屬性值:document.getElementsByTagName(‘input’)[0].onclick = func
優點:所有瀏覽器都兼容
缺點:
1.邏輯與顯示沒有分離
2.相同事件的監聽函數只能綁定一個,后綁定的會覆蓋掉前面的
3.無法通過事件的冒泡、委托等機制。
DOM2事件模型
此模型是W3C制定的標准模型。W3C制定的事件模型中,一次事件的發生包含三個過程:
(1)事件捕獲階段。事件被從document一直向下傳播到目標元素,在這過程中依次檢查經過的節點是否注冊了該事件的監聽函數,若有則執行。
(2)事件處理階段。事件到達目標元素,執行目標元素的事件處理函數.
(3)事件冒泡階段。事件從目標元素上升一直到達document,同樣依次檢查經過的節點是否注冊了該事件的監聽函數,有則執行。
所有的事件類型都會經歷 "事件捕獲階段" 但是只有部分事件會經歷 "事件冒泡階段" 階段,例如submit事件就不會被冒泡。
e.target與e.currentTarget是干什么的?
e.target獲取當前實際觸發事件節點,e.currentTarget獲取獲取當前監聽節點。
<div> <span>11111</span> </div>
$('div').on('click', function(e){
console.log(e.currentTarget);
console.log(e.target);
})
如果點擊span的輸出:
li對象
span對象
點擊li的非span區域,輸出
li對象
li對象
preventDefault與stopPropagation是干什么的
preventDefault取消事件默認動作,如下例,頁面不會跳轉
<a href='baidu.com' onclick="stop(e)"> //js: function stop(e){ e.preventDefault(); }
stopPropagation阻止元素冒泡,下例 點擊child div,parent 的 alert 不會執行
<div id="parent" onclick="alert(this.id)" > <div id="child" onclick="doSomething();" ></div> </div> //js function doSomething(e){ e.stopPropagation() }
什么是dispatchEvent?
dispatchEvent是js的事件觸發器,事件觸發器就是用來觸發某個元素下的某個事件。
可以自定義事件,通過觸發器觸發。
document.addEventListener('test', function(e){ console.log(e.msg) }); var e = document.createEvent('HTMLEvents'); e.initEvent('test', true, true); e.msg = ' test dispatchEvent'; document.dispatchEvent(e); //最終輸出 test dispatchEvent
如何實現事件代理,封裝一個方法:
首先介紹一下JavaScript的事件代理。事件代理在JS世界中一個非常有用也很有趣的功能。
當我們需要對很多元素添加事件的時候,可以通過將事件添加到它們的父節點而將事件委托給父節點來觸發處理函數,
這主要得益於瀏覽器的事件冒泡機制。
以下封裝一個簡單的實現,只支持id綁定和節點選擇
<ul id="list"> <li>1</li> <li>2</li> <li>3</li> <div>div</div> </ul> <script> function delegation(id, node, event, cb) { var el = document.getElementById(id); el.addEventListener(event, function(e){ if(e.target.nodeName.toLocaleLowerCase() == node.toLocaleLowerCase()){ cb && cb(e); } }) } delegation('list', 'li', 'click', function(e){ console.log(e.target.textContent); })
</script>
事件代理的問題:
<li><span></span></li>如果同時給span與li綁定事件該如何阻止冒泡???
代碼如下:事件代理同時綁定了li和span,當點擊span的時候,li和span都會冒泡。
<li> <span></span> </li> $(document).on('click', 'li', function(){ alert('li click'); }); $(document).on('click', 'span', function(){ alert('li span'); })
獲取當前事件的target目標元素,如果是span標簽,就不執行li標簽綁定事件的邏輯。
$(document).on('click', 'li', function(e){ if(e.target.nodeName == 'SPAN') { e.stopPropagation(); return; } alert('li click'); });
tap事件是如何實現的
以下是zepto部分源碼,tap是一個自定義事件,監聽touchend,並且250ms之內沒有觸摸操作而被觸發
.on('touchend MSPointerUp pointerup', function(e){ tapTimeout = setTimeout(function() { // trigger universal 'tap' with the option to cancelTouch() // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) var event = $.Event('tap') event.cancelTouch = cancelAll // [by paper] fix -> "TypeError: 'undefined' is not an object (evaluating 'touch.el.trigger'), when double tap if (touch.el) touch.el.trigger(event) // trigger double tap immediately if (touch.isDoubleTap) { if (touch.el) touch.el.trigger('doubleTap') touch = {} } // trigger single tap after 250ms of inactivity else { touchTimeout = setTimeout(function(){ touchTimeout = null if (touch.el) touch.el.trigger('singleTap') touch = {} }, 250) } }, 0) } else { touch = {} } deltaX = deltaY = 0 })
tap會產生什么問題
tap會出現目前所謂“點透”的情況,下圖是的黑色透明遮罩綁定了tap,提交按鈕綁定了click事件,如果在按鈕的位置點擊遮罩,會同時觸發click和tap,
tap的延遲為250毫秒,而click事件的延遲是300毫秒。tap觸發隱藏遮罩后,click剛好延遲結束,此時按鈕就會被點擊。
zepto的touch庫做了什么?
zepto touch庫通過touchmove,touchstart和touchend,封裝了自定義事件:觸摸,雙擊,長按,滑動,方向滑動等。
fastclick解決了什么問題
fastclick解決了click事件延遲的問題,因為300毫秒的延遲會造成很多問題,在移動端會干擾touch事件。
fastclick核心代碼:
FastClick.prototype.onTouchEnd = function(event){ if (!this.needsClick(targetElement)) { // 則屏蔽原生事件,避免觸發兩次click event.preventDefault(); // 觸發一次模擬的click this.sendClick(targetElement, event); } }
FastClick.prototype.sendClick = function(targetElement, event) { // 創建一個鼠標事件 clickEvent = document.createEvent('MouseEvents'); // 初始化鼠標事件為click事件 clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); // fastclick的內部變量,用來識別click事件是原生還是模擬 clickEvent.forwardedTouchEvent = true; // 在目標元素上觸發該鼠標事件, targetElement.dispatchEvent(clickEvent);
fastclick會造成某些問題
造成安卓移動端的video交互事件失效