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