当前位置: 首页 > 编程日记 > 正文

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

在上一篇中介绍了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

相关文章:

robot简单功能测试脚本设计(例子)

以学生管理系统的添加一个学生信息为例子页面对象&#xff1a;editbox&#xff08;姓名&#xff09;,button&#xff08;添加&#xff09;数据要求&#xff1a;1 姓名不能为空2 姓名不能重复程序结构1 点button&#xff0c;弹出对话框“姓名不能为空”2 输入姓名&#xff0c;点…

里氏替换原则(Liskov Substitution Principle,LSP)

昨天图文介绍了软件设计的一个基本原则“开闭原则”&#xff0c;而“开闭原则”的核心就是通过抽象把需求变化进行隔离&#xff0c;这种想法可以通过“里氏替换原则”进行保证。理解“里氏替换原则”也是理解面向对象中“运行时多态”的关键。希望大家仔细体会。

在IIS7里配置 ISAPI,运行dll程序,总提示下载dll

在IIS7里配置 ISAPI&#xff0c;运行dll程序&#xff0c;总提示下载dll&#xff0c;只需要把对应站点应用程序池里面的高级设置里的启用32位应用程序&#xff0c;设为“true"即可。

MySQL数据库高可用集群搭建-PXC集群部署

Percona XtraDB Cluster&#xff08;下文简称PXC集群&#xff09;提供了MySQL高可用的一种实现方法。集群是有节点组成的&#xff0c;推荐配置至少3个节点&#xff0c;但是也可以运行在2个节点上。 PXC原理描述&#xff1a; 分布式系统的CAP理论&#xff1a; C&#xff1a;一致…

搭建Jupyter学习环境

python notebook是一个基于浏览器的python数据分析工具&#xff0c;使用起来非常方便&#xff0c;具有极强的交互方式和富文本的展示效果。jupyter是它的升级版&#xff0c;它的安装也非常方便&#xff0c;一般Anaconda安装包中会自带。安装好以后直接输入jupyter notebook便可…

[转贴]2006十大经典语句

1. 骑白马的不一定是王子&#xff0c;他可能是唐僧&#xff1b; 2. 带翅膀的也不一定是天使&#xff0c;他可能是鸟人。 3. 站的更高&#xff0c;尿的更远。 4. 穿别人的鞋&#xff0c;走自己的路&#xff0c;让他们找去吧&#xff0c; 5. 我不是随便的人。我随便起来不是人 6.…

开放-封闭原则(The Open-Closed Principle,OCP)

自己设计的软件系统“易于维护”、“扩展性好”、“可重用”、“具有灵活性”&#xff0c;这是每位程序员所追求的目标。“开闭原则”为我们指明了方向&#xff0c;即我们所设计的软件尽量满足“开闭原则–对扩展开放&#xff0c;对修改关闭”&#xff0c;这样就能降低需求不断…

Interesting visualization tools for profiling.

Interesting visualization tools for profiling. http://dtrace.org/blogs/brendan/2012/03/17/linux-kernel-performance-flame-graphs/ http://dtrace.org/blogs/brendan/2013/07/01/detecting-outliers/转载于:https://www.cnblogs.com/kungfupanda/p/3245651.html

javascript网页开发 第二章

HTML高级部分 2.1. 表格标签 2.1.1 <table></table> Bgcolor 设置表格的背景色 Border 设置边框的宽度 Bordercolor 设置边框的颜色 Bordercolorlight 设置边框明亮部分的颜色 Bordercolordark 设置边框昏暗部分的颜色 Cellspacing 设置单元格之间的间隔大小 Cel…

ORACLE JET BASIC TABLE

转载于:https://blog.51cto.com/feitai/1917581

Visual Studio UML Use Case Diagram(1)

前几天我们介绍了Visual Studio UML Activity Diagram&#xff0c;今天我们介绍Visual Studio UML Use Case Diagram的内容。通常RUP按照动态划分&#xff0c;分为周期、阶段、里程碑、迭代&#xff0c;按照静态划分&#xff0c;分为角色、制品、工作流、活动&#xff0c;在Wor…

可以左右移动多选下拉列表的javaScipt(可以兼容IE和firefox)

自己在项目业余时间总结了一份可以左右移动&#xff08;Add和remove&#xff09;多选下拉列表的javaScipt,可以兼容IE和firefox,并且经过测试&#xff0c;只是代码略显臃肿&#xff0c;希望各位网友参考后给一些指点&#xff0c;特别是在简化代码方面。我在让其兼容 firefox很是…

OLAP和OLTP的区别(基础知识)

联机分析处理 (OLAP) 的概念最早是由关系数据库之父E.F.Codd于1993年提出的&#xff0c;他同时提出了关于OLAP的12条准则。OLAP的提出引起了很大的反响&#xff0c;OLAP作为一类产品同联机事务处理 (OLTP) 明显区分开来。当今的数据处理大致可以分成两大类&#xff1a;联机事务…

如何让phpmyadmin输入密码再进入

分类&#xff1a;wamp对于很多不熟悉PHP环境安装的朋友来说&#xff0c;用集成环境可以更快的上手&#xff0c;更方便的搭建PHP的运行环境&#xff0c;但是&#xff0c;WAMP的集成环境仅仅是将底层基础工作做好了&#xff0c;有些个别关键的配置操作并没有集成到环境安装中&…

Visual Studio UML Use Case Diagram(2)

Use Case Model是捕获用户需求确定系统边界最流行的方法。Use Case Model由两部分组成Use Case Diagram和Use Case Specification&#xff0c;对于不方便描述的部分可以放在Supplementary Specification中&#xff0c;通过Glossary统一大家的用词规范。昨天我们介绍了Visual St…

Delphi下利用WinIo模拟鼠标键盘详解

本文最早在编程论坛上发表&#xff0c;文章地址&#xff1a;http://programbbs.com/bbs/view12-17207-1.htm&#xff0c;相关文件可以在上述地址的页面中下载。转载时请注明出处。 前言 一日发现SendInput对某程序居然无效&#xff0c;无奈只好开始研究WinIo。上网查了很多资料…

在vs2005中使用Jmail发送邮件问题

jmail.Message Jmail new jmail.Message(); DateTime t DateTime.Now; String Subject " From EMail .net"; String body "你好科学12:15"; String FromEmail "jsyxo163.com"; String ToEmail…

nginx学习之静态内容篇(五)

1.根目录和索引文件 server {root /www/data;location / {}location /images/ {}location ~ \.(mp3|mp4) {root /www/media;} } root指令能放置的位置是&#xff1a;http&#xff0c;server&#xff0c;location。 上面的意思是&#xff1a;我所有的location定义都是基于根目录…

Modeling System Behavior with Use Case(1)

Modeling System Behavior with Use case 我们分为三个部分进行介绍&#xff0c;主要内容包括&#xff1a;需求简介、Use Case Model&#xff08;Use Case Diagram、Use Case Specification&#xff09;、Supplimentary Specification和Glossary&#xff0c;这部分内容是开发过…

matlab练习程序(高斯牛顿法最优化)

计算步骤如下&#xff1a; 图片来自《视觉slam十四讲》6.2.2节。 下面使用书中的练习yexp(a*x^2b*xc)w这个模型验证一下&#xff0c;其中w为噪声&#xff0c;a、b、c为待解算系数。 代码如下&#xff1a; clear all; close all; clc;a1;b2;c1; %待求解的系数x(0:0…

和Office一起做减肥操

随着微软公司的不断开发&#xff0c;Microsoft Office这款大家熟悉的软件真是越来越好用。可是随着版本的更新&#xff0c;软件的身材却越来越“肥胖”&#xff0c;于是很多朋友总想知道如何给它们“减肥”&#xff1f;今天&#xff0c;我们就说一说如何为Office2003减肥吧&…

codevs——1220 数字三角形(棋盘DP)

时间限制: 1 s空间限制: 128000 KB题目等级 : 黄金 Gold题解题目描述 Description如图所示的数字三角形&#xff0c;从顶部出发&#xff0c;在每一结点可以选择向左走或得向右走&#xff0c;一直走到底层&#xff0c;要求找出一条路径&#xff0c;使路径上的值最大。 输入描述 …

Modeling System Behavior with Use Case(2)

这是Modeling System Behavior with Use Case的第二部分&#xff0c;本图文首先介绍Use Case Model&#xff0c;然后介绍Actor以及Actor之间的关系&#xff0c;Use Case以及Use Case之间的关系&#xff0c;最后介绍Actor与Use Case之间的关系。

【Python】keras卷积神经网络识别mnist

卷积神经网络的结构我随意设了一个。 结构大概是下面这个样子&#xff1a; 代码如下&#xff1a; import numpy as np from keras.preprocessing import image from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Activation from keras.…

IIS安全机制基础

IIS (Internet Information Server)作为当今流行的Web服务器之一&#xff0c;提供了强大的Internet和Intranet服务功能。如何加强IIS的安全机制&#xff0c;建立高安全性能的可靠的Web服务器&#xff0c;已成为网络管理的重要组成部分。 以Windows NT的安全机制为基础 …

CLion 2017 注册码

注册码使用时间2017-2018 CNEKJPQZEX-eyJsaWNlbnNlSWQiOiJDTkVLSlBRWkVYIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb…

Modeling System Behavior with Use Case(3)

这是Modeling System Behavior with Use Case的最后一个部分&#xff0c;主要介绍Use Case Specification、Supplementary Specification和Glossary。今天华电时断时续停电&#xff0c;导致早晨所做的工作丢失&#xff0c;很是难过&#xff0c;发完这个图文&#xff0c;我继续代…

2019最新版本的PanDownload纯净版,网盘满速下载和搜索神器,追剧和动漫新番必不可少的下载工具【亲测有效】

PanDownload是百度网盘的第三方下载神器&#xff0c;它支持快速搜索功能&#xff0c;快速找到你想要的东西&#xff0c;还支持满速下载&#xff0c;可谓是不可多得的下载神器。 下载地址&#xff1a;http://t.cn/EobUOTS 城通网盘下载地址&#xff1a;https://u20150046.ctfi…

codevs——2894 Txx考试(背包)

时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题解题目描述 DescriptionTxx是一个成绩很差的人&#xff0c;考试便成了他的噩梦。于是他常在考试时睡觉以打发时间。今天他又要面临一次考试&#xff0c;为了保证有充足的睡眠&#xff0c;他决定只做k分钟题目。这次测…

利用Use Case为系统行为建模(1)

需求这块写了很多了&#xff0c;就剩下最后的三篇图文了。后面我们进入分析与设计部分。大概10月中旬&#xff0c;我们结束这块。开始机器学习的总结与代码实现。