/** * 仅提供简单的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);