摘要:寫在前面通過本文,您可以了解的事件模塊,之后到底發(fā)生了什么樣的事情,如何實(shí)現(xiàn)的事件委托,如何實(shí)現(xiàn)的自定義事件等問題。個(gè)人理解這么做目的在于便于移除事件,這樣就可以使用匿名函數(shù),而且仍可以將該匿名函數(shù)移除。
寫在前面
通過本文,您可以了解zepto的事件模塊,$(selector).on之后到底發(fā)生了什么樣的事情,如何實(shí)現(xiàn)的事件委托【$(selector).delegate】,如何實(shí)現(xiàn)的自定義事件等問題。由于篇幅有限,移除事件部分,代碼沒有貼出,大家可以看這里(完整版)。先來看下全部API(略去了off等移除事件方法):
$.proxy$.proxy(fn, context),用于改變函數(shù)的上下文,實(shí)現(xiàn)柯里化,有點(diǎn)類似于js原生的bind方法,支持兩種傳入?yún)?shù)方式:
fn為函數(shù),context為對(duì)象,將fn內(nèi)的this指向context
fn為對(duì)象,context為字符串,將調(diào)用fn的context方法
主要利用的apply來實(shí)現(xiàn):
// isString,isFunction為自定義的檢測(cè)類型方法,實(shí)現(xiàn)很簡(jiǎn)單 // (obj) => {}.toString.call(obj) === ["object String"] // isFunction也是同樣方法即可 $.proxy = function(fn, context) { var args = (2 in arguments) && slice.call(arguments, 2) if (isFunction(fn)) { // 利用apply調(diào)用,將上下文指定為context // 同時(shí)實(shí)現(xiàn)了柯里化 var proxyFn = function(){ return fn.apply( context, args ? args.concat(slice.call(arguments)) : arguments ); } proxyFn._zid = zid(fn) return proxyFn } else if (isString(context)) { if (args) { args.unshift(fn[context], fn) return $.proxy.apply(null, args) } else { return $.proxy(fn[context], fn) } } else { throw new TypeError("expected function") } };on
on被添加到了$.fn上,也就是$.prototype上,這樣一來,每一個(gè)zepto都會(huì)有該方法,on方法十分關(guān)鍵,one,live,delegate,bind等方法可以視為on方法的語法糖。在來看on方法,首先來看一下add和幾個(gè)工具方法:
function add(element, events, fn, data, selector, delegator, capture){ // zid方法用于給element對(duì)象設(shè)置唯一id值或者取出element對(duì)應(yīng)的id var id = zid(element), set = handlers[id] || (handlers[id] = []); // 多個(gè)事件傳入時(shí),利用空格來進(jìn)行分離 events.split(/s/).forEach(function(event) { if (event === "ready") return $(document).ready(fn); // zepto支持事件的命名空間,parse用來解析出命名空間和事件名 var handler = parse(event); handler.fn = fn; handler.sel = selector; if (handler.e in hover) { fn = function(e) { var related = e.relatedTarget; // relatedTarget屬性僅對(duì)mouseout,mouseover有效 // mouseover而言,是指移入目標(biāo)節(jié)點(diǎn)前離開的那個(gè)節(jié)點(diǎn) // mouseout而言,是指移出目標(biāo)節(jié)點(diǎn)進(jìn)入的那個(gè)節(jié)點(diǎn) // e.relatedTarget等于目標(biāo)節(jié)點(diǎn)且該節(jié)點(diǎn)不是目標(biāo)節(jié)點(diǎn)的子節(jié)點(diǎn) // 則證明該事件已經(jīng)觸發(fā)完成,可以執(zhí)行相應(yīng)的事件處理函數(shù) // $.contain是查詢this中是否包含related if ( !related || (this !== related && !$.contains(this, related)) ) { return handler.fn.apply(this, arguments); } }; } handler.del = delegator; var callback = delegator || fn; handler.proxy = function(e) { // 給e添加屬性 e = compatible(e); if (e.isImmediatePropagationStopped()) { return void 0; } // 將data掛載到event對(duì)象上 e.data = data; // return false => 進(jìn)行阻止默認(rèn)事件與事件冒泡 // callback函數(shù)綁定在了element上 // _args僅僅在trigger和triggerHandler方式調(diào)用時(shí)才有 var result = callback.apply(element, e._args == null ? [e] : [e].concat(e._args)); if (result === false) { e.preventDefault(); e.stopPropagation(); } return result; }; // 標(biāo)記handler位置,方便刪除 handler.i = set.length; set.push(handler); // 添加事件 // realEvent返回可行的事件名,因?yàn)閦epto中會(huì)嘗試 // 將mouseenter轉(zhuǎn)為mouseover,mouseleave轉(zhuǎn)為mouseout // 將focus轉(zhuǎn)為focusin,blur轉(zhuǎn)為focusout if ("addEventListener" in element) { element.addEventListener( realEvent(event), handler.proxy, eventCapture(handler, capture) ); } }); }
需要注意幾點(diǎn):
zepto會(huì)利用compatible給事件對(duì)象e添加三個(gè)方法:
isDefaultPrevented是否執(zhí)行了preventDefault來阻止默認(rèn)事件
isPropagationStopped是否執(zhí)行了stopPropagation來阻止冒泡
isImmediatePropagationStopped是否執(zhí)行了stopImmediatePropagation,阻止冒泡的同時(shí)阻止該元素上其余事件的執(zhí)行
e對(duì)象有兩種情況:
原生的事件對(duì)象
進(jìn)行事件委托時(shí),zepto給原生的設(shè)置一個(gè)代理對(duì)象,該對(duì)象由createProxy方法生成
handlers可以理解為一個(gè)緩存池,通過zid會(huì)對(duì)每一個(gè)元素生成一個(gè)唯一id值,每一個(gè)handlers[id]對(duì)應(yīng)一個(gè)數(shù)組對(duì)象,該數(shù)組中存儲(chǔ)著的是每一個(gè)在這個(gè)元素上綁定的事件及其執(zhí)行函數(shù)等信息。(個(gè)人理解這么做目的在于便于移除事件,這樣就可以使用匿名函數(shù),而且仍可以將該匿名函數(shù)移除)。
addEventListener的第三個(gè)參數(shù)決定了事件執(zhí)行的階段
默認(rèn)為false,也就是在冒泡階段來執(zhí)行,
true表示在捕獲階段執(zhí)行
eventCapture方法來確定事件在哪個(gè)階段執(zhí)行,對(duì)于事件委托的情況來說,在捕獲節(jié)點(diǎn)時(shí)執(zhí)行,而對(duì)于focus與blur由于不支持事件冒泡,所以只好利用事件捕獲來模擬事件冒泡
理解了add后,再看來on方法:
// 綁定事件 // event可以是對(duì)象,也可是字符串 // 只傳入event,data,callback時(shí),為bind方法 // 只傳入event,selector,callback時(shí)為delegate or live // 傳入了one參數(shù)時(shí),則為one方法 L.fn.on = function(event, selector, data, callback, one) { var autoRemove, delegator, $this = this; // 假若傳入的event為對(duì)象,key為事件名,fn為執(zhí)行函數(shù) if (event && !L.isString(event)) { $.each(event, function(type, fn) { $this.on(type, selector, data, fn, one); }); return $this; } // 假如未傳入selector的情況,bind就是這種情況 if (!isString(selector) && !isFunction(callback) && callback !== false) { callback = data; data = selector; selector = undefined; } // data未傳入,將callback設(shè)為data對(duì)應(yīng)的值 if (callback === undefined || data === false) { callback = data; data = undefined; } // 傳入false為處理函數(shù),默認(rèn)為阻止默認(rèn)事件,阻止冒泡 if (callback === false) callback = returnFalse; return $this.each(function(_, element) { if (one) { autoRemove = function(e) { // one為綁定的事件函數(shù)執(zhí)行一次后,就移除該事件 remove(element, e.type, callback); return callback.apply(this, arguments); }; } if (selector) { // 事件委托實(shí)現(xiàn) delegator = function(e) { var evt, match = L(e.target).closest(selector, element).get(0); if (match && match !== element) { // 生成代理事件對(duì)象,事件委托中傳入執(zhí)行函數(shù)的event對(duì)象并不是原生事件對(duì)象 evt = $.extend(createProxy(e), { currentTarget: match, liveFired: element }); return (autoRemove || callback).apply( match, [evt].concat(slice.call(arguments, 1)) ); } }; } add(element, event, callback, data, selector, delegator || autoRemove); }); };
bind方法是on的一個(gè)語法糖,其只接受了三個(gè)參數(shù),event, data, callback,同理delegate,live,one等也是一樣,只是利用不同的傳參數(shù)方式來調(diào)用on方法。
自定義事件利用$.Event可以創(chuàng)建并且初始化一個(gè)DOM事件:
// 創(chuàng)建自定義事件 // bubbles,默認(rèn)為true,冒泡時(shí)處理 L.Event = function(type, props) { if (!L.isString(type)) { props = type; type = props.type; } var event = document.createEvent(specialEvents[type] || "Events"), bubbles = true; if (props) { for (var name in props) { if (name === "bubbles") { bubbles = !!props[name]; } else { event[name] = bubbles[name]; } } } // initEvent初始化事件對(duì)象 // 事件類型,是否冒泡,是否可以阻止默認(rèn)事件 event.initEvent(type, bubbles, true); return compatible(event); };
利用trigger可以觸發(fā)事件,trigger還支持傳入?yún)?shù):
// 觸發(fā)事件,zepto的觸發(fā)事件只能作用于DOM上 L.fn.trigger = function(event, args) { event = (L.isString(event) || L.isPlainObject(event)) ? L.Event(event) : compatible(event); event._args = args; return this.each(function() { if (event.type in focus && typeof this[event.type] === "function") { this[event.type](); } else if ("dispatchEvent" in this) { // 瀏覽器原生觸發(fā)事件API // 默認(rèn)采用瀏覽器原生方法來觸發(fā)自定義事件 this.dispatchEvent(event); } else { // 假若不支持,則調(diào)用triggerHandler // 此方法不會(huì)冒泡,只會(huì)在當(dāng)前元素上觸發(fā) // 實(shí)現(xiàn)是:根據(jù)對(duì)應(yīng)事件篩選出其執(zhí)行函數(shù),調(diào)用其執(zhí)行函數(shù) $(this).triggerHandler(event, args); } }); };簡(jiǎn)化
使用zepto時(shí),對(duì)于click等事件來說并不用進(jìn)行bind而是直接調(diào)用$().click()就好:
("focusin focusout focus blur load resize scroll unload click dblclick " + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + "change select keydown keypress keyup error").split(" ").forEach(function(event) { // 傳參數(shù)為注冊(cè),不傳為觸發(fā) L.fn[event] = function(callback) { return (arguments.length === 1) ? this.bind(event, callback) : this.trigger(event) } });
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86547.html
摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個(gè)判斷需要引入設(shè)備偵測(cè)模塊。然后是監(jiān)測(cè)事件,根據(jù)這三個(gè)事件,可以組合出和事件。其中變量對(duì)象和模塊中的對(duì)象的作用差不多,可以先看看讀源碼之模塊對(duì)模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...
摘要:在觸發(fā)事件前,先將保存定時(shí)器的變量釋放,如果對(duì)象中存在,則觸發(fā)事件,保存的是最后觸摸的時(shí)間。如果有觸發(fā)的定時(shí)器,清除定時(shí)器即可阻止事件的觸發(fā)。其實(shí)就是清除所有相關(guān)的定時(shí)器,最后將對(duì)象設(shè)置為。進(jìn)入時(shí),立刻清除定時(shí)器的執(zhí)行。 大家都知道,因?yàn)闅v史原因,移動(dòng)端上的點(diǎn)擊事件會(huì)有 300ms 左右的延遲,Zepto 的 touch 模塊解決的就是移動(dòng)端點(diǎn)擊延遲的問題,同時(shí)也提供了滑動(dòng)的 swip...
摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了?;馃岬谋澈笸彩菬o盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...
摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了。火熱的背后往往也是無盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...
摘要:好啦我們已經(jīng)解決了是啥的疑問了,現(xiàn)在回去開始一步步解讀如何實(shí)現(xiàn)手動(dòng)觸發(fā)事件。我們主要看看這里面幾乎含有如何手動(dòng)觸發(fā)一個(gè)事件的大部分步驟和內(nèi)容。 前言 前端在最近幾年實(shí)在火爆異常,vue、react、angular各路框架層出不窮,咱們要是不知道個(gè)雙向數(shù)據(jù)綁定,不曉得啥是虛擬DOM,也許就被鄙視了。火熱的背后往往也是無盡的浮躁,學(xué)習(xí)這些先進(jìn)流行的類庫(kù)或者框架可以讓我們走的更快,但是靜下心...
閱讀 2403·2021-11-15 11:38
閱讀 2504·2021-11-15 11:37
閱讀 2637·2021-08-24 10:00
閱讀 2968·2019-08-30 15:56
閱讀 1328·2019-08-30 15:53
閱讀 3787·2019-08-29 18:43
閱讀 3005·2019-08-29 17:01
閱讀 3318·2019-08-29 16:25