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>

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

Apache服務器是強大的Web服務器軟件,充當瀏覽器與網站服務器間的橋樑。 1. 它處理HTTP請求,根據請求返回網頁內容;2. 模塊化設計允許擴展功能,例如支持SSL加密和動態網頁;3. 配置文件(如虛擬主機配置)需謹慎設置,避免安全漏洞,並需優化性能參數,例如線程數和超時時間,才能構建高性能、安全的Web應用。

在使用CraftCMS開發網站時,常常會遇到資源文件緩存的問題,特別是當你頻繁更新CSS和JavaScript文件時,舊版本的文件可能仍然被瀏覽器緩存,導致用戶無法及時看到最新的更改。這個問題不僅影響用戶體驗,還會增加開發和調試的難度。最近,我在項目中遇到了類似的困擾,經過一番探索,我找到了wiejeben/craft-laravel-mix這個插件,它完美地解決了我的緩存問題。

CentOS系統下HDFS文件系統的安裝、配置及優化指南本文將指導您如何在CentOS系統上安裝、配置和優化Hadoop分佈式文件系統(HDFS)。 HDFS安裝與配置Java環境安裝:首先,確保已安裝合適的Java環境。編輯/etc/profile文件,添加以下內容,並將/usr/lib/java-1.8.0/jdk1.8.0_144替換為您的實際Java安裝路徑:exportJAVA_HOME=/usr/lib/java-1.8.0/jdk1.8.0_144exportPATH=$J

Nginx性能監控與故障排查主要通過以下步驟進行:1.使用nginx-V查看版本信息,並啟用stub_status模塊監控活躍連接數、請求數和緩存命中率;2.利用top命令監控系統資源佔用,iostat和vmstat分別監控磁盤I/O和內存使用情況;3.使用tcpdump抓包分析網絡流量,排查網絡連接問題;4.合理配置worker進程數,避免並發處理能力不足或進程上下文切換開銷過大;5.正確配置Nginx緩存,避免緩存大小設置不當;6.通過分析Nginx日誌,例如使用awk和grep命令或ELK

要查看Tomcat日誌中的線程狀態,您可以採用以下幾種方法:TomcatManagerWeb界面:在瀏覽器中輸入Tomcat的管理地址(通常為http://localhost:8080/manager),登錄後即可查看線程池的狀態。 JMX監控:使用JMX監控工具(如JConsole)連接到Tomcat的MBean服務器,查看Tomcat線程池的狀態。在JConsole中選擇

本文介紹瞭如何分析Nginx日誌以提升網站性能和用戶體驗。 1.理解Nginx日誌格式,例如時間戳、IP地址、狀態碼等;2.使用awk等工具解析日誌,統計訪問量、錯誤率等指標;3.根據需求編寫更複雜的腳本或使用更高級工具,例如goaccess,分析不同維度的數據;4.對於海量日誌,考慮使用Hadoop或Spark等分佈式框架。通過分析日誌,可以識別網站訪問模式、改進內容策略,並最終優化網站性能和用戶體驗。

在CentOS系統上監控HDFS(Hadoop分佈式文件系統)狀態有多種途徑。本文將介紹幾種常用方法,助您選擇最合適的方案。 1.利用Hadoop自帶的WebUIHadoop自帶的Web界面提供集群狀態監控功能。步驟:確保Hadoop集群已啟動並運行。訪問WebUI:在瀏覽器中輸入http://:50070(Hadoop2.x)或http://:9870(Hadoop3.x)。默認用戶名和密碼通常為hdfs/hdfs。 2.命令行工具監控Hadoop提供一系列命令行工具,方便監

在Debian系統上配置HTTPS服務器涉及幾個步驟,包括安裝必要的軟件、生成SSL證書、配置Web服務器(如Apache或Nginx)以使用SSL證書。以下是一個基本的指南,假設你使用的是ApacheWeb服務器。 1.安裝必要的軟件首先,確保你的系統是最新的,並安裝Apache和OpenSSL:sudoaptupdatesudoaptupgradesudoaptinsta
