You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
6.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 仅提供简单的onoff(仅支持事件委托不支持当前元素绑定当前元素绑定请直接使用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);