在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整、通用的事件检测方案。

事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这在编写Javascript的过程中也非常重要,如mouseenter/mouseleave事件虽然实用,但并不是所有浏览器都提供了标准的支持,因此需要自己手动模拟,即:

function addEvent(element, name, handler) {if (name == 'mouseenter' && !hasEvent(name, element)) {//通过其他手段模拟mouseenter事件}//正常的事件注册
};

本文就重点讲述以上代码中hasEvent的具体实现。

基本方案

关于事件的最基本检测方式,则需要从事件的注册方法开始说。

事件通常有3种注册方式,其中之一就是内联式,即在HTML中通过属性的方式声明事件,比如:

<button onclick="alert('CLICKED!');">CLICK ME</button>

以上代码创建了一个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('div');var supported = name in element;
};

需要注意的是,事件是对on[事件名]的形式作为元素的属性而存在的,因此从通用性上考虑,在必要的时候对事件名补上'on'即可。另外由于是一个通用的判断事件是否可用的函数,当没有给定具体的元素时,可以使用最广泛应用的div元素作为替代。

部分标签特有事件

有些事件是一些元素特有的,通常包括以下几个:

  • form独有事件:submit、reset
  • input独有事件:change、select
  • img独有事件:load、error、abort

考虑到这些事件的存在,使用div元素有时会得到错误的结果,因此在创建一个通用的替代用元素时,可以使用一个字典来维护需要创建的元素标签名:

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] || 'div');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); //弹出functionalert(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对象的事件检测还没有一个完整的方案。

对于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很诡异地可以在div等元素上检测到以上3个事件,这直接导致对普通DOM元素检测事件的错误,也导致我们可以检测到window上的事件。好在一般开发者也不会去一个div之类的元素上检测是否有unload事件。因此补充hasEvent函数,将window上的事件导向一个div对象来检测部分事件:

if (!supported) {if (!element.setAttribute || !element.removeAttribute) {element = document.createElement('div');}element.setAttribute(name, 'return;');supported = typeof element[name] == 'function';element.removeAttribute(name);
}

至此,一个较为完整的hasEvent函数完成了,虽然在Firefox上还存在一些问题,比如以下的代码:

