|
|
/**
|
|
|
* 仅提供简单的on,off(仅支持事件委托,不支持当前元素绑定,当前元素绑定请直接使用addEventListener,removeEventListener)
|
|
|
* @param {Object} $
|
|
|
*/
|
|
|
(function($) {
|
|
|
if ('ontouchstart' in window) {
|
|
|
$.isTouchable = true;
|
|
|
$.EVENT_START = 'touchstart';
|
|
|
$.EVENT_MOVE = 'touchmove';
|
|
|
$.EVENT_END = 'touchend';
|
|
|
} else {
|
|
|
$.isTouchable = false;
|
|
|
$.EVENT_START = 'mousedown';
|
|
|
$.EVENT_MOVE = 'mousemove';
|
|
|
$.EVENT_END = 'mouseup';
|
|
|
}
|
|
|
$.EVENT_CANCEL = 'touchcancel';
|
|
|
$.EVENT_CLICK = 'click';
|
|
|
|
|
|
var _mid = 1;
|
|
|
var delegates = {};
|
|
|
//需要wrap的函数
|
|
|
var eventMethods = {
|
|
|
preventDefault: 'isDefaultPrevented',
|
|
|
stopImmediatePropagation: 'isImmediatePropagationStopped',
|
|
|
stopPropagation: 'isPropagationStopped'
|
|
|
};
|
|
|
//默认true返回函数
|
|
|
var returnTrue = function() {
|
|
|
return true
|
|
|
};
|
|
|
//默认false返回函数
|
|
|
var returnFalse = function() {
|
|
|
return false
|
|
|
};
|
|
|
//wrap浏览器事件
|
|
|
var compatible = function(event, target) {
|
|
|
if (!event.detail) {
|
|
|
event.detail = {
|
|
|
currentTarget: target
|
|
|
};
|
|
|
} else {
|
|
|
event.detail.currentTarget = target;
|
|
|
}
|
|
|
$.each(eventMethods, function(name, predicate) {
|
|
|
var sourceMethod = event[name];
|
|
|
event[name] = function() {
|
|
|
this[predicate] = returnTrue;
|
|
|
return sourceMethod && sourceMethod.apply(event, arguments)
|
|
|
}
|
|
|
event[predicate] = returnFalse;
|
|
|
}, true);
|
|
|
return event;
|
|
|
};
|
|
|
//简单的wrap对象_mid
|
|
|
var mid = function(obj) {
|
|
|
return obj && (obj._mid || (obj._mid = _mid++));
|
|
|
};
|
|
|
//事件委托对象绑定的事件回调列表
|
|
|
var delegateFns = {};
|
|
|
//返回事件委托的wrap事件回调
|
|
|
var delegateFn = function(element, event, selector, callback) {
|
|
|
return function(e) {
|
|
|
//same event
|
|
|
var callbackObjs = delegates[element._mid][event];
|
|
|
var handlerQueue = [];
|
|
|
var target = e.target;
|
|
|
var selectorAlls = {};
|
|
|
for (; target && target !== document; target = target.parentNode) {
|
|
|
if (target === element) {
|
|
|
break;
|
|
|
}
|
|
|
if (~['click', 'tap', 'doubletap', 'longtap', 'hold'].indexOf(event) && (target.disabled || target.classList.contains($.className('disabled')))) {
|
|
|
break;
|
|
|
}
|
|
|
var matches = {};
|
|
|
$.each(callbackObjs, function(selector, callbacks) { //same selector
|
|
|
selectorAlls[selector] || (selectorAlls[selector] = $.qsa(selector, element));
|
|
|
if (selectorAlls[selector] && ~(selectorAlls[selector]).indexOf(target)) {
|
|
|
if (!matches[selector]) {
|
|
|
matches[selector] = callbacks;
|
|
|
}
|
|
|
}
|
|
|
}, true);
|
|
|
if (!$.isEmptyObject(matches)) {
|
|
|
handlerQueue.push({
|
|
|
element: target,
|
|
|
handlers: matches
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
selectorAlls = null;
|
|
|
e = compatible(e); //compatible event
|
|
|
$.each(handlerQueue, function(index, handler) {
|
|
|
target = handler.element;
|
|
|
var tagName = target.tagName;
|
|
|
if (event === 'tap' && (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && tagName !== 'SELECT')) {
|
|
|
e.preventDefault();
|
|
|
e.detail && e.detail.gesture && e.detail.gesture.preventDefault();
|
|
|
}
|
|
|
$.each(handler.handlers, function(index, handler) {
|
|
|
$.each(handler, function(index, callback) {
|
|
|
if (callback.call(target, e) === false) {
|
|
|
e.preventDefault();
|
|
|
e.stopPropagation();
|
|
|
}
|
|
|
}, true);
|
|
|
}, true)
|
|
|
if (e.isPropagationStopped()) {
|
|
|
return false;
|
|
|
}
|
|
|
}, true);
|
|
|
};
|
|
|
};
|
|
|
var findDelegateFn = function(element, event) {
|
|
|
var delegateCallbacks = delegateFns[mid(element)];
|
|
|
var result = [];
|
|
|
if (delegateCallbacks) {
|
|
|
result = [];
|
|
|
if (event) {
|
|
|
var filterFn = function(fn) {
|
|
|
return fn.type === event;
|
|
|
}
|
|
|
return delegateCallbacks.filter(filterFn);
|
|
|
} else {
|
|
|
result = delegateCallbacks;
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
};
|
|
|
var preventDefaultException = /^(INPUT|TEXTAREA|BUTTON|SELECT)$/;
|
|
|
/**
|
|
|
* mui delegate events
|
|
|
* @param {type} event
|
|
|
* @param {type} selector
|
|
|
* @param {type} callback
|
|
|
* @returns {undefined}
|
|
|
*/
|
|
|
$.fn.on = function(event, selector, callback) { //仅支持简单的事件委托,主要是tap事件使用,类似mouse,focus之类暂不封装支持
|
|
|
return this.each(function() {
|
|
|
var element = this;
|
|
|
mid(element);
|
|
|
mid(callback);
|
|
|
var isAddEventListener = false;
|
|
|
var delegateEvents = delegates[element._mid] || (delegates[element._mid] = {});
|
|
|
var delegateCallbackObjs = delegateEvents[event] || ((delegateEvents[event] = {}));
|
|
|
if ($.isEmptyObject(delegateCallbackObjs)) {
|
|
|
isAddEventListener = true;
|
|
|
}
|
|
|
var delegateCallbacks = delegateCallbackObjs[selector] || (delegateCallbackObjs[selector] = []);
|
|
|
delegateCallbacks.push(callback);
|
|
|
if (isAddEventListener) {
|
|
|
var delegateFnArray = delegateFns[mid(element)];
|
|
|
if (!delegateFnArray) {
|
|
|
delegateFnArray = [];
|
|
|
}
|
|
|
var delegateCallback = delegateFn(element, event, selector, callback);
|
|
|
delegateFnArray.push(delegateCallback);
|
|
|
delegateCallback.i = delegateFnArray.length - 1;
|
|
|
delegateCallback.type = event;
|
|
|
delegateFns[mid(element)] = delegateFnArray;
|
|
|
element.addEventListener(event, delegateCallback);
|
|
|
if (event === 'tap') { //TODO 需要找个更好的解决方案
|
|
|
element.addEventListener('click', function(e) {
|
|
|
if (e.target) {
|
|
|
var tagName = e.target.tagName;
|
|
|
if (!preventDefaultException.test(tagName)) {
|
|
|
if (tagName === 'A') {
|
|
|
var href = e.target.href;
|
|
|
if (!(href && ~href.indexOf('tel:'))) {
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
} else {
|
|
|
e.preventDefault();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
$.fn.off = function(event, selector, callback) {
|
|
|
return this.each(function() {
|
|
|
var _mid = mid(this);
|
|
|
if (!event) { //mui(selector).off();
|
|
|
delegates[_mid] && delete delegates[_mid];
|
|
|
} else if (!selector) { //mui(selector).off(event);
|
|
|
delegates[_mid] && delete delegates[_mid][event];
|
|
|
} else if (!callback) { //mui(selector).off(event,selector);
|
|
|
delegates[_mid] && delegates[_mid][event] && delete delegates[_mid][event][selector];
|
|
|
} else { //mui(selector).off(event,selector,callback);
|
|
|
var delegateCallbacks = delegates[_mid] && delegates[_mid][event] && delegates[_mid][event][selector];
|
|
|
$.each(delegateCallbacks, function(index, delegateCallback) {
|
|
|
if (mid(delegateCallback) === mid(callback)) {
|
|
|
delegateCallbacks.splice(index, 1);
|
|
|
return false;
|
|
|
}
|
|
|
}, true);
|
|
|
}
|
|
|
if (delegates[_mid]) {
|
|
|
//如果off掉了所有当前element的指定的event事件,则remove掉当前element的delegate回调
|
|
|
if ((!delegates[_mid][event] || $.isEmptyObject(delegates[_mid][event]))) {
|
|
|
findDelegateFn(this, event).forEach(function(fn) {
|
|
|
this.removeEventListener(fn.type, fn);
|
|
|
delete delegateFns[_mid][fn.i];
|
|
|
}.bind(this));
|
|
|
}
|
|
|
} else {
|
|
|
//如果delegates[_mid]已不存在,删除所有
|
|
|
findDelegateFn(this).forEach(function(fn) {
|
|
|
this.removeEventListener(fn.type, fn);
|
|
|
delete delegateFns[_mid][fn.i];
|
|
|
}.bind(this));
|
|
|
}
|
|
|
});
|
|
|
|
|
|
};
|
|
|
})(mui); |