细说浏览器特性检测(2)-通用事件检测

2020-05-17 06:19:02易采站长站整理

supported = name in element;
}
})();

使用闭包将tags作为静态的字典使用,可以在一定程度上减少对象生成的开销。


DOM污染

DOM元素之所以会有类似onclick的属性,是因为在DOM元素对象的__proto__中有这个属性,由于Javascript弱类型机制,外部代码可以通过对__proto__添加属性而影响hasEvent函数的结果,如以下代码在Firefox和Chrome中就会产生错误的结果:

document.createElement(‘div’).__proto__.ontest = function() {};
var supported = hasEvent(‘test’, document.createElement(‘div’)); //true

在上面的示例中,虽然在修改__proto__属性和调用hasEvent时,使用的是不同的div对象,但由于__proto__的实质是原型链中的对象,因此会影响到所有的div对象。


为了处理这种情况,需要尝试将__proto__属性中相应的属性进行删除,由于原生类型的属性带有DontDelete标记,是无法使用delete关键字进行删除的,因此对hasEvent函数附加以下的逻辑就可以更安全地判断:

var temp;
if (supported && (temp = proto[name]) && delete proto[name]) {
supported = name in element;
proto[name] = temp;
}

逻辑很简单,尝试把__proto__中有可能附加上去的删了再试一试,当然别忘了再把原来的值变回去。


Firefox开始BUG

很遗憾,前文提供的hasEvent函数并不能在Firefox完美工作,在Firefox中运行以下代码将得到false的结果:

alert(‘onclick’ in document.documentElement); //Firefox弹出false

因此,需要再次改造hasEvent函数以支持Firefox。在多数浏览器中,当元素使用内联方式注册了事件之后,可以通过element.on[事件名]来获取注册在上面的函数对象,例如:

<button id=”test” onclick=”alert(‘CLICKED!’);” ontest=”alert(‘TEST!’);”>CLICK ME</button>
<script type=”text/javascript”>
var button = document.getElementById(‘test’);
alert(typeof button.onclick); //弹出function
alert(typoef button.ontest); //弹出string
</script>

因此,只需要通过Javascript将一个表示函数的字符串挂载到on[事件名]属性(attribute)上,再去获取并判断是否得到了一个函数对象即可。


因此hasEvent函数在前文提供的方法返回false时,可以额外增加以下的代码以进一步确定事件是否存在:

if (!supported) {
element.setAttribute(name, ‘return;’);
supported = typeof element[name] == ‘function’;
}

Firefox继续BUG

到现在为止,已经可以在兼容多数浏览器的情况下检测各DOM元素的事件,但是对于window对象的事件检测还没有一个完整的方案。