一、前言
event.js主要用于提供注册自定义事件和手动触发事件等功能。
二、源码
(
function ($) {
var _zid =
1,
undefined,
slice =
Array.prototype.slice,
isFunction = $.isFunction,
isString =
function (obj) {
return typeof obj ==
'string'
},
handlers = {},
specialEvents = {},
focusinSupported =
'onfocusin' in window,
focus = {focus:
'focusin', blur:
'focusout'},
hover = {mouseenter:
'mouseover', mouseleave:
'mouseout'};
specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove =
'MouseEvents';
function zid(element) {
return element._zid || (element._zid = _zid++)
}
function findHandlers(element, event, fn, selector) {
event = parse(event);
if (event.ns)
var matcher = matcherFor(event.ns);
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))
&& (!selector || handler.sel == selector)
})
}
function parse(event) {
var parts = (
'' + event).split(
'.');
return {e: parts[
0], ns: parts.slice(
1).sort().join(
' ')}
}
function matcherFor(ns) {
return new RegExp(
'(?:^| )' + ns.replace(
' ',
' .* ?') +
'(?: |$)')
}
function eventCapture(handler, captureSetting) {
return handler.del &&
(!focusinSupported && (handler.e
in focus)) || !!captureSetting
}
function realEvent(type) {
return hover[type] || (focusinSupported && focus[type]) || type
}
function add(element, events, fn, data, selector, delegator, capture) {
var id = zid(element), set = (handlers[id] || (handlers[id] = []));
events.split(
/\s/).forEach(
function (event) {
if (event ==
'ready')
return $(document).ready(fn);
var handler = parse(event);
handler.fn = fn;
handler.sel = selector;
if (handler.e
in hover) fn =
function (e) {
var related = e.relatedTarget;
if (!related || (related !==
this && !$.contains(
this, related)))
return handler.fn.apply(
this,
arguments)
};
handler.del = delegator;
var callback = delegator || fn;
handler.proxy =
function (e) {
e = compatible(e);
if (e.isImmediatePropagationStopped())
return;
e.data = data;
var result = callback.apply(element, e._args ==
undefined ? [e] : [e].concat(e._args));
if (result ===
false) e.preventDefault(), e.stopPropagation();
return result
};
handler.i = set.length;
set.push(handler);
if (
'addEventListener' in element)
element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
})
}
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};
$.proxy =
function (fn, context) {
var args = (
2 in arguments) && slice.call(
arguments,
2);
if (isFunction(fn)) {
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")
}
};
$.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)
};
var returnTrue =
function () {
return true
},
returnFalse =
function () {
return false
},
ignoreProperties =
/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,
eventMethods = {
preventDefault:
'isDefaultPrevented',
stopImmediatePropagation:
'isImmediatePropagationStopped',
stopPropagation:
'isPropagationStopped'
};
function compatible(event, source) {
if (source || !event.isDefaultPrevented) {
source || (source = event);
$.each(eventMethods,
function (name, predicate) {
var sourceMethod = source[name];
event[name] =
function () {
this[predicate] = returnTrue;
return sourceMethod && sourceMethod.apply(source,
arguments)
};
event[predicate] = returnFalse
});
event.timeStamp || (event.timeStamp =
Date.now());
if (source.defaultPrevented !==
undefined ? source.defaultPrevented :
'returnValue' in source ? source.returnValue ===
false :
source.getPreventDefault && source.getPreventDefault())
event.isDefaultPrevented = returnTrue
}
return event
}
function createProxy(event) {
var key, proxy = {originalEvent: event};
for (key
in event)
if (!ignoreProperties.test(key) && event[key] !==
undefined) proxy[key] = event[key];
return compatible(proxy, event)
}
$.fn.delegate =
function (selector, event, callback) {
return this.on(event, selector, callback)
};
$.fn.undelegate =
function (selector, event, callback) {
return this.off(event, selector, callback)
};
$.fn.live =
function (event, callback) {
$(document.body).delegate(
this.selector, event, callback);
return this
};
$.fn.die =
function (event, callback) {
$(document.body).undelegate(
this.selector, event, callback);
return this
};
$.fn.on =
function (event, selector, data, callback, one) {
var autoRemove, delegator, $
this =
this;
if (event && !isString(event)) {
$.each(event,
function (type, fn) {
$
this.on(type, selector, data, fn, one)
});
return $
this
}
if (!isString(selector) && !isFunction(callback) && callback !==
false)
callback = data, data = selector, selector =
undefined;
if (callback ===
undefined || data ===
false)
callback = data, data =
undefined;
if (callback ===
false) callback = returnFalse;
return $
this.each(
function (_, element) {
if (one) autoRemove =
function (e) {
remove(element, e.type, callback);
return callback.apply(
this,
arguments)
};
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)
})
};
$.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)
})
};
$.fn.trigger =
function (event, args) {
event = (isString(event) || $.isPlainObject(event)) ? $.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)
this.dispatchEvent(event);
else $(
this).triggerHandler(event, args)
})
};
$.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
};
(
'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) {
$.fn[event] =
function (callback) {
return (
0 in arguments) ?
this.bind(event, callback) :
this.trigger(event)
}
});
$.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)
};
})(Zepto);
三、源码分析
$.Event
注册自定义事件核心流程: 1、document.createEvent(type) 创建一个新的自定义事件 2、event.initEvent(type, bubbles, cancelable) 初始化自定义事件 3、compatible(event) 给事件添加isDefaultPrevented、isImmediatePropagationStopped、isPropagationStopped方法
$.fn.on
on方法主要调用原生的target.addEventListener(type, listener[, useCapture]);方法。 1、$.fn.one方法是在on方法的回调函数中调用remove方法移除事件监听 2、如果on方法中有选择器,虽然是必须在子元素上点击才有效果,但是事件监听其实还是在父元素上,只不过在回调函数中设置了如果不是匹配的子元素触发不进行操作
$.fn.off
off方法主要调用原生target.removeEventListener(type, listener[, useCapture]);方法,与$.fn.on相对
$.fn.trigger
trigger方法判断元素如果是dom元素触发的,则调用原生target.dispatchEvent(event)方法;如果不是dom元素上触发的,那么直接手动调用回调函数。
handlers
事件缓存池,用于移除或者触发监听函数找到相应的回调函数,然后进行相应操作。