代码改变世界

事件嗅探技术

2010-04-29 23:05  BlueDream  阅读(606)  评论(0)    收藏  举报

给浏览器绑定事件的兼容性很麻烦.因为不同的浏览器提供了不同的事件支持.比如IE系列的支持mouseenter/mouseleave事件.而opera不支持contextmenu以及input相关的onbeforepaste, onbeforecut等.

比如我们要绑定鼠标滚轮事件.那么通过浏览器嗅觉的方法.会是以下的伪代码:

 

if(co.browser.ie) {
    Event.on(document, 'mousewheel', callback);
} else {
    Event.on(document, 'DOMMouseScroll', callback);
}

 

但如果有一天IE支持了DOMMouseScroll事件的话.那么这段代码就又需要改变了.这样是很危险的.

那么有没有一种方法能够直接嗅探一个元素是否支持一个事件呢?

其实在DOM2事件标志是可以通过in来检测一个事件是否存在于一个元素

 

alert('onclick' in document.documentElement); // true
alert('onclick2' in document.documentElement); // false

 

而且要保证事件对应的元素可以是事件的起源地

 

alert('onreset' in document.documentElement); // false
alert('onreset' in document.createElement('input')); // true

 

可以看出通过in很容易就做到了事件支持检测.但不幸的是.firefox根本就不支持.但好的方法又被挖掘出来了.那就是将一个事件属性设置给要检测的元素.如果元素可以识别这个事件.那么这个事件会有一个事件句柄

 

var el = document.createElement('div');
el.setAttribute('onclick', 'return;');
typeof el.onclick; // "function"
el.setAttribute('onclick2', 'return;');
typeof el.onclick2; // "undefined"

 

通过上面的两个分支的分析.就可以写下这个检测函数: 

var isEventSupported = (function() {
    var TAGNAMES = {
        'select': 'input', 'change': 'input',
        'submit': 'form', 'reset': 'form',
        'error': 'img', 'load': 'img', 'abort': 'img'
    }, cache = {};
    function isEventSupported(eventName) {
        eventName = 'on' + eventName;
		if (eventName in cache) {
			return cache[eventName];
		} 
		var el = document.createElement(TAGNAMES[eventName] || 'div')
        var isSupported = (eventName in el),
			proto = el.__proto__ || {},
			temp;
		if (isSupported && (temp = proto[eventName]) && delete proto[eventName]) { // 防止DOM的__proto__被改写
			isSupported = eventName in el;
			proto[name] = temp;
		} 
        if (!isSupported ) {
            el.setAttribute(eventName, 'return;');
            isSupported = typeof el[eventName] == 'function';
			el.removeAttribute(eventName);
        } 
        el = null;
		cache[eventName] = isSupported;
        return isSupported;
        
    }
    return isEventSupported;
})();

到此.如果坚持opera浏览器是否支持contextmenu函数的话.就可以使用

 

alert(isEventSupported('contextmenu')); // opera -- false

 

不用使用浏览器探测.也不需要担心那些因素了.

因为Div可以作为所有鼠标事件的载体.那么如果单纯检测MouseEvent的支持.可以用下面的简化函数(JQ1.4也使用了)

 

var eventSupported = function( eventName ) { 
    var el = document.createElement("div"); 
    eventName = "on" + eventName; 

    var isSupported = (eventName in el); 
    if ( !isSupported ) { 
        el.setAttribute(eventName, "return;"); 
        isSupported = typeof el[eventName] === "function"; 
    } 
    el = null; 

    return isSupported; 
};

 

这样IE就可以用eventSupported('mouseenter')来检测了.

 

参考链接:

http://perfectionkills.com/detecting-event-support-without-browser-sniffing/