您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

javascript 浏览器兼容性事件处理机制

2026/1/12 17:32:53发布10次查看
使用类库可以比较容易的解决兼容性问题.但这背后的机理又是如何呢? 下面我们就一点点铺开来讲.
首先,dom level2为事件处理定义了两个函数addeventlistener和removeeventlistener, 这两个函数都来自于eventtarget接口. 
element.addeventlistener(eventname, listener, usecapture); element.removeeventlistener(eventname, listener, usecapture);
eventtarget接口通常实现自node或window接口.也就是所谓的dom元素. 
那么比如window也就可以通过addeventlistener来添加监听. 
function loadhandler() { console.log('the page is loaded!'); } window.addeventlistener('load', loadhandler, false);
移除监听通过removeeventlistener同样很容易做到, 只要注意移除的句柄和添加的句柄引用自一个函数就可以了. 
window.removeeventlistener('load', loadhandler, false);
如果我们活在完美世界.那么估计事件函数就此结束了. 
但情况并非如此.由于ie独树一帜.通过msdhtml dom定义了attachevent和detachevent两个函数取代了addeventlistener和removeeventlistener. 
恰恰函数间又存在着很多的差异性,使整个事件机制变得异常复杂. 
所以我们要做的事情其实就转移成了.处理ie浏览器和w3c标准之间对于事件处理的差异性. 
在ie下添加监听和移除监听可以这样写 
function loadhandler() { alert('the page is loaded!'); } window.attachevent('onload', loadhandler); // 添加监听 window.detachevent('onload', loadhandler); // 移除监听
从表象看来,我们可以看出ie与w3c的两处差异: 
1. 事件前面多了个on前缀. 
2. 去除了usecapture第三个参数. 
其实真正的差异远远不止这些.等我们后面会继续分析.那么对于现在这两处差异我们很容易就可以抽象出一个公用的函数 
function addlistener(element, eventname, handler) { if (element.addeventlistener) { element.addeventlistener(eventname, handler, false); } else if (element.attachevent) { element.attachevent('on' + eventname, handler); } else { element['on' + eventname] = handler; } } function removelistener(element, eventname, handler) { if (element.addeventlistener) { element.removeeventlistener(eventname, handler, false); } else if (element.detachevent) { element.detachevent('on' + eventname, handler); } else { element['on' + eventname] = null; } }
上面函数有两处需要注意一下就是: 
1. 第一个分支最好先测定w3c标准. 因为ie也渐渐向标准靠近. 第二个分支监测ie. 
2. 第三个分支是留给既不支持(add/remove)eventlistener也不支持(attach/detach)event的浏览器.
性能优化 
对于上面的函数我们是运用运行时监测的.也就是每次绑定事件都需要进行分支监测.我们可以将其改为运行前就确定兼容函数.而不需要每次监测. 
这样我们就需要用一个dom元素提前进行探测. 这里我们选用了document.documentelement. 为什么不用document.body呢? 因为document.documentelement在document没有ready的时候就已经存在. 而document.body没ready前是不存在的. 
这样函数就优化成 
var addlistener, removelistener, /* test element */ docel = document.documentelement; // addlistener if (docel.addeventlistener) { /* if `addeventlistener` exists on test element, define function to use `addeventlistener` */ addlistener = function (element, eventname, handler) { element.addeventlistener(eventname, handler, false); }; } else if (docel.attachevent) { /* if `attachevent` exists on test element, define function to use `attachevent` */ addlistener = function (element, eventname, handler) { element.attachevent('on' + eventname, handler); }; } else { /* if neither methods exists on test element, define function to fallback strategy */ addlistener = function (element, eventname, handler) { element['on' + eventname] = handler; }; } // removelistener if (docel.removeeventlistener) { removelistener = function (element, eventname, handler) { element.removeeventlistener(eventname, handler, false); }; } else if (docel.detachevent) { removelistener = function (element, eventname, handler) { element.detachevent('on' + eventname, handler); }; } else { removelistener = function (element, eventname, handler) { element['on' + eventname] = null; }; }
这样就避免了每次绑定都需要判断. 
值得一提的是.上面的代码其实也是有两处硬伤. 除了代码量增多外, 还有一点就是使用了硬性编码推测.上面代码我们基本的意思就是断定.如果document.documentelement具备了add/remove方法.那么element就一定具备(虽然大多数情况如此).但这显然是不够安全. 
不安全的检测 
下面两个例子说明.在某些情况下这种检测不是足够安全的. 
// in internet explorer var xhr = new activexobject('microsoft.xmlhttp'); if (xhr.open) { } // error var element = document.createelement('p'); if (element.offsetparent) { } // error
如: 在ie7下 typeof xhr.open === 'unknown'. 详细可参考feature-detection 
所以我们提倡的检测方式是 
var ishostmethod = function (object, methodname) { var t = typeof object[methodname]; return ((t === 'function' || t === 'object') && !!object[methodname]) || t === 'unknown'; };
这样我们上面的优化函数.再次改进成这样
var addlistener, docel = document.documentelement; if (ishostmethod(docel, 'addeventlistener')) { /* ... */ } else if (ishostmethod(docel, 'attachevent')) { /* ... */ } else { /* ... */ }
丢失的this指针 
this指针的处理.ie与w3c又出现了差异.在w3c下函数的指针是指向绑定该句柄的dom元素. 而ie下却总是指向window. 
// ie document.body.attachevent('onclick', function () { alert(this === window); // true alert(this === document.body); // false }); // w3c document.body.addeventlistener('onclick', function () { alert(this === window); // false alert(this === document.body); // true });
这个问题修正起来也不算麻烦 
if (ishostmethod(docel, 'addeventlistener')) { /* ... */ } else if (ishostmethod(docel, 'attachevent')) { addlistener = function (element, eventname, handler) { element.attachevent('on' + eventname, function () { handler.call(element, window.event); }); }; } else { /* ... */ }
我们只需要用一个包装函数.然后在内部将handler用call重新修正指针.其实大伙应该也看出了,这里还偷偷的修正了一个问题就是.ie下event不是通过第一个函数传递,而是遗留在全局.所以我们经常会写event = event || window.event这样的代码. 这里也一并做了修正. 
修正了这几个主要的问题.我们这个函数看起来似乎健壮了很多.我们可以暂停一下做下简单的测试, 测试三点
1. 各浏览器兼容 2. this指针指向兼容 3. event参数传递兼容. 
测试代码如下:
event test usecase 娴

该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product