通用事件检测

事件检查测量试验,即检查测量试验某一事件在分化的浏览器中是不是留存(可用),那在编写制定Javascript的进程中也十分重大,如mouseenter/mouseleave事件尽管实用,但实际不是有所浏览器都提供了行业内部的匡助,由此供给本身手动模拟,即:

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

本文就首要呈报以上代码中has伊芙nt的实际达成。

着力方案

有关事件的最基本检查实验方法,则需求从事件的登记格局先河说。

事件司空眼惯有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关键字展开删除的,由此对has伊夫nt函数附加以下的逻辑就足以更安全地认清:

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

逻辑很简短,尝试把__proto__中有极大可能率增大上去的删了再试一试,当然别忘了再把本来的值变回去。

Firefox开始BUG

很缺憾,前文提供的has伊夫nt函数并不可能在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)上,再去获得并认清是还是不是获得了三个函数对象就能够。

就此has伊夫nt函数在前文提供的方法重返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事件。由此补充has伊芙nt函数,将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); 
}

迄今,三个相比完整的has伊芙nt函数完毕了,即便在Firefox上还存在部分主题素材,举个例子以下的代码:

alert(hasEvent('unload', document.createElement('div')); //Firefox弹出true

而是在99%的选择场所之下,那么些函数是足以正确的行事的。

累加缓存

为了进一步提升hasEvent的工效,思念到DOM规范规定的平地风波数量相当的少,能够对通用的平地风波(即不钦定检查评定的因素对象)检查测量检验增多缓存机制。

增加了缓存之后,最后完全的has伊芙nt函数如下:

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树结构发生变化时接触,能够在这里看看实际的事件列表。

不满的是has伊夫nt函数不可能检测到Mutation 伊芙nt,由此对此此类事件,要求另一种比较复杂的平地风波检查测验方案。

从Mutation Event的列表中能够发现,此类事件的特性在于当DOM树结构产生变化时才会被触发,因而得以动用下边那套逻辑去检查测量检验:

  1. 预加防范叁个标识位,默以为false。
  2. 创造出贰个DOM树结构。
  3. 注册三个Mutation 伊夫nt。
  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都会绸缪利用那几个事件。

随意has伊芙nt函数如故hasMutation伊芙nt函数都不能够检查评定到这几个事件,然则问题异常的小,因为:

  1. 那件事件和onload同样,页面包车型大巴生命周期中只会触发贰次,不会频仍使用。
  2. 有着帮忙addEventListener的浏览器都帮助那些事件(包括IE9),因而确定轻便。

于是这些事件被排除在了本文切磋范围之外,具体的能够查看各框架的document.ready函数的实现格局。

连带能源

  • Detecting event support without browser sniffing为本文提供了多量的思绪。
  • Diego Perini's NWMatcher提供了Mutation Event质量评定的思路。
  • 点此查看hasEvent和hasMutationEvent的源码。

哪位无聊就把持有的Mutation 伊夫nt的检查实验函数写出来吗……

你也许感兴趣的篇章:

  • 前述浏览器天性检验(1)-jQuery1.4加多部分

本文由华夏彩票发布于关于计算机,转载请注明出处:通用事件检测

您可能还会对下面的文章感兴趣: