摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動觸發(fā)事件。我們主要看看這里面幾乎含有如何手動觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。
前言
前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫或者框架可以讓我們走的更快,但是靜下心來回歸基礎(chǔ),把基石打牢固,卻可以讓我們走的更穩(wěn),更遠(yuǎn)。
最近一直在看zepto的源碼,希望通過學(xué)習(xí)它掌握一些框架設(shè)計(jì)的技巧,也將很久不再拾起的js基礎(chǔ)重新溫習(xí)鞏固一遍。如果你對這個(gè)系列感興趣,歡迎點(diǎn)擊下方地址watch,隨時(shí)關(guān)注動態(tài)。這篇文章主要想說一下zepto中事件模塊(event.js)的trigger實(shí)現(xiàn)原理。
原文地址
倉庫地址
event.js模塊zepto中由許多小的模塊組合合成,基礎(chǔ)的zepto.js模塊,event.js事件處理模塊,ajax.js請求處理模塊等等。其中event.js事件處理模塊的核心完成了zepto中事件綁定on,移除off還有手動觸發(fā)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事件的某個(gè)事件處理程序 $list.off("click", cb1) // 也可以直接移除click事件 $list.off("click") // 手動觸發(fā)事件 $list.trigger("click", "qianlongo")
哥們你逗我呢,jQuery,zepto多熟了,誰不會用這個(gè)??!客觀別急,我們今天主要是慢慢來看看它源碼怎么實(shí)現(xiàn)的。
一步步看trigger怎么實(shí)現(xiàn)直接上代碼
$.fn.trigger = function (event, args) { // 對傳入的event進(jìn)行處理,如果是字符串或者純對象,得到一個(gè)自己創(chuàng)建的事件對象 // 如果傳入的已經(jīng)是個(gè)經(jīng)過$.Event處理的對象,則放入compatible再次改造(其實(shí)就是添加了幾個(gè)方法,和重寫了幾個(gè)方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數(shù) 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 // 觸發(fā)dom事件 else if ("dispatchEvent" in this) this.dispatchEvent(event) // 因?yàn)閦epto對象內(nèi)部的元素不一定是dom元素,此時(shí)直接觸發(fā)回調(diào)函數(shù) else $(this).triggerHandler(event, args) }) }
直接貼出trigger函數(shù)的代碼可能我們是懵逼的
$是啥?。。。?/p>
$.fn是啥啊?。。?/p>
$.isPlainObject又是啥啊?。?!
$.Event又是什么鬼?
仿佛有一連串的問題等待著我們解決。
為了直接切入不易理解,我們先來看看zepto中是如何給基礎(chǔ)的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)
盡量刪除了一些不必要的代碼,可以看到我們平時(shí)使用的Zepto其實(shí)就是其匿名函數(shù)自執(zhí)行內(nèi)部導(dǎo)出的一個(gè)函數(shù)。而$.fn就是其原型
如何給zepto.js模塊增添功能
zepto.js模塊只有一些基礎(chǔ)的功能,比如給dom添加事件的功能就沒有,怎么添加呢?
(function ($) { // xxx $.fn.on = function () {//xxxx} $.fn.off = function () {//xxxx} $.fn.trigger = function () {//xxxx} $.Event = function () {//xxx} // xxx })(Zepto)
最后縮減掉其他的干擾代碼,可以看到所謂的給zepto.js模塊增添功能,基本上就是在其原型上添加新的方法或者直接在$函數(shù)上定一些靜態(tài)方法。
好啦我們已經(jīng)解決了$,$.fn是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動觸發(fā)事件。
重新看trigger函數(shù)源碼$.fn.trigger = function (event, args) { // 對傳入的event進(jìn)行處理,如果是字符串或者純對象,得到一個(gè)自己創(chuàng)建的事件對象 // 如果傳入的已經(jīng)是個(gè)經(jīng)過$.Event處理的對象,則放入compatible再次改造(其實(shí)就是添加了幾個(gè)方法,和重寫了幾個(gè)方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數(shù) event._args = args // xxx }
先把后面的一些代碼給刪除了,我們先理解這幾句代碼。其中非常重要的一個(gè)函數(shù)就是$.Event,至于
isString => 判斷是不是字符串
isPlainObject => 判斷是不是存粹的對象(必須是對象,window對象除外,該對象的原型必須和Object的原型一致)
compatible => 其實(shí)就是對事件對象event做一些擴(kuò)展,比如添加一些方法,重寫一些方法之類的。
這幾個(gè)方法暫時(shí)可以不需要太多關(guān)心.
我們主要看看$.Event,這里面幾乎含有如何手動觸發(fā)一個(gè)dom事件的大部分步驟和內(nèi)容。
我們主要看看$.Event,這里面幾乎含有如何手動觸發(fā)一個(gè)dom事件的大部分步驟和內(nèi)容。
我們主要看看$.Event,這里面幾乎含有如何手動觸發(fā)一個(gè)dom事件的大部分步驟和內(nèi)容。
$.Event是個(gè)啥創(chuàng)建并初始化一個(gè)指定的dom事件對象, 如果給定了props,則將其擴(kuò)展到事件對象上
$.Event = function (type, props) { // 當(dāng)type是個(gè)對象時(shí),比如{type: "click", data: "qianlongo"} if (!isString(type)) props = type, type = props.type // click,mousedown,mouseup mousemove對應(yīng)MouseEvent // 其他事件對應(yīng)為Events // 并把bubbles設(shè)置為true,表示事件冒泡,為false則不冒泡 var event = document.createEvent(specialEvents[type] || "Events"), bubbles = true // 當(dāng)props存在的時(shí)候,對props進(jìn)行循環(huán)處理,將其屬性擴(kuò)展到event對象上 if (props) for (var name in props) (name == "bubbles") ? (bubbles = !!props[name]) : (event[name] = props[name]) // 初始化事件對象,第一個(gè)為事件類型,第二個(gè)為冒泡與否,第三個(gè)為是否可以通過preventDefault來阻止瀏覽器默認(rèn)行為 event.initEvent(type, bubbles, true) // 再對創(chuàng)造出來的時(shí)間對象處理一番并返回 return compatible(event) }
注釋已經(jīng)寫的很清楚了,這個(gè)函數(shù)就是返回一個(gè)經(jīng)過初始化了的事件對象
到這里我們直接歸納一下要手動觸發(fā)一個(gè)dom事件的基本步驟
手動觸發(fā)一個(gè)dom事件,需要3步,如果你對document.createEvent,不是很熟悉,可以點(diǎn)擊查看。
創(chuàng)建一個(gè)事件對象 document.createEvent(event)
初始化事件對象 event.initEvent(type, bubbles, true)
分發(fā)事件 dom.dispatchEvent(event)
到這里已經(jīng)完成了前面兩步,還剩最后一步了,在來看trigger剩下的代碼
手動觸發(fā)dom事件最后一步$.fn.trigger = function (event, args) { // 對傳入的event進(jìn)行處理,如果是字符串或者純對象,得到一個(gè)自己創(chuàng)建的事件對象 // 如果傳入的已經(jīng)是個(gè)經(jīng)過$.Event處理的對象,則放入compatible再次改造(其實(shí)就是添加了幾個(gè)方法,和重寫了幾個(gè)方法) event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) // args傳遞給事件處理程序的參數(shù) 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 // 觸發(fā)dom事件 else if ("dispatchEvent" in this) this.dispatchEvent(event) // 因?yàn)閦epto對象內(nèi)部的元素不一定是dom元素,此時(shí)直接觸發(fā)回調(diào)函數(shù) else $(this).triggerHandler(event, args) }) }
最后一步其實(shí)就是將當(dāng)前選中的元素進(jìn)行一次each遍歷,然后判斷要觸發(fā)的事件是不是focus或者blur,如果是就直接執(zhí)行。
再進(jìn)一步,如果dispatchEvent方法在當(dāng)前的dom元素屬性中存在,那么便將該事件觸發(fā)。(為什么要這一步呢?因?yàn)槲覀冎?()函數(shù)的使用方式有很多,有些方式得到的zepto對象是沒有選中dom節(jié)點(diǎn)的)
最后還有一個(gè)else分支,這個(gè)分支處理走的不是手動觸發(fā)事件,而是直接觸發(fā)注冊事件時(shí)添加的事件處理程序(因?yàn)檫@里涉及到zepto事件模塊中如何管理元素與事件隊(duì)列的映射關(guān)系,篇幅會比較長,會在接下來的文章中說,這里不展開說明)
手動diy一個(gè)根據(jù)上面的描述,手動觸發(fā)DOM事件,原來并沒有那么神奇,完成三步,即可達(dá)到目標(biāo)。我們自己來手動寫一個(gè)示例
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. 創(chuàng)建一個(gè)事件對象 document.createEvent(event) let event = document.createEvent("Event") // 2. 初始化事件對象 event.initEvent(type, bubbles, true) event.initEvent("click", true, true) // 3. 分發(fā)事件 dom.dispatchEvent(event) $item1.dispatchEvent(event)
這個(gè)時(shí)候控制臺打印出了
1
1是item1的事件處理函數(shù)打印出來的。
后面的li那部分則是list打印出來的。
如果將initEvent的第二個(gè)參數(shù)設(shè)置為false,將不允許冒泡,則只會打印出1
結(jié)尾如果這部分對你有點(diǎn)點(diǎn)幫助,點(diǎn)個(gè)star好不好呀! ???
倉庫地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/50849.html
摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動觸發(fā)事件。我們主要看看這里面幾乎含有如何手動觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫或者框架可以讓我們走的更快,但是靜下心...
摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動觸發(fā)事件。我們主要看看這里面幾乎含有如何手動觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫或者框架可以讓我們走的更快,但是靜下心...
摘要:分別存儲事件的定時(shí)器。事件定時(shí)器延時(shí)時(shí)間存儲事件對象滑動方向判斷我們根據(jù)下圖以及對應(yīng)的代碼來理解滑動的時(shí)候方向是如何判定的。取消長按,以及取消所有事件取消長按取消所有事件方式都是類似,先調(diào)用取消定時(shí)器,然后釋放對應(yīng)的變量,等候垃圾回收。 前言 移動端原生支持touchstart、touchmove、touchend等事件,但是在平常業(yè)務(wù)中我們經(jīng)常需要使用swipe、tap、double...
摘要:不支持事件冒泡帶來的直接后果是不能進(jìn)行事件委托,所以需要對和事件進(jìn)行模擬。調(diào)用函數(shù),分隔出參數(shù)的事件名和命名空間。這里判斷是否為函數(shù),即第一種傳參方式,調(diào)用函數(shù)的方法,將上下文對象作為的第一個(gè)參數(shù),如果存在,則與的參數(shù)合并。 Event 模塊是 Zepto 必備的模塊之一,由于對 Event Api 不太熟,Event 對象也比較復(fù)雜,所以乍一看 Event 模塊的源碼,有點(diǎn)懵,細(xì)看下...
閱讀 1357·2021-09-02 10:19
閱讀 1101·2019-08-26 13:25
閱讀 2108·2019-08-26 11:37
閱讀 2413·2019-08-26 10:18
閱讀 2676·2019-08-23 16:43
閱讀 2989·2019-08-23 16:25
閱讀 775·2019-08-23 15:53
閱讀 3297·2019-08-23 15:11