写在前面
经由过程本文,您能够相识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)
}
});