php事件检测,细说浏览器特性检测(2)-通用事件检测_jquery
在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整、通用的事件检测方案。
事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这在编写Javascript的过程中也非常重要,如mouseenter/mouseleave事件虽然实用,但并不是所有浏览器都提供了标准的支持,因此需要自己手动模拟,即:function addEvent(element, name, handler) {
if (name == 'mouseenter' && !hasEvent(name, element)) {
//通过其他手段模拟mouseenter事件
}
//正常的事件注册
};
本文就重点讲述以上代码中hasEvent的具体实现。
基本方案
关于事件的最基本检测方式,则需要从事件的注册方法开始说。
事件通常有3种注册方式,其中之一就是内联式,即在HTML中通过属性的方式声明事件,比如:CLICK ME
以上代码创建了一个button标签,并注册了click事件。
另一个方案是通过直接给onclick赋值来注册事件:document.getElementById('myButton').onclick = function() {
alert('CLICKED!');
};
从上面两种注册事件的方式可以发现,其实onclick是button标签的一种属性(attribute),通过对其赋值可以完成事件的注册。
因此,最基本的事件检测方案,就是通过检查on[事件名]属性是否存在于DOM元素之中,因此有最简单的一个版本:function hasEvent(name, element) {
name = name.indexOf('on') ? 'on' + name : name;
element = element || document.createElement('p');
var supported = name in element;
};
需要注意的是,事件是对on[事件名]的形式作为元素的属性而存在的,因此从通用性上考虑,在必要的时候对事件名补上'on'即可。另外由于是一个通用的判断事件是否可用的函数,当没有给定具体的元素时,可以使用最广泛应用的p元素作为替代。
部分标签特有事件
有些事件是一些元素特有的,通常包括以下几个:form独有事件:submit、reset
input独有事件:change、select
img独有事件:load、error、abort
考虑到这些事件的存在,使用p元素有时会得到错误的结果,因此在创建一个通用的替代用元素时,可以使用一个字典来维护需要创建的元素标签名:var hasEvent = (function() {
var tags = {
onsubmit: 'form', onreset: 'form',
onselect: 'input', onchange: 'input',
onerror: 'img', onload: 'img', onabort: 'img'
};
return function(name, element) {
name = name.indexOf('on') ? 'on' + name : name;
element = element || document.createElement(tags[name] || 'p');
supported = name in element;
}
})();
使用闭包将tags作为静态的字典使用,可以在一定程度上减少对象生成的开销。
DOM污染
DOM元素之所以会有类似onclick的属性,是因为在DOM元素对象的__proto__中有这个属性,由于Javascript弱类型机制,外部代码可以通过对__proto__添加属性而影响hasEvent函数的结果,如以下代码在Firefox和Chrome中就会产生错误的结果:document.createElement('p').__proto__.ontest = function() {};
var supported = hasEvent('test', document.createElement('p')); //true
在上面的示例中,虽然在修改__proto__属性和调用hasEvent时,使用的是不同的p对象,但由于__proto__的实质是原型链中的对象,因此会影响到所有的p对象。
为了处理这种情况,需要尝试将__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[事件名]来获取注册在上面的函数对象,例如:CLICK ME
var button = document.getElementById('test');
alert(typeof button.onclick); //弹出function
alert(typoef button.ontest); //弹出string
因此,只需要通过Javascript将一个表示函数的字符串挂载到on[事件名]属性(attribute)上,再去获取并判断是否得到了一个函数对象即可。
因此hasEvent函数在前文提供的方法返回false时,可以额外增加以下的代码以进一步确定事件是否存在:if (!supported) {
element.setAttribute(name, 'return;');
supported = typeof element[name] == 'function';
}
Firefox继续BUG
到现在为止,已经可以在兼容多数浏览器的情况下检测各DOM元素的事件,但是对于window对象的事件检测还没有一个完整的方案。
对于IE系列、Chrome和Safari,都可以使用简单的on[事件名] in window检测事件是否存在,因此原有的提供防止DOM污染后的hasEvent函数可以很好地完成任务。
唯有Firefox上,以下代码会给出错误的结果:alert('onload' in window); //Firefox弹出false
alert('onunload' in window); //Firefox弹出false
alert('onerror' in window); //Firefox弹出false
值得庆幸也值得愤怒的是,Firefox很诡异地可以在p等元素上检测到以上3个事件,这直接导致对普通DOM元素检测事件的错误,也导致我们可以检测到window上的事件。好在一般开发者也不会去一个p之类的元素上检测是否有unload事件。因此补充hasEvent函数,将window上的事件导向一个p对象来检测部分事件:if (!supported) {
if (!element.setAttribute || !element.removeAttribute) {
element = document.createElement('p');
}
element.setAttribute(name, 'return;');
supported = typeof element[name] == 'function';
element.removeAttribute(name);
}
至此,一个较为完整的hasEvent函数完成了,虽然在Firefox上还存在一些问题,比如以下的代码:alert(hasEvent('unload', document.createElement('p')); //Firefox弹出true
但是在99%的应用场合之下,这个函数是可以正确的工作的。
添加缓存
为了进一步提高hasEvent的工作效率,考虑到DOM规范规定的事件数量不多,可以对通用的事件(即不指定检测的元素对象)检测添加缓存机制。
添加了缓存之后,最终完整的hasEvent函数如下:var hasEvent = (function () {
var tags = {
onsubmit: 'form', onreset: 'form',
onselect: 'input', onchange: 'input',
onerror: 'img', onload: 'img', onabort: 'img'
},
cache = {};
return function(name, element) {
name = name.indexOf('on') ? 'on' + name : name;
//命中缓存
if (!element && name in cache) {
return cache[name];
}
element = element || document.createElement(tags[name] || 'p');
var proto = element.__proto__ || {},
supported = name in element,
temp;
//处理显示在元素的__proto__上加属性的情况
if (supported && (temp = proto[name]) && delete proto[name]) {
supported = name in element;
proto[name] = temp;
}
//处理Firefox不给力的情况
//Firefox下'onunload' in window是false,但是p有unload事件(OTL)
if (!supported) {
if (!element.setAttribute || !element.removeAttribute) {
element = document.createElement('p');
}
element.setAttribute(name, 'return;');
supported = typeof element[name] == 'function';
element.removeAttribute(name);
}
//添加到缓存
cache[name] = supported;
return supported;
};
})();
Mutation Event
Mutation Event是由DOM Level 2制定的一类特殊的事件,这些事件在某个元素为根的DOM树结构发生变化时触发,可以在这里看到具体的事件列表。
遗憾的是hasEvent函数无法检测到Mutation Event,因此对于此类事件,需要另一种较为复杂的事件检测方案。
从Mutation Event的列表中可以发现,此类事件的特点在于当DOM树结构发生变化时才会被触发,因此可以使用下面这套逻辑去检测:准备一个标记位,默认为false。
创建出一个DOM树结构。
注册一个Mutation Event。
通过一定手段让这个DOM树变化,从而触发注册的事件。
在事件处理函数中,将标记位设为true。
返回标记位。
具体的实现代码可以如下:function hasMutationEvent(name, tag, change) {
var element = document.createElement(tag),
supported = false;
function handler() {
supported = true;
};
//IE9开始支持addEventListener,因此只有IE6-8没有这个函数
//但是IE6-8已经确定不支持Mutation Event,所以有这个判断
if (!element.addEventListener) {
return false;
}
element.addEventListener(name, handler, false);
change(element);
element.removeEventListener(name, handler, false);
return supported;
};
例如需要检测DOMAttrModified事件是否存在,只需要用以下代码:var isDOMAttrModifiedSupported =
hasMutationEvent('DOMAttrModified', 'p', function (p) { p.id = 'new'; });
对于其他事件的检测,同样只需要制作出一个特定的change函数即可。
DOMContentLoaded
这个事件在文档加载完成时触发,但不需要等待图片等资源下载,多数Javascript框架的document.ready都会试图使用这个事件。
无论是hasEvent函数还是hasMutationEvent函数都无法检测到这个事件,但是问题不大,因为:这事件和onload一样,页面的生命周期中只会触发一次,不会频繁使用。
所有支持addEventListener的浏览器都支持这个事件(包括IE9),因此判断简单。
所以这个事件被排除在了本文讨论范围之外,具体的可以查看各框架的document.ready函数的实现方式。
相关标签:事件检测
本文原创发布php中文网,转载请注明出处,感谢您的尊重!
php事件检测,细说浏览器特性检测(2)-通用事件检测_jquery相关推荐
- 细说浏览器特性检测(2)-通用事件检测
在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整.通用的事件检测方案. 事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这 ...
- 细说浏览器特性检测(1)-jQuery1.4添加部分
浏览器特性检测即通过探测对象是否拥有某个属性或者函数,或者通过其他的编码探测方式,来决定其是否支持某一功能.特性.其最经典的运用莫过于通用的addEvent函数: function addEvent( ...
- 检测Web浏览器上的内存泄漏
目录 背景 拍摄堆快照 一个真实世界的例子--AsyncSubject 附加说明 兴趣点 下载源 - 1.2 KB 背景 当您关闭浏览器选项卡时,所有内存都将被释放.内存泄漏在Web浏览器上很可能不是 ...
- 检测IE浏览器类型并跳转至谷歌浏览器打开网页
一.检测是否是IE浏览器,然后展现跳转或下载页面 不使用userAgent的原因:可被修改,没有检测ActiveX特性坚定 if (!!window.ActiveXObject || "Ac ...
- 用户代理检测与浏览器Ua详细分析
. 首页 博客园 联系我 前言:用户代理字符串与用户代理检测. 先看结论/可用代码. 浏览器市场份额现状. Mozilla Firefox. Microsoft Internet Explorer. ...
- 大华事件检测智能服务器,大华股份:高速公路智能事件检测 解决方案
1方案背景 随着我国基础设施建设的飞速发展,公路尤其是高速公路的总里程数年年攀升.交通运输部统计数据显示,截止2017年底全国公路总里程477.35万公里,其中高速公路13.65万公里,位居世界第一. ...
- QQ浏览器之后,Vivo NEX又检测出百度手机输入法后台录音
(点击上方蓝字,快速关注我们) 转自:cnBeta.观察者 由于vivo NEX的设计机制,前置摄像头隐藏在机身内,而在上市之后不久就曝出有应用调用镜头被发现的情况,而vivo也在上周四更新了全新的固 ...
- JS window对象 Navigator对象 Navigator 对象包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本。...
Navigator对象 Navigator 对象包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本. 对象属性: 查看浏览器的名称和版本,代码如下: <script type=" ...
- userAgent,JS用户代理检测——判断浏览器内核、浏览器、浏览器平台、windows操作系统版本、移动设备、游戏系统
1.识别浏览器呈现引擎 为了不在全局作用域中添加多余变量,这里使用单例模式(什么是单例模式?)来封装检测脚本.检测脚本的基本代码如下所示: 1 var client = function() { 2 ...
最新文章
- React Nativi 参考文章
- ajax从mysql提取数据在html中_Python骚操作,提取pdf文件中的表格数据!
- 计网 - 传输层协议 TCP:TCP 为什么握手是 3 次、挥手是 4 次?
- 为什么 Redis 要比 Memcached 更火?
- three.js 几何体-组合网格_3dmax利用优化和多边形倒角制作饰品组合1
- spark、oozie、yarn、hdfs、zookeeper、
- AddMvc 和 AddMvcCore 的区别
- [转]Zookeeper入门看这篇就够了
- Linux基础之命令练习Day2-useradd(mod,del),groupadd(mod,del),chmod,chown,
- java日历类add方法_Java日历computeTime()方法及示例
- WPF中同一窗口下的界面切换
- android手势第一次设置密码_Android实现手势密码功能
- 易支付v5.8个人二维码免签系统破解版
- 秋招之8.31农行研发中心笔试
- 中科院阿里云联合发布11比特云接入超导量子计算服务
- java 编辑器_推荐 5 款牛逼的代码编辑器
- 如何实现跨项目共享token
- Windows Audio无法启动 错误 0x80070005:拒绝访问
- UE4_模型_Bound(边界)
- React Native 布局实现测试
热门文章
- 各位加了一天班累了吧?那我们来继续未完的表单验证吧
- 如何查看linux命令源代码(转自网络)
- JS中的关于类型转换的性能优化
- 人生总是起起落落落落落落...
- Java Web-网页基础-HTML-选择器Selector-DOM
- win2003 apache php5.4 mysql_【php】在Windows2003下配置Apache2.4与php5.4
- 案例解读:利用12c渐进式DASH分析ON CPU
- PostgreSQL实际场景的十大缺陷你知道吗?
- 中国人民大学教授杜小勇:One Size Does not Fit All?
- 给数据库减负的八个思路,盘它!