摘要:好啦我們已經解決了是啥的疑問了,現在回去開始一步步解讀如何實現手動觸發事件。我們主要看看這里面幾乎含有如何手動觸發一個事件的大部分步驟和內容。
前言
前端在最近幾年實在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個雙向數據綁定,不曉得啥是虛擬DOM,也許就被鄙視了。火熱的背后往往也是無盡的浮躁,學習這些先進流行的類庫或者框架可以讓我們走的更快,但是靜下心來回歸基礎,把基石打牢固,卻可以讓我們走的更穩,更遠。
最近一直在看zepto的源碼,希望通過學習它掌握一些框架設計的技巧,也將很久不再拾起的js基礎重新溫習鞏固一遍。如果你對這個系列感興趣,歡迎點擊下方地址watch,隨時關注動態。這篇文章主要想說一下zepto中事件模塊(event.js)的trigger實現原理。
原文地址
倉庫地址
event.js模塊zepto中由許多小的模塊組合合成,基礎的zepto.js模塊,event.js事件處理模塊,ajax.js請求處理模塊等等。其中event.js事件處理模塊的核心完成了zepto中事件綁定on,移除off還有手動觸發trigger等功能。我們簡單回顧下如何使用zepto的這三大功能。
let $list = $(".list") let cb1 = function (e, name) { console.log(1, name) } let cb2 = function (e, name) { console.log(2, name) } $list.on("click", cb1) $list.on("click", cb2) // 移除事件 // 我們可以指定移除click事件的某個事件處理程序 $list.off("click", cb1) // 也可以直接移除click事件 $list.off("click") // 手動觸發事件 $list.trigger("click", "qianlongo")
哥們你逗我呢,jQuery,zepto多熟了,誰不會用這個啊!客觀別急,我們今天主要是慢慢來看看它源碼怎么實現的。
一步步看trigger怎么實現直接上代碼
$.fn.trigger = function (event, args) { // 對傳入的event進行處理,如果是字符串或者純對象,得到一個自己創建的事件對象 // 如果傳入的已經是個經過$.Event處理的對象,則放入compatible再次改造(其實就是添加了幾個方法,和重寫了幾個方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數 event._args = args return this.each(function () { // handle focus(), blur() by calling them directly if (event.type in focus && typeof this[event.type] == "function") this[event.type]() // items in the collection might not be DOM elements // 觸發dom事件 else if ("dispatchEvent" in this) this.dispatchEvent(event) // 因為zepto對象內部的元素不一定是dom元素,此時直接觸發回調函數 else $(this).triggerHandler(event, args) }) }
直接貼出trigger函數的代碼可能我們是懵逼的
$是啥啊!!!
$.fn是啥啊!!!
$.isPlainObject又是啥啊!!!
$.Event又是什么鬼?
仿佛有一連串的問題等待著我們解決。
為了直接切入不易理解,我們先來看看zepto中是如何給基礎的zepto.js模塊添加功能的
首先看看zepto.js模塊
var Zepto = (function () { // xxxx var $ = function (selector, context) { return zepto.init(selector, context) } return $ zepto.Z.prototype = Z.prototype = $.fn // xxxx })() window.Zepto = Zepto window.$ === undefined && (window.$ = Zepto)
盡量刪除了一些不必要的代碼,可以看到我們平時使用的Zepto其實就是其匿名函數自執行內部導出的一個函數。而$.fn就是其原型
如何給zepto.js模塊增添功能
zepto.js模塊只有一些基礎的功能,比如給dom添加事件的功能就沒有,怎么添加呢?
(function ($) { // xxx $.fn.on = function () {//xxxx} $.fn.off = function () {//xxxx} $.fn.trigger = function () {//xxxx} $.Event = function () {//xxx} // xxx })(Zepto)
最后縮減掉其他的干擾代碼,可以看到所謂的給zepto.js模塊增添功能,基本上就是在其原型上添加新的方法或者直接在$函數上定一些靜態方法。
好啦我們已經解決了$,$.fn是啥的疑問了,現在回去開始一步步解讀如何實現手動觸發事件。
重新看trigger函數源碼$.fn.trigger = function (event, args) { // 對傳入的event進行處理,如果是字符串或者純對象,得到一個自己創建的事件對象 // 如果傳入的已經是個經過$.Event處理的對象,則放入compatible再次改造(其實就是添加了幾個方法,和重寫了幾個方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數 event._args = args // xxx }
先把后面的一些代碼給刪除了,我們先理解這幾句代碼。其中非常重要的一個函數就是$.Event,至于
isString => 判斷是不是字符串
isPlainObject => 判斷是不是存粹的對象(必須是對象,window對象除外,該對象的原型必須和Object的原型一致)
compatible => 其實就是對事件對象event做一些擴展,比如添加一些方法,重寫一些方法之類的。
這幾個方法暫時可以不需要太多關心.
我們主要看看$.Event,這里面幾乎含有如何手動觸發一個dom事件的大部分步驟和內容。
我們主要看看$.Event,這里面幾乎含有如何手動觸發一個dom事件的大部分步驟和內容。
我們主要看看$.Event,這里面幾乎含有如何手動觸發一個dom事件的大部分步驟和內容。
$.Event是個啥創建并初始化一個指定的dom事件對象, 如果給定了props,則將其擴展到事件對象上
$.Event = function (type, props) { // 當type是個對象時,比如{type: "click", data: "qianlongo"} if (!isString(type)) props = type, type = props.type // click,mousedown,mouseup mousemove對應MouseEvent // 其他事件對應為Events // 并把bubbles設置為true,表示事件冒泡,為false則不冒泡 var event = document.createEvent(specialEvents[type] || "Events"), bubbles = true // 當props存在的時候,對props進行循環處理,將其屬性擴展到event對象上 if (props) for (var name in props) (name == "bubbles") ? (bubbles = !!props[name]) : (event[name] = props[name]) // 初始化事件對象,第一個為事件類型,第二個為冒泡與否,第三個為是否可以通過preventDefault來阻止瀏覽器默認行為 event.initEvent(type, bubbles, true) // 再對創造出來的時間對象處理一番并返回 return compatible(event) }
注釋已經寫的很清楚了,這個函數就是返回一個經過初始化了的事件對象
到這里我們直接歸納一下要手動觸發一個dom事件的基本步驟
手動觸發一個dom事件,需要3步,如果你對document.createEvent,不是很熟悉,可以點擊查看。
創建一個事件對象 document.createEvent(event)
初始化事件對象 event.initEvent(type, bubbles, true)
分發事件 dom.dispatchEvent(event)
到這里已經完成了前面兩步,還剩最后一步了,在來看trigger剩下的代碼
手動觸發dom事件最后一步$.fn.trigger = function (event, args) { // 對傳入的event進行處理,如果是字符串或者純對象,得到一個自己創建的事件對象 // 如果傳入的已經是個經過$.Event處理的對象,則放入compatible再次改造(其實就是添加了幾個方法,和重寫了幾個方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數 event._args = args return this.each(function () { // handle focus(), blur() by calling them directly if (event.type in focus && typeof this[event.type] == "function") this[event.type]() // items in the collection might not be DOM elements // 觸發dom事件 else if ("dispatchEvent" in this) this.dispatchEvent(event) // 因為zepto對象內部的元素不一定是dom元素,此時直接觸發回調函數 else $(this).triggerHandler(event, args) }) }
最后一步其實就是將當前選中的元素進行一次each遍歷,然后判斷要觸發的事件是不是focus或者blur,如果是就直接執行。
再進一步,如果dispatchEvent方法在當前的dom元素屬性中存在,那么便將該事件觸發。(為什么要這一步呢?因為我們知道$()函數的使用方式有很多,有些方式得到的zepto對象是沒有選中dom節點的)
最后還有一個else分支,這個分支處理走的不是手動觸發事件,而是直接觸發注冊事件時添加的事件處理程序(因為這里涉及到zepto事件模塊中如何管理元素與事件隊列的映射關系,篇幅會比較長,會在接下來的文章中說,這里不展開說明)
手動diy一個根據上面的描述,手動觸發DOM事件,原來并沒有那么神奇,完成三步,即可達到目標。我們自己來手動寫一個示例
let $list = document.querySelector(".list") let $item1 = document.querySelector(".item1") $list.addEventListener("click", function () { console.log(this.innerHTML) }, false) $item1.addEventListener("click", function () { console.log(this.innerHTML) }, false) // 1. 創建一個事件對象 document.createEvent(event) let event = document.createEvent("Event") // 2. 初始化事件對象 event.initEvent(type, bubbles, true) event.initEvent("click", true, true) // 3. 分發事件 dom.dispatchEvent(event) $item1.dispatchEvent(event)
這個時候控制臺打印出了
1
1是item1的事件處理函數打印出來的。
后面的li那部分則是list打印出來的。
如果將initEvent的第二個參數設置為false,將不允許冒泡,則只會打印出1
結尾如果這部分對你有點點幫助,點個star好不好呀! ???
倉庫地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83367.html
摘要:好啦我們已經解決了是啥的疑問了,現在回去開始一步步解讀如何實現手動觸發事件。我們主要看看這里面幾乎含有如何手動觸發一個事件的大部分步驟和內容。 前言 前端在最近幾年實在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個雙向數據綁定,不曉得啥是虛擬DOM,也許就被鄙視了。火熱的背后往往也是無盡的浮躁,學習這些先進流行的類庫或者框架可以讓我們走的更快,但是靜下心...
摘要:好啦我們已經解決了是啥的疑問了,現在回去開始一步步解讀如何實現手動觸發事件。我們主要看看這里面幾乎含有如何手動觸發一個事件的大部分步驟和內容。 前言 前端在最近幾年實在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個雙向數據綁定,不曉得啥是虛擬DOM,也許就被鄙視了。火熱的背后往往也是無盡的浮躁,學習這些先進流行的類庫或者框架可以讓我們走的更快,但是靜下心...
摘要:分別存儲事件的定時器。事件定時器延時時間存儲事件對象滑動方向判斷我們根據下圖以及對應的代碼來理解滑動的時候方向是如何判定的。取消長按,以及取消所有事件取消長按取消所有事件方式都是類似,先調用取消定時器,然后釋放對應的變量,等候垃圾回收。 前言 移動端原生支持touchstart、touchmove、touchend等事件,但是在平常業務中我們經常需要使用swipe、tap、double...
摘要:不支持事件冒泡帶來的直接后果是不能進行事件委托,所以需要對和事件進行模擬。調用函數,分隔出參數的事件名和命名空間。這里判斷是否為函數,即第一種傳參方式,調用函數的方法,將上下文對象作為的第一個參數,如果存在,則與的參數合并。 Event 模塊是 Zepto 必備的模塊之一,由于對 Event Api 不太熟,Event 對象也比較復雜,所以乍一看 Event 模塊的源碼,有點懵,細看下...
閱讀 3422·2023-04-25 22:44
閱讀 926·2021-11-15 11:37
閱讀 1632·2019-08-30 15:55
閱讀 2639·2019-08-30 15:54
閱讀 1080·2019-08-30 13:45
閱讀 1430·2019-08-29 17:14
閱讀 1853·2019-08-29 13:50
閱讀 3402·2019-08-26 11:39