首頁 web前端 js教程 javascript 浏览器兼容性事件处理机制

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

Jun 01, 2016 am 09:54 AM
javascript 相容性 瀏覽器

使用类库可以比较容易的解决兼容性问题.但这背后的机理又是如何呢? 下面我们就一点点铺开来讲.

首先,DOM Level2为事件处理定义了两个函数addEventListener和removeEventListener, 这两个函数都来自于EventTarget接口. 

<code class="javascript">element.addEventListener(eventName, listener, useCapture); 
element.removeEventListener(eventName, listener, useCapture); </code>
登入後複製

EventTarget接口通常实现自Node或Window接口.也就是所谓的DOM元素. 

那么比如window也就可以通过addEventListener来添加监听. 

<code class="javascript">function loadHandler() { 
console.log('the page is loaded!'); 
} 
window.addEventListener('load', loadHandler, false); </code>
登入後複製

移除监听通过removeEventListener同样很容易做到, 只要注意移除的句柄和添加的句柄引用自一个函数就可以了. 

<code class="javascript">window.removeEventListener('load', loadHandler, false); </code>
登入後複製

如果我们活在完美世界.那么估计事件函数就此结束了. 

但情况并非如此.由于IE独树一帜.通过MSDHTML DOM定义了attachEvent和detachEvent两个函数取代了addEventListener和removeEventListener. 

恰恰函数间又存在着很多的差异性,使整个事件机制变得异常复杂. 

所以我们要做的事情其实就转移成了.处理IE浏览器和w3c标准之间对于事件处理的差异性. 

在IE下添加监听和移除监听可以这样写 

<code class="javascript">function loadHandler() { 
alert('the page is loaded!'); 
} 
window.attachEvent('onload', loadHandler); // 添加监听 
window.detachEvent('onload', loadHandler); // 移除监听 </code>
登入後複製

从表象看来,我们可以看出IE与w3c的两处差异: 

1. 事件前面多了个"on"前缀. 
2. 去除了useCapture第三个参数. 

其实真正的差异远远不止这些.等我们后面会继续分析.那么对于现在这两处差异我们很容易就可以抽象出一个公用的函数 

<code class="javascript">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; 
} 
} </code>
登入後複製

上面函数有两处需要注意一下就是: 

1. 第一个分支最好先测定w3c标准. 因为IE也渐渐向标准靠近. 第二个分支监测IE. 
2. 第三个分支是留给既不支持(add/remove)EventListener也不支持(attach/detach)Event的浏览器. 

性能优化 

对于上面的函数我们是运用"运行时"监测的.也就是每次绑定事件都需要进行分支监测.我们可以将其改为"运行前"就确定兼容函数.而不需要每次监测. 

这样我们就需要用一个DOM元素提前进行探测. 这里我们选用了document.documentElement. 为什么不用document.body呢? 因为document.documentElement在document没有ready的时候就已经存在. 而document.body没ready前是不存在的. 

这样函数就优化成 

<code class="javascript">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; 
}; 
} </code>
登入後複製

这样就避免了每次绑定都需要判断. 

值得一提的是.上面的代码其实也是有两处硬伤. 除了代码量增多外, 还有一点就是使用了硬性编码推测.上面代码我们基本的意思就是断定.如果document.documentElement具备了add/remove方法.那么element就一定具备(虽然大多数情况如此).但这显然是不够安全. 

不安全的检测 

下面两个例子说明.在某些情况下这种检测不是足够安全的. 

<code class="javascript">// In Internet Explorer 
var xhr = new ActiveXObject('Microsoft.XMLHTTP'); 
if (xhr.open) { } // Error 
var element = document.createElement('p'); 
if (element.offsetParent) { } // Error </code>
登入後複製

如: 在IE7下 typeof xhr.open === 'unknown'. 详细可参考feature-detection 

所以我们提倡的检测方式是 

<code class="javascript">var isHostMethod = function (object, methodName) { 
var t = typeof object[methodName]; 
return ((t === 'function' || t === 'object') && !!object[methodName]) || t === 'unknown'; 
}; </code>
登入後複製

这样我们上面的优化函数.再次改进成这样

<code class="javascript">var addListener, docEl = document.documentElement; 
if (isHostMethod(docEl, 'addEventListener')) { 
/* ... */ 
} 
else if (isHostMethod(docEl, 'attachEvent')) { 
/* ... */ 
} 
else { 
/* ... */ 
} </code>
登入後複製