alert(hasEvent('unload', document.createElement('div')); //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] || 'div');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,但是div有unload事件(OTL)if (!supported) {if (!element.setAttribute || !element.removeAttribute) {element = document.createElement('div');}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树结构发生变化时才会被触发,因此可以使用下面这套逻辑去检测:

  1. 准备一个标记位,默认为false。
  2. 创建出一个DOM树结构。
  3. 注册一个Mutation Event。
  4. 通过一定手段让这个DOM树变化,从而触发注册的事件。
  5. 在事件处理函数中,将标记位设为true。
  6. 返回标记位。

具体的实现代码可以如下:

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', 'div', function (div) { div.id = 'new'; });

对于其他事件的检测,同样只需要制作出一个特定的change函数即可。

DOMContentLoaded

这个事件在文档加载完成时触发,但不需要等待图片等资源下载,多数Javascript框架的document.ready都会试图使用这个事件。

无论是hasEvent函数还是hasMutationEvent函数都无法检测到这个事件,但是问题不大,因为:

  1. 这事件和onload一样,页面的生命周期中只会触发一次,不会频繁使用。
  2. 所有支持addEventListener的浏览器都支持这个事件(包括IE9),因此判断简单。

所以这个事件被排除在了本文讨论范围之外,具体的可以查看各框架的document.ready函数的实现方式。

相关资源

  • Detecting event support without browser sniffing为本文提供了大量的思路。
  • Diego Perini’s NWMatcher提供了Mutation Event检测的思路。
  • 点此查看hasEvent和hasMutationEvent的源码。

哪位无聊就把所有的Mutation Event的检测函数写出来吧……

转载于:https://www.cnblogs.com/GrayZhang/archive/2010/10/29/feature-detection-event.html

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

  1. php事件检测,细说浏览器特性检测(2)-通用事件检测_jquery

    在上一篇中介绍了jQuery1.4版本新增的几个浏览器特性检测方案和具体的目的,本文将以事件为中心,介绍一个较为完整.通用的事件检测方案. 事件检测,即检测某一事件在不同的浏览器中是否存在(可用),这 ...

  2. 细说浏览器特性检测(1)-jQuery1.4添加部分

    浏览器特性检测即通过探测对象是否拥有某个属性或者函数,或者通过其他的编码探测方式,来决定其是否支持某一功能.特性.其最经典的运用莫过于通用的addEvent函数: function addEvent( ...

  3. 大华事件检测智能服务器,大华股份:高速公路智能事件检测 解决方案

    1方案背景 随着我国基础设施建设的飞速发展,公路尤其是高速公路的总里程数年年攀升.交通运输部统计数据显示,截止2017年底全国公路总里程477.35万公里,其中高速公路13.65万公里,位居世界第一. ...

  4. 网联V2X视频事件检测相机使用说明书

    1 产品概览 网联 V2X视频事件检测相机 视频事件检测相机 ,内置 1/1.8″逐行扫描 800万像素传感器:视 万像素传感器:视 频编码协议支持 H.265.H.264.MJPEG:具有 1个 1 ...

  5. 基于锚框与无需锚框的通用物体检测算法

    物体检测通常是指在图像中检测出物体出现的位置及对应的类别,是计算机视觉的根本问题,也是最基础的问题.它广泛应用于日常生活中,如浏览器的拍照识图.自动驾驶行人车辆检测.道路目标检测(人行道检测)及图像分 ...

  6. 基于昇腾AI异构计算架构CANN的通用目标检测与识别一站式方案初体验

    前言 强大的社会粘性不断催温数字化发展,目标检测与识别作为计算机视觉领域的一项关键技术,应用场景广泛,前景十分广阔,从城市治理.楼宇园区.互联网等领域,延伸至智能家居.金融.医疗影像等更多创新领域.随 ...

  7. python tensorflow2 deeplearning 音频处理 声学事件检测

    文章目录 1 pycharm查看函数信息 3线性回归程序 4理解卷积神经网络中的通道 5 mnist分类 6 实验:使用卷积递归神经网络进行单通道和多通道声音事件检测. 6.1 遇到的问题: 6.1. ...

  8. 何为非侵入式负荷识别-事件检测

    1 事件检测方法综述 现有的事件检测方法主要分为两类:规则检测和概率模型检测.还有其他的,但是主要是这两类. 规则检测的方法是直接观察待测信号的变化,提出一种规则作为事件检测判据,规则合理与否就决定了 ...

  9. 通用目标检测的review

    今晚去蹭了超强师姐的讲座,以下是今晚的笔记. 目前存在的目标检测可以分为以下几类: One-short, few-short, weakly supervised object detection:这 ...

最新文章

  1. 安装Synchronization service (Project Server 2007) 时出现 MSMQ 错误的解决
  2. 【JVM调优】JVM内存管理调优浅谈
  3. android ContentObserver监听系统短信和备份短信到本地
  4. CodeFrist基础_迁移更新数据
  5. c语言打印树形图形,数据结构C语言版树形结构.ppt
  6. ssh连接远程linux环境
  7. 浪漫的表白 (5 分)
  8. 力扣62-不同路径(C++,留个思路)
  9. 点云谱聚类实现代码_Spark跑DBSCAN算法,工业级代码长啥样?
  10. python015 Python3 函数
  11. ubuntu16.04 安装图像界面,设置自动登录以及取消休眠模式
  12. python表白代码大全简单-python表白代码
  13. 2017华为软挑——遗传算法
  14. 组成原理---控制器
  15. Adobe CS5 序列号及配置方法
  16. 3600000毫秒等于多少小时_一秒多少毫秒
  17. windows设置tomcat守护进程
  18. 计算机专业英语课程整体设计,计算机专业英语的课程整体.doc
  19. 现阶段人工智能应用涉及到哪些行业?
  20. awgn matlab,Matlab实现加性高斯白噪声信道(AWGN)下的digital调制格式识别分类

热门文章

  1. C与C++之间相互调用
  2. 感恩心成就了车建新和红星美凯龙
  3. activity的四种加载模式
  4. 优雅的理解 call 和 apply 的使用方法
  5. expect脚本同步文件、 expect脚本指定host和要同步的文件 、 构建文件分发系统 、批量远程执行命令...
  6. 啥不懂也能动手搭建属于自己的博客网站
  7. html5 canvas雨点打到窗玻璃动画
  8. window.open的小技巧分享(转)
  9. SqlServer时间函数的使用例子整理
  10. CCNA基础知识汇总