4.2 事件的支持侦测
Prototype的核心成员kangax 写了一篇叫《Detecting event support without browser sniffing》文章,来判定浏览器对某种事件的支持。里面给出的实现如下。
var isEventSupported = (function() { var TAGNAMES = { 'select': 'input', 'change': 'input', 'submit': 'form', 'reset': 'form', 'error': 'img', 'load': 'img', 'abort': 'img' } function isEventSupported(eventName) { var el = document.createElement(TAGNAMES[eventName] || 'div'); eventName = 'on' + eventName; var isSupported = (eventName in el); if (!isSupported) { el.setAttribute(eventName, 'return;'); isSupported = typeof el[eventName] == 'function'; } el = null; return isSupported; } return isEventSupported; })();
现在jQuery与mass使用的脚本都是其简化版,其中mass对IE内存泄漏做了优化。
$.eventSupport = function(eventName, el) { el = el || document.documentElement eventName = "on" + eventName; var ret = eventName in el; if (el.setAttribute && !ret) { el.setAttribute(eventName, ""); ret = typeof el[eventName] === "function"; el.removeAttribute(eventName); } el = null; return ret; };
不过哪一个也好,这种检测只对DOM0事件凑效,像DOMMouseScroll、DOMContentLoaded、DOMFocusIn、DOMFocusOut、DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved、DOMNodeRemovedFromDocument、DOMNodeInsertedIntoDocument、DOMAttrModified、DOM-CharacterDataModified这些以DOM开头的事件就无难为力了。
这些事件中有的非常有用,如:DOMMouseScroll,Firefox一直不支持mousewheel,只能用它做替代品;DOMContentLoaded 是实现domReady的重要事件;DOMNodeRemoved 是判定元素是否从其父节点移除,父节点可能是其他元素节点或文档碎片;DOMNodeRemovedFromDocument是移离DOM树;DOMAttrModified以前经常用于模拟IE的onpropertychange;DOMCharacterData-Modified用于监听contenteditable为true的元素内容变动。
在mass Framework中,是使用以下方法判定的。
//https://github.com/RubyLouvre/mass-Framework/blob/1.4/event.js try { //如果浏览器支持创建MouseScrollEvents事件对象,那么就用DOMMouseScroll document.createEvent("MouseScrollEvents"); eventHooks.mousewheel = { bindType: "DOMMouseScroll", delegateType: "DOMMouseScroll" }; //如果某一天,Firefox回心转意支持mousewheel,那么我们就不需要这个钩子 if ($.eventSupport("mousewheel")) { delete eventHooks.mousewheel; } } catch (e) { }
此外,mass还对focusin进行识别。focusin与focusout是一对,判定当中一个就明白另一个情况。这两个事件也很重要,用于实现focus与blur的事件代理,因为focus与blur不支持冒泡,需要用它们的冒泡版实现(假若不支持focusin与focusout,jQuery也找到办法了,不过有原生的就用原生的)。
//https://github.com/RubyLouvre/mass-Framework/blob/1.4/support.js#L108 //首先判定它是否是W3C阵营,IE肯定支持 $.support.focusin = !!window.attachEvent; $(function() { var div = document.createElement("div"); document.body.appendChild(div); div.innerHTML = "<a href='#'></a>"; if (!support.focusin) { a = div.firstChild; a.addEventListener('focusin', function() { $.support.focusin = true; }, false); a.focus(); } });
CSS3添加两种动画,一种是transition动画,第一种是keyframe补间动画,它们在结束时都有相应的事件回调。但在标准化过程中,浏览器给它们起的名字相当没规则。这个也需预先侦测出来。
下面是bootstrap的实现,听说来源于modernizr,比较粗糙。比如说你现在用的Opera已经支持不带前缀的标准事件名,它还是返回oTransitionEnd。
$.support.transition = (function() { var transitionEnd = (function() { var el = document.createElement('bootstrap'), transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd otransitionend', 'transition': 'transitionend' }; for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return transEndEventNames[name] } } }()) return transitionEnd && { end: transitionEnd } })()
keyframe补间动画的检测来自mass 的fx_neo模块,以后会说到的。
var eventName = { AnimationEvent: 'animationend', WebKitAnimationEvent: 'webkitAnimationEnd' }, animationend; for (var name in eventName) { if (/object|function/.test(typeof window[name])) { animationend = eventName[name] break } }