前言:原本这篇文章是打算6号书写出来的,但是基于某些私人原因,希望能够通过这篇文章尽可能的将事件讲解的更加详细和通俗易懂,因此,多花了一天时间,不多说了,接下来对“事件”来一个较为详细的介绍。
欢迎大家互相学习交流。独行冰海
需要了解事件的什么?
对于事件来讲,首先,我们需要了解这样几个概念:事件;事件处理程序;事件类型;事件流;事件冒泡;事件捕获;事件对象;事件方面的性能优化(事件委托、移除事件处理程序);常见的浏览器兼容问题。
事件的概念
事件:指的是文档或者浏览器窗口中发生的一些特定交互瞬间。我们可以通过侦听器(或者处理程序)来预定事件,以便事件发生的时候执行相应的代码。
事件处理程序
事件处理程序:我们用户在页面中进行的点击这个动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,即:click、mousemove、load等都是事件的名称。响应某个事件的函数则称为事件处理程序,或者叫做事件侦听器。
接下来我们用一段代码来再说明一下上面这两个抽象的概念,具体解释见代码注释:
事件-独行冰海
理解事件的基本概念
<script><br/> var main = document.getElementById('main');<br/> // 此處click,點擊,是一種事件的名稱,是瀏覽器視窗中發生點擊的瞬間。 on這個單字,其實就是讓click這個事件得以相應,因此,onclick就叫做事件處理程序。下面這段程式碼,我們透過處理程序(onclick)為main這個元素預定了點擊(click),這樣在點擊(click)發生的時候,執行函數中的程式碼(彈出一個對話框)。 <br/> main.onclick = function(){<br/> alert('點擊了此處哦!');<br/> }<br/></script>
}
關於事件>
關於事件處理到現在之前的發展到次的變化。
事件處理程序的名字是以「on」開頭,因此click事件的處理程序就是onclick。在最初,是使用HTML事件處理程序的,也就是說,某個元素(如div),支援的每一種事件,都可以使用一個與對應事件處理程序同名的HTML特性來制定(也就是標籤的一個屬性),這個特性的值就是能夠執行的JavaScript程式碼。例如:
事件-獨行冰海
HTML事件處理程序
當然,我們也可以再onclick=」」當中進行函數的呼叫。
但是,無論哪種方法,都暴露出來HTML事件處理程序的許多問題:
首先,HTML程式碼域JavaScript程式碼緊密的耦合在一起,沒有實現相互分離,在進行程式碼的更新與維護的時候就顯得異常困難。
第二,擴充事件處理程序的作用域鏈在不同瀏覽器當中會導致不同的結果。
第三,如果不採用呼叫函數的方式,而是像例子中那樣直接書寫程式碼,那麼程式碼的通用性很差,會使得整站的程式碼量很大,通用性差。如果提取出來,存放在函數當中,那麼,會面臨另一個問題——當函數還沒有被定義,只是HTML、CSS程式碼載入完畢,用戶進行點擊,會完全沒有反應。
基於上述的這些問題,人們逐漸的去完善了事件處理程序,此時出現了DOM0級事件處理程序。 (第四代WEB瀏覽器中出現)
DOM0級事件處理程序是什麼樣子的呢?其實它就是我們平常最常用的事件綁定,如下面這個例子:
事件獨行冰-獨行冰海
DOM0級事件處理程序
<script><br/> var btn = document.<br/><script>🎜 var btn = document.getElementById(' btn');🎜 btn.onclick = function(){🎜 alert(this.innerHTML);🎜 }🎜</script>🎜🎜 }🎜🎜🎜 }🎜🎜🎜但是,卻出現了這樣一個問題,當我希望為同一個元素/標籤綁定多個同類型事件的時候(如,為上面的這個p標籤綁定3個點擊事件),是不被允許的。那麼,此時,出現了另一個事件處理程序,就是DOM2級的事件處理程序,在DOM2級當中,定義了兩個基本方法,用於處理指定(即綁定)和刪除事件處理程序的操作,分別是addEventListener()和removeEventListener(),IE9+、FireFox、Safari、Chrome和Opera都是支援DOM2級事件處理程序的。對於IE8-,則使用的是IE專有的事件處理程序:兩個類似的方法-attachEvent()與detachEvent()。 🎜具體實例程式碼如下:(以addEventListener為例,給出了兩種書寫方式)🎜🎜🎜🎜
🎜
🎜🎜🎜 DOM0級事件處理程序
🎜🎜<script>🎜 var btn = document. getElementById('btn');🎜 btn.addEventListener("click", test, false);🎜 function test(){<br/> alert(this.innerHTML);<br/> }<br/></script>
事件-独行冰海
DOM0级事件处理程序
<script><br/> var btn = document.getElementById('btn');<br/> btn.addEventListener("click", function(){<br/> alert(this.innerHTML); <br/> }, false);<br/></script>
addEventListener()和removeEventListener()中的第三个参数,表示的是在哪个事件阶段进行事件处理,如果是false,则指的是冒泡阶段;如果是true,则指的是捕获阶段。
在事件方面,IE与FF存在着一系列的兼容问题,具体问题可查看博文《IE浏览器与FF火狐浏览器在事件上的兼容问题》
关于如何创建一个兼容全部浏览器的事件侦听器,我们在下一篇博文《跨浏览器的事件处理函数——处理DOM2级事件兼容 》当中再做详细的介绍和代码示范。
事件类型
之前在课程的讲解当中,我们把事件分为了三大类,分别是一般事件、表单事件和页面事件。当前我们可以再做细分:
UI事件:如load、unload、error、resize、scroll、select、DOMActive,是用户与页面上的元素交互时触发的。
焦点事件:如blur、DOMFocusIn、DOMFocusOut、focus、focusin、focusout,在元素获得或失去焦点的时候触发,这些事件当中,最为重要的是blur和focus,有一点需要引起注意,这一类事件不会发生冒泡!
鼠标与滚轮事件:如click、dblclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup,是当用户通过鼠标在页面执行操作时所触发的。
滚轮事件:mousewheel(IE6+均支持)、DOMMouseScroll(FF支持的,与mousewheel效果一样)。是使用鼠标滚轮时触发的。
文本事件:textInput,在文档中输入文本触发。
键盘事件:keydown、keyup、keypress,当用户通过键盘在页面中执行操作时触发。
合成事件:DOM3级新增,用于处理IME的输入序列。所谓IME,指的是输入法编辑器,可以让用户输入在物理键盘上找不到的字符。compositionstart、compositionupdate、compositionend三种事件。
变动事件:DOMsubtreeModified、DOMNodeInserted、DOMNodeRemoved、DOMAttrModified、DOMCharacterDataModified等,当底层DOM结构发生变化时触发。IE8-不支持。
变动名称事件:指的是当元素或者属性名变动时触发,当前已经弃用!
对于事件的基本类型,随着HTML5的出现和发展,又新增了HTML5事件、设备事件、触摸事件、手势事件等各种事件,在后面我们再详细介绍。
事件流
事件流:描述的是从页面中接收事件的顺序。
IE与原来的NetScape(网景),对于事件流提出的是完全不同的顺序。IE团队提出的是事件冒泡流;NetScape的事件流是事件捕获流。
事件冒泡
事件冒泡:表示的是,事件开始的时候由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
事件捕获
事件捕获:表示的是,事件开始的时候由最不具体的节点接收,然后逐级向下传播到最具体的节点。
来看一个实例:
事件-独行冰海
如果单击了p标签,那么,如果是事件冒泡流的事件流机制,则click事件将按照如下顺序进行执行:p —— div —— body —— html —— document。
如果采用捕获流的事件流机制,则click事件的执行顺序为:document —— html —— body —— div —— p
对于冒泡流的事件流机制,存在如下的兼容问题:
div -> body -> document
>=IE6.0 p -> div -> body -> html -> document
>=Mozilla 1.0 p -> div -> body -> html -> document -> window
欢迎大家互相学习交流。独行冰海
事件对象
事件对象:在触发DOM上的某个事件的时候,会产生一个事件对象event,而在这个对象当中会包含着所有与事件有关的信息。我们书写如下基本代码:
事件-独行冰海
理解事件的基本概念
<script><br/> var main = document.getElementById('main');<br/> main.onclick = function(event){<br/> console.log(event);<br/> }<br/></script>
使用console.log打印出的结果如下图。
JavaScript事件 详细讲解 - 独行冰海 - 独行冰海
其中有两个信息,我们最为常用,分别是type和target。
type表示的是被触发事件的类型;
target表示的是事件的目标。
其他信息,如:
bubbles:表示事件是否冒泡
cancelable:表示是否可以取消事件的默认行为
currentTarget:表示事件处理程序当前正在处理事件的那个元素
defaultPrevented:表示是否调用了preventDefault()
detail:表示的是与事件相关的细节信息
eventPhase:调用事件处理处理程序的阶段:1表示捕获阶段、2表示处于目标、3表示冒泡阶段
在其中还有一些其他信息,在此就不再一一列举了。
事件方面性能优化
谈一谈事件方面如何优化性能——事件委托和事件处理程序的移除
在JavaScript代码当中,添加到页面中的事件越多,页面的性能也就越差。导致这一问题的原因主要有:
每个函数都是对象,都会占用内存。内存中对象越多,性能也就越差。
必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
为了进行页面的性能优化,因此我们会采用两种方法,就是上面提到的——事件委托和事件处理程序的移除。
事件委托
很多人问我,什么时候使用事件委托,其实,简单来说,当时一个页面事件处理程序比较多的时候,我们通常情况下会使用它。
事件委托主要利用了事件冒泡,只指定一个事件处理程序,就可以管理一个类型的所有事件。例如:我们为整个一个页面制定一个onclick事件处理程序,此时我们不必为页面中每个可点击的元素单独设置事件处理程序(onclick)。还是,看一个例子。
效果:点击不同的元素执行不同的操作。
不使用事件委托:
事件-独行冰海
左
1
2
3
右
<script><br/> var left = document.getElementById('left');<br/> var first = document.getElementById('first');<br/> var second = document.getElementById('second');<br/> var third = document.getElementById('third');<br/> var right = document.getElementById('right');<br/> left.addEventListener("click", function(){<br/> alert('点击的是左这个字,执行相关操作'); <br/> }, false);<br/> first.addEventListener("click", function(){<br/> alert('要执行第一个序号对应的相关操作'); <br/> }, false);<br/> second.addEventListener("click", function(){<br/> alert('要执行第二个序号对应的相关操作'); <br/> }, false);<br/> third.addEventListener("click", function(){<br/> alert('要执行第三个序号对应的相关操作'); <br/> }, false);<br/> right.addEventListener("click", function(){<br/> alert('点击的是右这个字,执行相关操作'); <br/> }, false);<br/></script>
不难看出,我们使用了5个事件侦听器,每设置一个就需要绑定一个。
使用事件委托:
事件-独行冰海
左
1
2
3
右
<script><br/> var control = document.getElementById('control');<br/> control.addEventList ", function(e){<br/> 請注意器,如果想書寫相容全部瀏覽器的事件處理程序,請查看下一篇博文<br/> var target = e.target;<br/> // id){<br/> case "left" : {<br/> break;<br/> }<br/> case "first" : {的相關操作');<br/> break;<br/> }<br/> case "second" : {<br/> alert('要執行第二個序號對應的相關操作');<br/> break;<br/> }<br/> case "third" : {<br/> alert( '要執行第三序號對應的相關作業');<br/> break;<br/> case "right" : {<br/> alert('點選的是右這個字,為相關操作); }<br/> }<br/> }, false);<br/></script>
簡要的總結所謂的事件委託:給元素的父級或祖級,甚至頁面綁定事件,然後利用事件冒泡的基本原理,透過事件目標物件進行偵測,然後執行相關操作。其優點在於:
大大減少了事件處理程序的數量,在頁面中設定事件處理程序的時間就更少了(DOM引用減少——也就是上面我們透過id去獲取標籤,所需要的查找操作以及DOM引用也就更少了)。
document(註:上面的例子沒有綁定在document上,而是綁定到了父級的div上,最推薦的是綁定在document上)物件可以快速的存取到,而且可以在頁面生命週期的任何時點上為它添加事件處理程序,並不需要等待DOMContentLoaded或load事件。換句話說,只要可點擊的元素在頁面中呈現出來了,那麼它就立刻具備了對應的功能。
整個頁面佔用的記憶體空間會更少,從而提升了整體的效能。
移除事件處理程序
每當將一個事件處理程序指定給一個元素時,在運行中的瀏覽器程式碼與支援頁面互動的JavaScript程式碼之間就會建立一個連結。連線數量也直接影響頁面的執行速度。所以,當記憶體中存在著過時的「空事件處理程序」的時候,就會造成Web應用程式的記憶體和效能問題。
那麼什麼時候會造成「空事件處理程序」的出現呢?
文檔中元素存在事件,透過一些DOM節點操作(removeChild、replaceChild等方法),移除了這個元素,但是DOM節點的事件沒有被移除。
innerHTML去替換頁面中的某一部分,頁面中原來的部分存在事件,沒有移除。
頁面卸載引起的事件處理程序在記憶體中的滯留。
解決方法:
合理利用事件委託;
在執行相關操作的時候,先移除掉事件,再移除DOM節點;
在頁面卸載之前,先透過onunload事件移除所有事件處理程序。