Home Web Front-end JS Tutorial javascript 浏览器兼容性事件处理机制

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

Jun 01, 2016 am 09:54 AM
javascript compatibility Browser

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

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

<code class="javascript">element.addEventListener(eventName, listener, useCapture); 
element.removeEventListener(eventName, listener, useCapture); </code>
Copy after login

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

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

<code class="javascript">function loadHandler() { 
console.log('the page is loaded!'); 
} 
window.addEventListener('load', loadHandler, false); </code>
Copy after login

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

<code class="javascript">window.removeEventListener('load', loadHandler, false); </code>
Copy after login

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

但情况并非如此.由于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>
Copy after login

从表象看来,我们可以看出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>
Copy after login

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

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>
Copy after login

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

值得一提的是.上面的代码其实也是有两处硬伤. 除了代码量增多外, 还有一点就是使用了硬性编码推测.上面代码我们基本的意思就是断定.如果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>
Copy after login

如: 在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>
Copy after login

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

<code class="javascript">var addListener, docEl = document.documentElement; 
if (isHostMethod(docEl, 'addEventListener')) { 
/* ... */ 
} 
else if (isHostMethod(docEl, 'attachEvent')) { 
/* ... */ 
} 
else { 
/* ... */ 
} </code>
Copy after login

丢失的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>
Copy after login

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

<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>
Copy after login

我们只需要用一个包装函数.然后在内部将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>
Copy after login
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Solve caching issues in Craft CMS: Using wiejeben/craft-laravel-mix plug-in Solve caching issues in Craft CMS: Using wiejeben/craft-laravel-mix plug-in Apr 18, 2025 am 09:24 AM

When developing websites using CraftCMS, you often encounter resource file caching problems, especially when you frequently update CSS and JavaScript files, old versions of files may still be cached by the browser, causing users to not see the latest changes in time. This problem not only affects the user experience, but also increases the difficulty of development and debugging. Recently, I encountered similar troubles in my project, and after some exploration, I found the plugin wiejeben/craft-laravel-mix, which perfectly solved my caching problem.

Tips for using HDFS file system on CentOS Tips for using HDFS file system on CentOS Apr 14, 2025 pm 07:30 PM

The Installation, Configuration and Optimization Guide for HDFS File System under CentOS System This article will guide you how to install, configure and optimize Hadoop Distributed File System (HDFS) on CentOS System. HDFS installation and configuration Java environment installation: First, make sure that the appropriate Java environment is installed. Edit /etc/profile file, add the following, and replace /usr/lib/java-1.8.0/jdk1.8.0_144 with your actual Java installation path: exportJAVA_HOME=/usr/lib/java-1.8.0/jdk1.8.0_144exportPATH=$J

How to optimize website performance: Experiences and lessons learned from using the Minify library How to optimize website performance: Experiences and lessons learned from using the Minify library Apr 17, 2025 pm 11:18 PM

In the process of developing a website, improving page loading has always been one of my top priorities. Once, I tried using the Miniify library to compress and merge CSS and JavaScript files in order to improve the performance of the website. However, I encountered many problems and challenges during use, which eventually made me realize that Miniify may no longer be the best choice. Below I will share my experience and how to install and use Minify through Composer.

How to monitor HDFS status on CentOS How to monitor HDFS status on CentOS Apr 14, 2025 pm 07:33 PM

There are many ways to monitor the status of HDFS (Hadoop Distributed File System) on CentOS systems. This article will introduce several commonly used methods to help you choose the most suitable solution. 1. Use Hadoop’s own WebUI, Hadoop’s own Web interface to provide cluster status monitoring function. Steps: Make sure the Hadoop cluster is up and running. Access the WebUI: Enter http://:50070 (Hadoop2.x) or http://:9870 (Hadoop3.x) in your browser. The default username and password are usually hdfs/hdfs. 2. Command line tool monitoring Hadoop provides a series of command line tools to facilitate monitoring

How to solve the error in CentOS HDFS configuration How to solve the error in CentOS HDFS configuration Apr 14, 2025 pm 07:06 PM

Troubleshooting HDFS configuration errors under CentOS Systems This article is intended to help you solve problems encountered when configuring HDFS in CentOS systems. Please follow the following steps to troubleshoot: Java environment verification: Confirm that the JAVA_HOME environment variable is set correctly. Add the following in the /etc/profile or ~/.bashrc file: exportJAVA_HOME=/path/to/your/javaexportPATH=$JAVA_HOME/bin: $PATHExecute source/etc/profile or source~/.bashrc to make the configuration take effect. Hadoop

Use Composer to solve browser sniffing: The practical application of WhichBrowser/Parser library Use Composer to solve browser sniffing: The practical application of WhichBrowser/Parser library Apr 17, 2025 pm 11:21 PM

I encountered a tricky problem when developing a multi-device-compatible website: how to accurately identify the user's browser and device information. After trying multiple methods, I found that directly parsing user-agent strings (User-Agent) are both complex and unreliable, and often misjudgments occur. Fortunately, I successfully solved this problem by installing the WhichBrowser/Parser library using Composer.

What is the reason why the browser does not respond after the WebSocket server returns 401? How to solve it? What is the reason why the browser does not respond after the WebSocket server returns 401? How to solve it? Apr 19, 2025 pm 02:21 PM

The browser's unresponsive method after the WebSocket server returns 401. When using Netty to develop a WebSocket server, you often encounter the need to verify the token. �...

Why can't JavaScript directly obtain hardware information on the user's computer? Why can't JavaScript directly obtain hardware information on the user's computer? Apr 19, 2025 pm 08:15 PM

Discussion on the reasons why JavaScript cannot obtain user computer hardware information In daily programming, many developers will be curious about why JavaScript cannot be directly obtained...

See all articles