丢失的this指针 

this指针的处理.IE与w3c又出现了差异.在w3c下函数的指针是指向绑定该句柄的DOM元素. 而IE下却总是指向window. 

<code class="javascript">// 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 
}); </code>
登入後複製

这个问题修正起来也不算麻烦 

<code class="javascript">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 { 
/* ... */ 
} </code>
登入後複製

我们只需要用一个包装函数.然后在内部将handler用call重新修正指针.其实大伙应该也看出了,这里还偷偷的修正了一个问题就是.IE下event不是通过第一个函数传递,而是遗留在全局.所以我们经常会写event = event || window.event这样的代码. 这里也一并做了修正. 

修正了这几个主要的问题.我们这个函数看起来似乎健壮了很多.我们可以暂停一下做下简单的测试, 测试三点

1. 各浏览器兼容 2. this指针指向兼容 3. event参数传递兼容. 

测试代码如下:

<code class="javascript">

<title> Event Test UseCase </title> 
<meta http-equiv="Content-Type" content="text/html; charset=gb2312"> 
 
 
<div id="odiv" style="width:200px;height:100px;background-color:red; text-align:center">娴</div></code>
登入後複製
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1318
25
PHP教程
1268
29
C# 教程
1248
24
歐易交易所如何註冊賬戶 歐易交易所註冊教程 歐易交易所如何註冊賬戶 歐易交易所註冊教程 Apr 24, 2025 pm 02:06 PM

註冊歐易賬戶的步驟如下:1.準備有效郵箱或手機號和穩定網絡。 2.訪問歐易官網。 3.進入註冊頁面。 4.選擇郵箱或手機號註冊,填寫信息。 5.獲取並填寫驗證碼。 6.同意用戶協議。 7.完成註冊並登錄,進行KYC和設置安全措施。

歐易交易所app官網下載蘋果手機下載 歐易交易所app官網下載蘋果手機下載 Apr 28, 2025 pm 06:57 PM

歐易交易所app支持蘋果手機下載,訪問官網,點擊“蘋果手機”選項,在App Store中獲取並安裝,註冊或登錄後即可進行加密貨幣交易。

幣安下載鏈接 幣安下載路徑 幣安下載鏈接 幣安下載路徑 Apr 24, 2025 pm 02:12 PM

安全下載幣安APP需通過官方渠道:1. 訪問幣安官網,2. 找到並點擊APP下載入口,3. 選擇掃描二維碼、應用商店或直接下載APK文件的方式下載,確保鏈接和開發者信息真實,開啟雙重驗證保護賬戶安全。

芝麻開門官網入口 芝麻開門官方最新入口2025 芝麻開門官網入口 芝麻開門官方最新入口2025 Apr 28, 2025 pm 07:51 PM

芝麻開門是重點加密貨幣交易的平台,用戶可以通過官方網站或社交媒體獲取入口,確保訪問時驗證SSL證書和網站內容的真實性。

芝麻開門交易所如何註冊賬戶 芝麻開門交易所註冊教程 芝麻開門交易所如何註冊賬戶 芝麻開門交易所註冊教程 Apr 24, 2025 pm 02:00 PM

註冊芝麻開門賬號需7步:1.準備有效郵箱或手機號及穩定網絡;2.訪問官網;3.進入註冊頁面;4.選擇並填寫註冊方式;5.獲取並填寫驗證碼;6.同意用戶協議;7.完成註冊並登錄,建議進行KYC和設置安全措施。

歐易官網入口 歐易官方最新入口2025 歐易官網入口 歐易官方最新入口2025 Apr 28, 2025 pm 07:48 PM

選擇可靠的交易平台如歐易(OKEx),確保訪問官方入口。

幣安官網入口 幣安官方最新入口2025 幣安官網入口 幣安官方最新入口2025 Apr 28, 2025 pm 07:54 PM

訪問幣安官網,並查看HTTPS和綠鎖標誌,避免釣魚網站,官方應用也可安全訪問。

什麼是鏈上交易?全球鏈上交易所有哪些? 什麼是鏈上交易?全球鏈上交易所有哪些? Apr 22, 2025 am 10:06 AM

歐盟MiCA合規認證,覆蓋50 法幣通道,冷存儲比例95%,零安全事件記錄。美國SEC持牌平台,法幣直購便捷,冷存儲比例98%,機構級流動性,支持大額OTC和自定義訂單,多級清算保護。

See all articles