一步一步DIY zepto庫,研究zepto原始碼3 -- event模組
上面的博文介紹的都是原始碼src下的zepto.js檔案,接著我們來看看zepto的事件模組,對應檔案是event.js
1.繫結事件
例項Demo
<div id="foo1">foo1</div>
<div id="foo2">foo2</div>
<div id="foo3">foo3
<div id="foo31">foo31</div>
</div>
<a href="demo1.html" class="my-a"> demo1.html</a>
<script src="zepto.js"></script>
<script type="text/javascript">
var div1 = $('#foo1');
var div2 = $('#foo2');
$('body').on('click', '#foo1', function(event) {
console.log(event);
event.preventDefault();
alert("點選");
});
$('body' ).on('click', '#foo2', 'test',function(event) {
event.preventDefault();
alert("點選foo2"+event.data);//點選foo2test
});
$('#foo3').on('click', function(event) {
alert("點選");
});
$('body').on('click', '.my-a', false);//不跳轉
</script>
1.1 handlers
物件
handlers
{
1: [ // handlers的值為DOM元素的_zid
{
del: function() {}, // 實現事件代理的函式
e: "click", // 事件名稱
fn: function() {}, // 使用者傳入的回撥函式
i: 0, // 該物件在數組裡的下標
ns: "", // 事件的名稱空間,只用使用$.fn.triggerHandler時可用,$.fn.trigger不能使用。
proxy: function(e) {}, // 真正繫結事件時的回撥函式,裡面判斷呼叫del或者fn
sel: undefined // 要進行事件代理時傳入的selector
}
]
}
1.2 全域性變數
var _zid = 1, //用來生成標示元素和回撥函式的id,每標示一個就+1
undefined,
handlers = {},
slice = Array.prototype.slice,
isFunction = $.isFunction,
isString = function(obj) {
return typeof obj == 'string';
},
specialEvents = {},
focusinSupported = 'onfocusin' in window,
focus = { focus: 'focusin', blur: 'focusout' },
hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' };
1.3 新增三個方法:isDefaultPrevented、isDefaultPrevented和isPropagationStopped
- 如果
preventDefault()
被該事件的例項呼叫,那麼返回true。 這可作為跨平臺的替代原生的defaultPrevented
屬性,如果defaultPrevented
缺失或在某些瀏覽器下不可靠的時候 - 如果
stopImmediatePropagation()
被該事件的例項呼叫,那麼返回true。Zepto在不支援該原生方法的瀏覽器中實現它(例如老版本的Android) - 如果
stopPropagation()
被該事件的例項呼叫,那麼返回true
通過改寫原生的preventDefault、stopImmediatePropagation和stopPropagation方法實現新增三個方法
新增的三個方法:
var returnTrue = function() {
return true;
},
returnFalse = function() {
return false;
}, // 構建事件物件時所不要的幾個屬性:returnValue、layerX和layerY(還有以大寫字母開頭的屬性?)
ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$)/,
// 事件物件需要新增的三個方法名
eventMethods = {
preventDefault: 'isDefaultPrevented',
stopImmediatePropagation: 'isImmediatePropagationStopped',
stopPropagation: 'isPropagationStopped'
};
// 新增eventMethods裡面的三個方法:isDefaultPrevented、isDefaultPrevented和isPropagationStopped
function compatible(event, source) {
if (source || !event.isDefaultPrevented) {
source || (source = event);
//遍歷eventMethods物件,name是key,predicate是value
$.each(eventMethods, function(name, predicate) {
var sourceMethod = source[name];
event[name] = function() {
this[predicate] = returnTrue;
return sourceMethod && sourceMethod.apply(source, arguments);
}
event[predicate] = returnFalse;
})
try {
event.timeStamp || (event.timeStamp = Date.now())
} catch (ignored) {}
// 設定isDefaultPrevented預設指向的函式
// 如果有defaultPrevented屬性,就根據defaultPrevented的值來判斷
if (source.defaultPrevented !== undefined ? source.defaultPrevented :
'returnValue' in source ? source.returnValue === false :
//getPreventDefault和defaultPrevented屬性類似,不過是非標準的。為了相容沒有defaultPrevented引數的瀏覽器
source.getPreventDefault && source.getPreventDefault())
event.isDefaultPrevented = returnTrue;
}
return event;
}
1.4 $.fn.on
實現
/**呼叫形式:
*on(type, [selector], function(e){ ... })
*on(type, [selector], [data], function(e){ ... })
*on({ type: handler, type2: handler2, ... }, [selector])
*on({ type: handler, type2: handler2, ... }, [selector], [data])
*/
$.fn.on = function(event, selector, data, callback, one) {
var autoRemove, delegator, $this = this;
//event 為物件,批量繫結事件
if (event && !isString(event)) {
$.each(event, function(type, fn) {
$this.on(type, selector, data, fn, one);
})
return $this;
}
//處理引數
//沒傳selector引數 callback不是函式,且不為false
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = data, data = selector, selector = undefined;
//沒傳data
if (callback === undefined || data === false)
callback = data, data = undefined;
if (callback === false) callback = returnFalse;
// 給每一個Z物件裡面的元素繫結事件
return $this.each(function(_, element) {
// 繫結一次,自動解綁
if (one) autoRemove = function(e) {
remove(element, e.type, callback);
return callback.apply(this, arguments);
}
//有selector選擇符,使用代理
if (selector) delegator = function(e) {
var evt, match = $(e.target).closest(selector, element).get(0);
if (match && match !== element) {
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);
})
}
1.5 核心函式add remove
/**
* 新增事件的實際方法
* @param {元素} element DOM元素
* @param {String} events 事件字串
* @param {Function} fn 回撥函式
* @param {All} data 繫結事件時傳入的data,可以是各種型別
* @param {String} selector 被代理元素的css選擇器
* @param {[type]} delegator 進行事件代理的函式
* @param {[type]} capture 指定捕獲或者冒泡階段
*/
function add(element, events, fn, data, selector, delegator, capture) {
var id = zid(element),
set = (handlers[id] || (handlers[id] = []));
//多個事件以空格為間隔
events.split(/\s/).forEach(function(event) {
//為ready
if (event == 'ready') return $(document).ready(fn);
//*************************構建handler*************************
var handler = parse(event);
handler.fn = fn;
handler.sel = selector;
// emulate mouseenter, mouseleave
// mouseenter、mouseleave通過mouseover、mouseout來模擬realEvent函式處理
// hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
if (handler.e in hover) fn = function(e) {
//http://www.w3school.com.cn/jsref/event_relatedtarget.asp
// relatedTarget為相關元素,只有mouseover和mouseout事件才有
// 對mouseover事件而言,相關元素就是那個失去游標的元素;
// 對mouseout事件而言,相關元素則是獲得游標的元素。
var related = e.relatedTarget;
if (!related || (related !== this && !$.contains(this, related)))
return handler.fn.apply(this, arguments);
};
handler.del = delegator;
// 需要進行事件代理時,呼叫的是封裝了fn的delegator函式
var callback = delegator || fn;
handler.proxy = function(e) {
e = compatible(e); //無第二個引數,其實就是e = e;
if (e.isImmediatePropagationStopped()) return;
e.data = data;
var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
//當事件處理函式返回false時,阻止預設操作和冒泡
if (result === false) e.preventDefault(), e.stopPropagation();
return result;
}
handler.i = set.length; // 把handler在set中的下標賦值給handler.i
set.push(handler);
//*************************構建handler end*************************
if ('addEventListener' in element)
//addEventListener -- https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
//使用`addEventListener`所傳入的真正回撥函式就是proxy函式
element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture));
})
}
//刪除handler
function remove(element, events, fn, selector, capture) {
var id = zid(element);
(events || '').split(/\s/).forEach(function(event) {
findHandlers(element, event, fn, selector).forEach(function(handler) {
delete handlers[id][handler.i];
if ('removeEventListener' in element)
element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture));
})
})
}
$.event = { add: add, remove: remove };
1.6 工具函式
1.6.1 zid
函式
//通過一個_zid而不是通過DOM物件的引用來連線handler是因為:防止移除掉DOM元素後,
//handlers物件還儲存著對這個DOM元素的引用。通過使用_zid就可以防止這種情況發生,
//避免了記憶體洩漏
function zid(element) {
return element._zid || (element._zid = _zid++);
}
1.6.2 focus和blur事件的冒泡問題
//focus和blur事件本身是不冒泡的,如果需要對這兩個事件進行事件代理,就要運用一些小技巧。
//首先,如果瀏覽器支援focusin和focusout,就使用這兩個可以冒泡事件來代替。
//如果瀏覽器不支援focusion和focusout,就利用focus和blur捕獲不冒泡的特性,
//傳入addEventListener中的第三個引數設定true,以此來進行事件代理
function eventCapture(handler, captureSetting) {
return handler.del &&
(!focusinSupported && (handler.e in focus)) ||
!!captureSetting;
}
1.6.3 實際傳入到addEventListener第二個引數
// 構建事件代理中的事件物件
function createProxy(event) {
var key, proxy = { originalEvent: event }; // 新的事件物件有個originalEvent屬性指向原物件
// 將原生事件物件的屬性複製給新物件,除了returnValue、layerX、layerY和值為undefined的屬性
// returnValue屬性為beforeunload事件獨有
for (key in event)
if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key];
// 新增eventMethods裡面的幾個方法,並返回新的事件物件
return compatible(proxy, event);
}
1.6.4 其餘工具函式
// 根據給定的引數在handlers變數中尋找對應的handler
function findHandlers(element, event, fn, selector) {
event = parse(event); // 解析event引數,分離出事件名和ns
if (event.ns) var matcher = matcherFor(event.ns);
// 取出所有屬於element的handler,並且根據event、fn和selector引數進行篩選
return (handlers[zid(element)] || []).filter(function(handler) {
return handler && (!event.e || handler.e == event.e) // 事件名不同的過濾掉
&& (!event.ns || matcher.test(handler.ns)) // 名稱空間不同的過濾掉
&& (!fn || zid(handler.fn) === zid(fn)) // 回撥函式不同的過濾掉(通過_zid屬性判斷是否同一個函式)
&& (!selector || handler.sel == selector); // selector不同的過濾掉
})
}
//解析event引數,如 "click.abc",abc作為ns(名稱空間)
function parse(event) {
var parts = ('' + event).split('.');
return { e: parts[0], ns: parts.slice(1).sort().join(' ') }
}
// 生成匹配的namespace表示式:'abc def' -> /(?:^| )abc .* ?def(?: |$)/
function matcherFor(ns) {
return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}
function realEvent(type) {
return hover[type] || (focusinSupported && focus[type]) || type;
}
2.取消事件繫結
示例:
$('body').off('click', '#foo1');
$.fn.off = function(event, selector, callback) {
var $this = this;
if (event && !isString(event)) {
$.each(event, function(type, fn) {
$this.off(type, selector, fn);
})
return $this;
}
if (!isString(selector) && !isFunction(callback) && callback !== false)
callback = selector, selector = undefined;
if (callback === false) callback = returnFalse;
return $this.each(function() {
remove(this, event, callback, selector);
})
}
3.觸發事件
$.Event
:建立並初始化一個指定的DOM事件。如果給定properties物件,使用它來擴展出新的事件物件。預設情況下,事件被設定為冒泡方式;這個可以通過設定bubbles
為false
來關閉。
通過document.createEvent建立事件物件,然後通過dispatchEvent(原始碼中在$.fn.trigger
和$.fn.triggerHandler
中處理)來出發。
// Create the event.
var event = document.createEvent('Event');
// Define that the event name is 'build'.
event.initEvent('build', true, true);
// Listen for the event.
elem.addEventListener('build', function (e) {
// e.target matches elem
}, false);
// target can be any Element or other EventTarget.
elem.dispatchEvent(event);
上面有點要注意的就是當建立滑鼠相關的事件時要在document.createEvent
的第一個引數中傳入MouseEvents
,以提供更多的事件屬性。滑鼠相關的事件指的是:click、mousedown、mouseup和mousemove
示例:
$(document).on('mylib:change', function(e, from, to) {
console.log('change on %o with data %s, %s', e.target, from, to)
})
$(document.body).trigger('mylib:change', ['one', 'two'])
原始碼實現:
$.fn.trigger = function(event, args) {
event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event);
event._args = args;
return this.each(function() {
// handle focus(), blur() by calling them directly
// 過直接呼叫focus()和blur()方法來觸發對應事件,這算是對觸發事件方法的一個優化
if (event.type in focus && typeof this[event.type] == "function") {
this[event.type]();
}
// items in the collection might not be DOM elements
else if ('dispatchEvent' in this) {
this.dispatchEvent(event);
} else {
$(this).triggerHandler(event, args);
}
});
};
// triggers event handlers on current element just as if an event occurred,
// doesn't trigger an actual event, doesn't bubble
// 直接觸發事件的回撥函式,而不是直接觸發一個事件,所以也不冒泡
$.fn.triggerHandler = function(event, args) {
var e, result;
this.each(function(i, element) {
e = createProxy(isString(event) ? $.Event(event) : event);
e._args = args;
e.target = element;
$.each(findHandlers(element, event.type || event), function(i, handler) {
result = handler.proxy(e);
if (e.isImmediatePropagationStopped()) return false;
});
});
return result;
};
//生成一個模擬事件,如果是滑鼠相關事件,document.createEvent傳入的第一個引數為'MouseEvents'
$.Event = function(type, props) {
if (!isString(type)) props = type, type = props.type;
var event = document.createEvent(specialEvents[type] || 'Events'),
bubbles = true
if (props)
for (var name in props)(name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]);
event.initEvent(type, bubbles, true);
return compatible(event);
};
4.bind,unbind,one實現
$.fn.bind = function(event, data, callback) {
return this.on(event, data, callback)
}
$.fn.unbind = function(event, callback) {
return this.off(event, callback)
}
$.fn.one = function(event, selector, data, callback) {
return this.on(event, selector, data, callback, 1)
}
相關推薦
一步一步DIY zepto庫,研究zepto原始碼3 -- event模組
上面的博文介紹的都是原始碼src下的zepto.js檔案,接著我們來看看zepto的事件模組,對應檔案是event.js 1.繫結事件 例項Demo <div id="foo1">foo1</div> &
一步一步DIY zepto庫,研究zepto原始碼4 -- ajax模組
上面的博文介紹的都是原始碼src下的基礎模組zepto.js檔案和事件模組event.js,下面接著看另外一個獨立的模組–ajax模組ajax.js 1.ajax的過程 當global: true時。在Ajax請求生命週期內,以下這些事件將被觸發。
一例mysql主從數據庫,從庫宕機後無法啟動的解決方案
mysql starting 啟動時報錯信息: Starting MySQL... ERROR! The server quit without updating PID file (/usr/local/mysql/data/qkzhi-appzookeeper-1.novalocal.pid
Light libraries是一組通用的C基礎庫,目標是為減少重復造輪子而寫(全部用POSIX C實現)
six clas 原子操作 roi 實現 class 動態庫 readme tps Light libraries是一組通用的C基礎庫,目標是為減少重復造輪子而寫實現了日誌、原子操作、哈希字典、紅黑樹、動態庫加載、線程、鎖操作、配置文件、os適配層、事件驅動、工作隊列、RP
如何在 webpack 中引入未模組化的庫,如 Zepto
前言 最近我在研究多頁面 webpack 模組打包的完整解決方案時,發現用 import 匯入 Zepto 時,會報 Uncaught TypeError: Cannot read property 'createElement' of undefined 錯誤,
用Html5/CSS3做Winform,一步一步教你搭建CefSharp開發環境(附JavaScript異步調用C#例子,及全部源代碼)上
轉載 界面設計 右鍵 異步 一個 由於 編寫 scrip 調用 本文為雞毛巾原創,原文地址:http://www.cnblogs.com/jimaojin/p/7077131.html,轉載請註明 CefSharp說白了就是Chromium瀏覽器的嵌入式核心,我們用此開發W
1.一步一步學開發(遊戲賬服數據庫的使用 Erlang 服務器)
http ats 日誌收集 yield data obj 開發 用戶 nbsp mysql 與mongodb的特點與優劣 http://www.cnblogs.com/eternal1025/p/5419905.html 首先我們來分析下mysql 與mongodb的特
一步一印,有印為證
有物為證 之前一段時間告誡自己和家人:"路很長也很短,很短也很長,每天進步一點點,自律",最近沈下心態,思索最近半年的工作經歷和當下生活狀態,借用馬未都先生節目中的一句話“觀復嘟嘟,有物為證”稍作修改來描述近來心態變化,那就是“一步一腳印,有印為證”,思考良久其實二者的關系並不矛盾,之前強調的是放慢角度
利用 Siblings一步實現多個同級div,只改變當前點擊的div樣式
ima 可選參數 cto 一個 五個 點擊 rem wid bin 記錄一點,小技巧。直接上代碼嘍,因為今天還沒有功夫扯皮呢。 <!DOCTYPE html> <html> <head&g
微信小程序的認證流程,極限工坊一步一步教會你
nag 進行 text 根據 服務號 登陸 dad 訂閱號 手機號碼 微信小程序作為一種輕巧靈活的手機應用,改變著手機互聯網形態的同時,也在改變著我們的生活方式。 下面淘小咖具體教大家如何輕松快捷註冊小程序,看圖教程一步一步來! 1、小程序賬號註冊 百度搜索或者直接在公眾平
U盤安裝蘋果系統教程,菜鳥一步一步也能成大牛
女朋友蘋果電腦遇到問題,女朋友不會整,又怕自己整壞了,就跑去電腦城修,結果被坑了150塊,本來說要雙系統Mac和Windows,結果店家拿著電腦就跑出去了(並不知道幹啥去了),回來發現只有win7,沒有Mac,並且索要正版的價格,我尼瑪,就坑我小女朋友,還好小女朋友沒給他,本來就是盜版系
Spring的前世今生: Spring5.0已經出來了,Springboot已經風靡全球, Spring怎麼一步一步走過來的, 讓我們看看其前世今生~~~
Spring5.0已經出來了,Springboot已經風靡全球, Spring怎麼一步一步走過來的, 讓我們看看其前世今生~~~ 這是關於什麼是Spring Framework,但它是如何開發的?嗯,背後有一個有趣的歷史。讓我們一瞥Spring Framework的
中國正攜區塊鏈下一步新金融的大棋,深圳極可能成為落子的第一步
當眾人還在為加密貨幣的漲跌而喋喋不休時,中國已悄然攜區塊鏈下一盤關乎中國新金融新經濟的大棋,而深圳極有可能成為落子的第一步。 金融業務一直被視為區塊鏈技術的第一大應用,8月中旬以來,儘管監管層接連收緊了對加密貨幣的監管,但對區塊鏈在金融創新上的推動卻始終低調而急促,國
Jquery 分頁外掛, 帶你一步一步接入後臺資料
目錄 一、效果圖 二、分頁 js 原始碼講解 三、分頁樣式 css 原始碼 三、實現前後臺對接 一、效果圖 二、分頁 js 原始碼講解 新建一個 js 檔案,基本直接複製貼上就行,記得引入到需要的頁面中。 需要注意的是: 前面的建構函式
抖音、吃雞、王者榮耀:你的自律,是如何被頂級產品經理一步一步毀掉的
作者:書單君 來源:書單(ID:BookSelection) 01 你的沉迷 跟這個時代有關 這是個特別容易沉迷的時代。 抖音、煲劇、王者榮耀、吃雞遊戲……你的時間和注意力悄悄被它們偷走,卻從不說再
(原創)超詳細一步一步在eclipse中配置Struts2環境,無基礎也能看懂
(原創)超詳細一步一步在eclipse中配置Struts2環境,無基礎也能看懂 1. 在官網https://struts.apache.org下載Struts2,建議下載2.3系列版本。從圖中可以看出,既可以分開下載子檔案,又可以一次全部下載。 這裡我後
分享我這8年,是如何一步一步走向架構師的
摘要:心血經驗分享,架構師更多的是一個不斷學習,不斷積累的過程,希望可以幫到同行業的朋友們 前言 成為優秀的架構師是大部分初中級工程師的階段性目標。優秀的架構師往往具備七種核心能力:程式設計能力、除錯能力、編譯部署能力、效能優化能力、業務架構能力、線上運維能力、專案管理能力和規劃能力。 這幾種能力之
自定義實現SpringMvc框架,自定義@Controller、@RequestMapping註解,自己也是一步一步的對程式碼的理解出來的,只是比較簡單的例子
1.自定義的DispatcherServlet,就是SpringMvc執行載入類 /*** * 手寫SpringMvc框架<br> * 思路:<br> * 1.手動建立一個DispatcherServlet 攔截專案的所有請求 SpringMv
stm8 觸控庫使用教程 一步一步
配套的檔案資料會上傳 1、檢視觸控庫說明文件,根據需要選取晶片(主要支援幾個鍵):STMTouch Driver User Manual 一個channel為一個按鈕,比如STM8L101F 就支援3個按鈕,通過按鈕擴充方式可以增加按鈕,參照: 增加觸控感測按鈕數量