事件通常與函數配合使用,這樣就可以通過發生的事件來驅動函數執行。
事件是文檔或者瀏覽器窗口中發生的,特定的交互瞬間。
事件是用戶或瀏覽器自身執行的某種動作,如click,load和mouseover都是事件的名字。
事件是javaScript和DOM之間交互的橋梁。
你若觸發,我便執行——事件發生,調用它的處理函數執行相應的JavaScript代碼給出響應。
典型的例子有:頁面加載完畢觸發load事件;用戶單擊元素,觸發click事件。
事件流描述的是從頁面中接收事件的順序。
事件發生時會在元素節點與根節點之間按照特定的順序傳播,路徑所經過的所有節點都會收到該事件,這個傳播過程即DOM事件流。
事件傳播的順序對應瀏覽器的兩種事件流模型:捕獲型事件流和冒泡型事件流。
冒泡型事件流:事件的傳播是從最特定的事件目標到最不特定的事件目標。即從DOM樹的葉子到根。【推薦】
捕獲型事件流:事件的傳播是從最不特定的事件目標到最特定的事件目標。即從DOM樹的根到葉子。
事件捕獲的思想就是不太具體的節點應該更早接收到事件,而最具體的節點最后接收到事件。
<html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id="myDiv">Click me!</div> </body> </html>
上面這段html代碼中,單擊了頁面中的<div>元素,
在冒泡型事件流中click事件傳播順序為<div>—》<body>—》<html>—》document
在捕獲型事件流中click事件傳播順序為document—》<html>—》<body>—》<div>
note:
1)、所有現代瀏覽器都支持事件冒泡,但在具體實現中略有差別:
IE5.5及更早版本中事件冒泡會跳過<html>元素(從body直接跳到document)。
IE9、Firefox、Chrome、和Safari則將事件一直冒泡到window對象。
2)、IE9、Firefox、Chrome、Opera、和Safari都支持事件捕獲。盡管DOM標准要求事件應該從document對象開始傳播,但這些瀏覽器都是從window對象開始捕獲事件的。
3)、由於老版本瀏覽器不支持,很少有人使用事件捕獲。建議使用事件冒泡。
DOM標准采用捕獲+冒泡。兩種事件流都會觸發DOM的所有對象,從document對象開始,也在document對象結束。
DOM標准規定事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。
- 事件捕獲階段:實際目標(<div>)在捕獲階段不會接收事件。也就是在捕獲階段,事件從document到<html>再到<body>就停止了。上圖中為1~3.
- 處於目標階段:事件在<div>上發生並處理。但是事件處理會被看成是冒泡階段的一部分。
- 冒泡階段:事件又傳播回文檔。
note:
1)、盡管“DOM2級事件”標准規范明確規定事件捕獲階段不會涉及事件目標,但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都會在捕獲階段觸發事件對象上的事件。結果,就是有兩次機會在目標對象上面操作事件。
2)、並非所有的事件都會經過冒泡階段 。所有的事件都要經過捕獲階段和處於目標階段,但是有些事件會跳過冒泡階段:如,獲得輸入焦點的focus事件和失去輸入焦點的blur事件。
更多詳情見:http://www.cnblogs.com/starof/p/4066381.html
JavaScript中常用的事件
1.onclick事件 點擊事件(onclick並不是js中的方法,onclick只是瀏覽器提供js的一個dom接口,讓js可以操作dom,所以onclick大小寫都是沒問題的,比如HTML代碼就不用區分大小寫) 例: <%@pagelanguage="Java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionmyFunction(){ alert("測試onclick點擊事件"); } </script> </head> <body> <center> <buttononclick="myFunction()">點擊這里</button> </center> </body> </html> onclick通常在下列基本對象中產生: button(按鈕對象)、checkbox(復選框)、radio(單選框)、reset buttons(重置按鈕)、submit buttons(提交按鈕) 2.onload事件 可以body執行,<bodyonload="alert(123)"></body>,其中onload后面可以寫一個方法,如:onload="test()",然后在JavaScript中寫一個test()方法,則在頁面一開始加載的時候會先調用這個方法 <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functiontest(){ alert("測試onload方法"); } </script> </head> <bodyonload="test()"> </body> </html> 注意:此方法只能寫在<body>標簽之中 3.onchange事件 事件在內容改變的時候觸發,和jQuery中的change()方法一樣 當內容改變時觸發。可用於文本框、列表框等對象,該事件一般用於響應用戶修改內容帶來的其他改變操作。 說明:當用戶向一個文本框中輸入文本時,不會觸發onchange事件,只有用戶輸入結束后,單擊文本框以外的區域,使文本框失去焦點時才觸發該事件,如果是下拉框,則選擇結束后即觸發。 <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionupperCase(){ varx = document.getElementById("fname").value; document.getElementById("fname").value = x.toUpperCase(); } </script> </head> <body> <p> <labelfor="name">用戶名:</label> <inputtype="text"id="fname"onchange="upperCase()"value=""/> </p> </body> </html> 說明:上例實際效果是,當輸入框失去焦點時內容轉成大寫。出現這種情況是由於input必須是失去焦點才會檢測到內容發生改變。而change事件通常是用於下拉菜單select標簽。 4.onblur事件和onfocus事件 當前元素失去焦點時觸發該事件,對應的是onfocus事件:得到焦點事件 <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionchkvalue(txt) { if(txt.value=="") alert("文本框里必須填寫內容!"); } functionsetStyle(x){ document.getElementById(x).style.background="yellow" } </script> </head> <body> 失去焦點: <inputtype="text"name="name"value=""size="30"onblur="chkvalue(this)"><br> 得到焦點: <inputtype="text"id="name"value=""size="30"onfocus="setStyle(this.id)"> </body> </html> 5.onscroll事件 窗口滾動事件:當頁面滾動時調用函數。此事件寫在方法的外面,且函數名后面不加括號,例如window.onscroll=move 例: <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionmove() { alert("頁面滾動時調用"); } window.onscroll = move; </script> </head> <body> 測試onscroll方法<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> 測試onscroll方法<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> 測試onscroll方法<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> 測試onscroll方法<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR> </body> </html> 6.onsubmit事件 屬於<form>表單元素,寫在<form>表單標簽內。語法:onsubmit=”return 函數名()” 例: <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionmove() { alert("測試onsubmit........"+testForm.name.value); } </script> </head> <body> <formaction=""method="post"name="testForm"onsubmit="returnmove()"> <inputtype="text"name="name"value=""> <br> <inputtype="submit"name="submit"value="測試onsubmit"/> </form> </body> </html> 2.鼠標相關事件 1.onmousemove和onmouseout和onmouseover事件 Onmouseover:鼠標移動到某對象范圍的上方時,觸發事件調用函數。注意:在同一個區域中,無論怎樣移動都只觸發一次函數。 Onmouseout:鼠標離開某對象范圍時,觸發事件調用函數。 Onmousemove:鼠標移動到某對象范圍的上方時,觸發事件調用函數。注意:在同一個區域中,只要鼠標動一次就觸發一次事件。 例: <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionbigImg(x) { x.style.height="180px"; x.style.width="180px"; } functionnormalImg(x) { x.style.height="128px"; x.style.width="128px"; } </script> </head> <body> <imgonmousemove="bigImg(this)"onmouseout="normalImg(this)"border="0"src="images/defaultAvatar.gif"alt="Smiley"> </body> </html> 2.onmouseup和onmousedown Onmouseup:當鼠標松開時觸發事件 Onmousedown:當鼠標按下鍵時觸發事件 例: <%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%> <!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JavaScript的一些常用方法</title> <scripttype="text/javascript"> functionmouseDown(){ document.getElementById("p1").style.color="red"; } functionmouseUp(){ document.getElementById("p1").style.color="green"; } </script> </head> <body> <pid="p1"onmousedown="mouseDown()"onmouseup="mouseUp()"> 請點擊文本!mouseDown()函數當鼠標按鈕在段落上被按下時觸發。此函數把文本顏色設置為紅色。mouseUp() 函數在鼠標按鈕被釋放時觸發。mouseUp() 函數把文本的顏色設置為綠色。 </p> </body> </html>
常用的事件主要有:
(1)單擊事件:onclick。用戶單擊鼠標按鍵時產生的事件,同時。nclick指定的事件處理程序或代碼將被調用執行.
(2)改變事件:onchange。當text或textarea元素內的字符值改變或select表格選項狀態改變時發生該事件。
(3)選中事件:onselect。當text或textarea對象中的文字被選中時會引發該事件。如:
<ipnut type="text" value="默認信息”onselect=alert(”您選中T文本框中的文字”)>
(4)獲得焦點事件:onfocus。用戶單擊text或textarea以及select對象,即光標落在文本框或選擇框時會產生該事件。如:
<select name= "zhengjian" onfocus=alert(”我成為焦點”)>
(5)失去焦點事件:onblur.失去焦點事件正好與獲得焦點事件相對,當text或textarea以及select對象不再擁有焦點而退出后台時,引發該事件。
(6)載人文件事件:onload,’當頁面文件載人時產生該事件。onload的一個作用就是在首次載人一個頁面文件時檢測cookie的值,並用一個變量為其賦值,使它可以被源代碼使用,本事件是window的事件,但是在HTML中指定事件處理程序時,一般把它寫在<body>標記中。如:
<body onload=alert(”正在加載頁面,請等待一”)>
(7)卸載文件事件:onunload。與載人文件事件。nload正好相反,當Web頁面退出時引發的事件,並可更新。ookie的狀態。如:
<body onunload=confirm(”你確定要離開本頁?”)>
(8)鼠標鎮蓋事件:onmouseover, onmouseover是當鼠標位於元素上方時所引發的事件。如:
<input type= "boutton" value=”按鈕”onmouseover= "window. status=‘請您注意下面的狀態欄·;return true">
(9)鼠標離開事件:onmouseout, onmouseout是當鼠標離開元素時引發的事件。如果和鼠標覆蓋事件同時使用,可以創建動態按鈕的效果。
(10)一般事件。
ondbclick:鼠標雙擊事件。
onkeypress:當鍵盤上的某個鍵被按下並且釋放時觸發的事件,要求頁面內必須有激活的對象。
onkeydown:當鍵盤上某個鍵被按下時觸發的事件,要求頁面內必須有激活的對象。
onkeyup:當鍵盤上某個鍵被放開時觸發的事件,要求頁面內必須有激活的對象。
(11)頁面相關事件。
onabort:圖片在下載時被用戶中斷時觸發的事件。
onbeforeunload:當前頁面的內容將要被改變時觸發的事件。
DOM事件流
- “DOM2級事件”規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的是事件捕獲,為截獲事件提供了機會。然后是實際的目標接收到事件。最后一個階段是冒泡階段。(事件處理中“處於目標階段”被看成冒泡階段的一部分)。
- IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都會在捕獲階段觸發事件對象上的事件,就是有兩個機會在目標對象上面操作事件。(盡管DOM2級事件規范明確要求捕獲階段不涉及事件目標)。
事件處理程序
HTML 事件處理程序
簡單來講,HTML 事件處理程序是直接在HTML中綁定事件,如下
<input type="button" value="Click Me" onclick="alert("Clicked")" />
注意事項:
- 不能在其中使用未經轉義的HTML語法字符,如
&
、“”
、<
、>
,因為這是在HTML中綁定的,會造成瀏覽器解析DOM結構錯誤。 -
擴展函數作用域,來看下面的代碼:
<!-- 輸出 "Click Me、lzh" --> <form method="post"> <input type="text" name="username" value="lzh"> <input type="button" value="Click Me" onclick="alert(value);alert(username.value);"> </form>
如果當前元素是一個表單輸入元素,瀏覽器內部大概是這樣實現的:
function () { with (document) { with (this.form) { with (this) { //元素屬性值 } } } }
form
元素,調用username
會報錯,所以不論是服務端渲染還是Ajax請求回來數據再渲染,最好還是把form結構寫完整。
擴展作用域有三個缺點:
- 函數被調用時還沒定義會報錯,只好
try{}catch(ex){}
,分離的寫法可以在DOMContentLoaded之后再綁定。 - 擴展的作用域鏈在不同瀏覽器中會導致不同的結果。
- HTML 與 JavaScript 代碼緊密耦合,如果要更換事件處理程序,需要改動 HTML 代碼和 JavaScript代碼。
DOM0級事件處理程序
- 每個元素(包括
window
和document
)都有自己的事件處理程序屬性,這些屬性通常全部小寫。使用 DOM0 級指定的事件處理程序被認為是元素的方法。this
引用當前元素。通過this
可以訪問元素的任何屬性和方法。DOM0 級事件處理程序在冒泡階段被處理。
var btn = document.getElementById("myBtn"); btn.onclick = function () { alert(this.id); //"myBtn" };
DOM2級事件處理程序
addEventListener()
包含三個參數,要處理的事件名、事件處理函數、布爾值,布爾值為true,表示在捕獲階段調用事件處理程序,反之在冒泡階段調用。- DOM2 級事件處理程序中的
this
也指向addEventListener
的那個元素。- 可以添加多個事件處理程序,按添加順序依次調用。
removeEventListener
無法移除匿名函數的事件處理程序。
var btn = document.getElementById("myBtn"); var handler = function () { alert(this.id); }; btn.addEventListener("click", handler, false); //這里省略了其他代碼 btn.removeEventListener("click", handler, false); // 有效!
- IE9、Firefox、Safari、Chrome 和Opera 支持DOM2 級事件處理程序。
IE事件處理程序
attachEvent
detachEvent
接收兩個參數,事件處理程序名稱、事件處理程序函數。由於IE8及更早版本只支持事件冒泡,所以該事件處理程序只支持事件冒泡。- 老版本的Opera支持這種方法,但現在Opera已經改用blink內核,IE11已經不支持這種方法,注意 IE9 就已經支持 DOM2 級事件處理程序了。
- 特別要注意:第一個參數包含on,比如onclick。
- 區別於DOM0 級事件處理程序,
this
指向 'window'。- 也可以添加多個事件處理程序。
跨瀏覽器的事件處理程序
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type, handler, false); } else if (element.attachEvent){ element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } };
- 存在問題:
- IE事件處理程序 中的
this
指向window
。- 只支持 DOM0 級的瀏覽器不能多次添加事件處理程序,不過這種瀏覽器應該不多了,即使是IE8 也支持attachEvent。
- 會不會有一些事件,在瀏覽器支持 DOM2 級事件處理程序的情況下,那些事件只能用
on + name
的形式呢? 之前一直懷疑 (1).xhr.onreadystatechange()
和 (2).DOMNodeInserted
事件,這里我多慮了,經過驗證,(1).是支持 DOM2 級事件的,(2).天生就是 DOM2 級的。這里只是為了打消我的疑慮,記錄下來。
事件對象
DOM 中的事件對象
- 兼容 DOM 的瀏覽器會將一個
event
對象傳入事件處理程序, IE9 及更高版本可以。無論指定事件處理程序時使用什么方法(DOM0 級 DOM2 級),HTML 事件處理程序可以通過訪問event
變量得到event
對象。- event 中的屬性和方法都是只讀的
- 常用屬性:
target
事件的目標currentTarget
綁定事件的元素,與 'this' 的指向相同stopPropagation()
取消事件的進一步捕獲或冒泡。如果bubbles為true,則可以使用這個方法stopImmediatePropagation()
取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用(DOM3級事件中新增)preventDefault()
取消事件的默認行為,比如點擊鏈接跳轉。如果cancelable
是true
,則可以使用這個方法type
被觸發的事件類型eventPhase
調用事件處理程序的階段:1表示捕獲階段,2表示“處於目標”,3表示冒泡階段
this
target
currentTarget
舉例:
document.body.onclick = function(event){ alert(event.currentTarget === document.body); //true alert(this === document.body); //true alert(event.target === document.getElementById("myBtn")); //true };
- 通過
event.type
與switch case
組合,可以通過一個函數處理多個事件。- 只有在事件處理程序執行期間,
event
對象才會存在;一旦事件處理程序執行完成,event
對象就會被銷毀。
IE 中的事件對象
- DOM0 級的事件處理程序,
event
作為window
的一個屬性存在。(從 IE9 開始,event 可以從參數中獲得)attachEvent
添加的事件處理程序,event
作為參數傳入,也可以通過window
來訪問event
對象。- HTML 事件處理程序依然可以通過訪問
event
變量得到event
對象。- 屬性和方法:
cancelBubble
設置true
orfalse
可以取消事件冒泡returnValue
設置true
orfalse
可以取消事件的默認行為。srcElement
事件的目標(與DOM中的target
相同)
- 注意事項:
attachEvent
中的event.srcElement === this
嗎? 答案是否定的,因為前面說到過attachEvent
中this
指向window
, DOM0 級、DOM2 級 事件處理程序this
才指向event.target / window.event.srcElement
跨瀏覽器的事件對象
var EventUtil = { getEvent: function(event){ return event ? event : window.event; // window.event DOM0級時IE }, getTarget: function(event){ return event.target || event.srcElement; // event.srcElement for IE }, preventDefault: function(event){ if (event.preventDefault){ event.preventDefault(); } else { event.returnValue = false; // IE } }, stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; // IE } } };
事件類型
- DOM3 級事件規定了幾類事件;HTML5 也定義了一組事件;還有一些事件沒有規范,瀏覽器的實現不一致。
- DOM3 級事件模塊在 DOM2 級事件模塊基礎上重新定義了這些事件,也添加了一些新事件。包括 IE9 在內的所有主流瀏覽器都支持 DOM2 級事件。IE9 也支持 DOM3 級事件。
這里只總結一些常見的事件類型
UI事件類型
- load 事件,當頁面完全加載后(包括所有圖像、JavaScript 文件、CSS 文件等外部資源),就會觸發
window
上面的 load 事件。
EventUtil.addHandler(window, "load", function(){ var image = document.createElement("img"); EventUtil.addHandler(image, "load", function(event){ event = EventUtil.getEvent(event); alert(EventUtil.getTarget(event).src); }); document.body.appendChild(image); image.src = "smile.gif"; //在此之前要先指定事件處理程序 });
- script 元素也會觸發 load 事件,據此可以判斷動態加載的 JavaScript 文件是否加載完畢。與圖像不同,只有在設置了 script 元素的 src 屬性並將該元素添加到文檔后,才會開始下載 JavaScript 文件
- IE8 及更早版本不支持 script 元素上的 load 事件。
- 在不屬於 DOM 文檔的圖像(包括未添加到文檔的 img 元素和 Image 對象)上觸發 load 事件時,IE8 及之前版本不會生成 event 對象。IE9 修復了這個問題。
- resize 事件
- 瀏覽器窗口大小發生變化時會觸發該事件,這個事件在
window
上觸發,IE、Safari、Chrome 和 Opera 會在瀏覽器窗口變化了 1 像素時就觸發 resize 事件,然后隨着變化不斷重復觸發。Firefox 則只會在用戶停止調整窗口大小時才會觸發。- 注意不要在這個事件的處理程序中加入大計算量的代碼,或者采用函數節流的方式優化性能。
- 瀏覽器窗口最小化或最大化時也會觸發 resize 事件。
- scroll 事件
- 該事件在 window 上發生,此處和書上講的有點不一樣,webkit 內核或 blink 內核的瀏覽器(Chrome、Opera、Safari)可以通過 document.body.scrollTop 獲取頁面被卷去的高度,而 Trident、Gecko (IE、火狐)可以通過 document.documentElement.scrollTop來獲取該值。
- 另外標准模式、混雜模式這兩種方法還有出入,此處不討論。
- 所以最好通過
document.body.scrollTop + document.documentElement.scrollTop
的方式獲取 scrollTop 的值,因為兩者之一會等於0,或者使用document.body.scrollTop || document.documentElement.scrollTop
,兩者效果一致。
焦點事件
- 這里忽略 DOMFocusIn、DOMFocusOut,因為只有 Opera 支持這個事件,且 DOM3 級事件廢棄了它們。
- blur:在元素失去焦點時觸發。這個事件不會冒泡;所有瀏覽器都支持它。
- focus:在元素獲得焦點時觸發。這個事件不會冒泡;所有瀏覽器都支持它。
- focusin:與 focus 等價,但它冒泡。
- focusout:與 blur 等價,也冒泡。
- 支持 focusin、focusout 的瀏覽器有:IE5.5+、Safari 5.1+、Opera 11.5+和Chrome。但只支持 DOM2 級事件處理程序
- Firefox 不支持 focusin、focusout
- blur、focusout 的事件目標是失去焦點的元素;focus、focusin 的事件目標是獲得焦點的元素
鼠標與滾輪事件
click
在用戶單擊住鼠標按鈕或按下回車鍵時觸發。 觸發順序 mousedown mouseup click,如果 mousedown、mouseup 其中之一被取消,就不會觸發 click 事件。dblclick
觸發順序 mousedown mouseup click mousedown mouseup click dblclick, 如果中間有事件被取消,dblclick 也不會被觸發mousedown
用戶按下了任意鼠標按鈕時觸發。mouseup
用戶釋放按鈕時觸發mouseenter
在鼠標光標從元素外部首次移動到元素范圍之內時觸發。不冒泡,而且在光標移動到后代元素上不會觸發。DOM2 級事件並沒有定義這個事,但 DOM3 級事件將它納入了規范。IE、Firefox9+和Opera支持這個事件。mouseleave
在位於元素上方的鼠標光標移動到元素范圍之外時觸發。不冒泡,而且在光標移動到后代元素上不會觸發。DOM2 級事件並沒有定義這個事,但 DOM3 級事件將它納入了規范。IE、Firefox9+ 和 Opera 支持這個事件。mouseover
在鼠標指針位於一個元素外部,然后用戶將其首次移入另一個元素邊界之內時觸發。不能通過鍵盤觸發這個事件。mouseout
在鼠標指針位於一個元素上方,然后用戶將其移入另一個元素時觸發。又移入的另一個元素可能位於前一個元素的外部,也可能是這個元素的子元素。不能通過鍵盤觸發這個事件。
- 用代碼說明一下 mouseenter、mouseleave 和 mouseover、mouseout 的區別:
<!DOCTYPE html> <html lang="zh-cn"> <head> <title>test1</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="test1.css"> </head> <body> <div class="mouseover"> <div class="sub-mouseover"> </div> </div> <div class="mouseenter"> <div class="sub-mouseenter"> </div> </div> <script src="test1.js"></script> </body> </html>
.wrap { width: 200px; height: 100px; } .mouseover { background: pink; } .mouseenter { margin-top: 30px; background: gray; } .sub-mouseover, .sub-mouseenter { width: 100px; height: 50px; background: #AE81FF; }
var div1 = document.querySelector(".mouseover"), div2 = document.querySelector(".mouseenter"); div1.addEventListener("mouseover", function(){ console.log("div1 mouseover"); }); div1.addEventListener("mouseout", function(){ console.log("div1 mouseout"); }) div2.addEventListener("mouseenter", function(){ console.log("div2 mouseenter"); }) div2.addEventListener("mouseleave", function(){ console.log("div2 mouseleave"); })
-
效果圖
-
鼠標由左側從上到下依次經過所有 div 的情況,輸出
div1 mouseover
div1 mouseout
div1 mouseover
div1 mouseout
div2 mouseenter
div2 mouseleave
mousemove
當鼠標指針在元素內部移動時重復地觸發。不能通過鍵盤觸發這個事件。- 除了 mouseenter、mousedleave,所有鼠標事件都會冒泡,取消鼠標事件將會影響瀏覽器的默認行為,也會影響其它事件,因為鼠標事件與其它事件是密不可分的。
- 關於
dblclick
IE8 及之前版本中的實現有一個小bug,因此在雙擊事件中,會跳過第二個mousedown 和click事件,其順序如下:mousedown
mouseup
click
mouseup
dblclick
,但還是會觸發dblclick
事件- 客戶區坐標位置:鼠標事件中的
event
都有clientX
clientY
屬性,表示在視口中客戶區的坐標位置,這些值不包括頁面滾動的距離,因此這個位置並不表示鼠標在頁面上的位置:
- 頁面坐標位置:pageX、pageY,這兩個屬性表示鼠標光標在頁面中的位置,在頁面沒有滾動的情況下,pageX 和 pageY 的值與 clientX、clientY 的值相等。IE8 及更早版本不支持事件對象上的頁面坐標,不過使用客戶區坐標和滾動信息可以計算出來。這時候需要用到document.body(混雜模式)或document.documentElement(標准模式)中的scrollLeft 和scrollTop 屬性。計算過程如下所示:
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "click", function(event){ event = EventUtil.getEvent(event); var pageX = event.pageX, pageY = event.pageY; if (pageX === undefined){ pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if (pageY === undefined){ pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } alert("Page coordinates: " + pageX + "," + pageY); });
- 屏幕坐標位置:screenX、screenY
- 修改鍵 用戶按住Shift、Ctrl、Alt、Meta(Windows或Cmd,cmd(mac))時觸發鼠標事件,可以在
event
中獲得修改鍵。
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "click", function(event){ event = EventUtil.getEvent(event); var keys = new Array(); if (event.shiftKey){ keys.push("shift"); } if (event.ctrlKey){ keys.push("ctrl"); } if (event.altKey){ keys.push("alt"); } if (event.metaKey){ keys.push("meta"); } alert("Keys: " + keys.join(",")); });
- IE9、Firefox、Safari、Chrome 和Opera 都支持這4 個鍵。IE8 及之前版本不支持metaKey 屬性。另外,舊版本的 IE 有自己的一套寫法。
- 相關元素
mouseover
mouseout
時的 event.relatedTarget,不做詳細記錄。- 鼠標按鈕
mousedown
mouseup
是在按下/釋放任意鼠標按鈕時觸發的,所以通過 event.button: 0(左) 1(中) 2(右) 可以判斷按的是哪個鍵,但是IE8 及更低版本的瀏覽器不支持,有兼容寫法,此處不詳細敘述。EventUtil.getButton
有詳細實現。- mousewheel
event.whellDelta
為正數時,向前滾動(回到頂部、頁面向下滑動),負數則反過來,這個值是120的倍數,Opera低版本中正負相反,火狐中有自己的一套方法,這里不做詳細記錄。- 觸摸設備
- 不支持dblclick 事件。雙擊瀏覽器窗口會放大畫面,而且沒有辦法改變該行為。
- 輕擊可單擊元素會觸發mousemove 事件。如果此操作會導致內容變化,將不再有其他事件發生;如果屏幕沒有因此變化,那么會依次發生mousedown、mouseup 和click 事件。輕擊不可單擊的元素不會觸發任何事件。可單擊的元素是指那些單擊可產生默認操作的元素(如鏈接),或者那些已經被指定了onclick 事件處理程序的元素。
- mousemove 事件也會觸發mouseover 和mouseout 事件。
- 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發mousewheel 和scroll 事件。
- 無障礙性問題
- 如果需要考慮這個問題,不建議使用
click
之外的鼠標事件。因為這個不能通過鍵盤觸發,不利於屏幕閱讀器訪問。此處不詳細記錄。
鍵盤與文本事件
keydown
: 當用戶按下鍵盤上的任意鍵時觸發,而且如果按住不放的話,會重復觸發此事件。keypress
當用戶按下鍵盤上的字符鍵時觸發,而且如果按住不放的話,會重復觸發此事件。按下Esc 鍵也會觸發這個事件。Safari 3.1 之前的版本也會在用戶按下非字符鍵時觸發keypress事件。keyup
:當用戶釋放鍵盤上的鍵時觸發。- 觸發順序:
keydown
、keypress
、keyup
,keydown
、keypress
都是在文本框發生變化之前被觸發的;keyup
事件則是在文本框已經發生變化之后被觸發的。- 如果用戶按下了一個字符鍵不放,就會重復觸發 keydown 和keypress 事件,直到用戶松開該鍵為止。
- 鍵盤事件也支持修改鍵(ctrl等)
- keydown、keyup 中的 event 有 keyCode, 與ASCII 碼中對應小寫字母或數字的編碼相同。
- keypress 中的 event 有 charCode,這個值是按下的那個鍵所代表字符的 ASCII 編碼,用
String.fromCharCode()
可以轉換成實際的字符- DOM3 級中,有
key
和char
,其中key
可以直接得到 "k"、"K"、"Shift" 等,char
屬性在按下字符鍵時行為與key
相同,在按下非字符鍵時為null
,但是支持還不完整,chrome 總是輸出 undefined。keyIdentifier
Chrome 已經不推薦使用- 表示按下的按鍵在鍵盤的位置,比如按下左右側的shift鍵,這個值就不同,Chrome 和 Safari 的實現有 bug。
textInput
: 在文本插入文本框之前會觸發textInput 事件。目的是代替keypress,退格鍵不會觸發textInput,但是會觸發keypress(只要改變文本),只有真正可以編輯的區域才會觸發textInput,但是keypress獲得焦點即可觸發。event.data中包含用戶的輸入,拼音輸入法中輸入過程的拼音不會觸發該事件。- inputMethod 代表用戶是怎樣輸入的,比如通過粘貼的方式,但是支持的瀏覽器很少。
變動事件
DOM2 級的變動(mutation)事件能在 DOM 中的某一部分發生變化時給出提示,比如 DOM 節點的插入、移除、特性被修改等等
HTML5 事件
- contextmenu 事件
EventUtil.addHandler(window, "load", function(event){ var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "contextmenu", function(event){ event = EventUtil.getEvent(event); EventUtil.preventDefault(event); var menu = document.getElementById("myMenu"); menu.style.left = event.clientX + "px"; menu.style.top = event.clientY + "px"; menu.style.visibility = "visible"; }); EventUtil.addHandler(document, "click", function(event){ document.getElementById("myMenu").style.visibility = "hidden"; }); });
- beforeunload 事件,用戶關閉標簽頁時提示
EventUtil.addHandler(window, "beforeunload", function(event){ event = EventUtil.getEvent(event); var message = "I'm really going to miss you if you go."; event.returnValue = message; return message; });
- DOMContentLoaded 在形成完整DOM樹之后就會觸發,不理會圖像、JavaScript 文件、CSS 文件或其它資源是否已經下載完畢。其實更應該使用 DOMContentLoaded 而不是 window.onload:
EventUtil.addHandler(window, "DOMContentLoaded", function(event){ alert("Content loaded."); }); EventUtil.addHandler(window, "load", function(event){ alert("Window loaded."); });
- IE9+、Firefox、Chrome、Safari 3.1+ 和 Opera9+ 都支持 DOMContentLoaded 事件。
- readystatechange 事件,略。
- pageshow 和 pagehide 事件,此處要了解 Firefox 和 Opera 有一個特性叫 “往返緩存”(back-forward cache/bfcache),用戶點擊“前進”、“后退”按鈕時,會將頁面緩存在內存。不重新加載,JavaScript的狀態會保留。但是無論頁面是否來自 bfcache,都會觸發 pageshow 事件,pageshow 的事件處理程序的 event 對象中有
event.persisted
屬性,為true
代表頁面來自bfcache,同樣 pagehide 事件觸發時,如果頁面被保存到 bfcache 中,則該屬性為 true。支持pageshow、pagehide 事件的瀏覽器有 Firefox、Safari5+、Chrome 和 Opera。 IE9 及以前的版本不支持這兩個事件。指定了 onunload 事件處理程序的頁面會被自動排除在 bfcache 之外。- hashchange 事件。在 window 上觸發,event 包含 oldURL、newURL 兩個屬性。支持該事件的有 IE8+、Firefox3.6+、Safari5+、Chrome 和 Opera10.6+,但oldURL、newURL只有Firefox6+、Chrome和Opera支持。所以最好用 location 來指定當前的 hash:
EventUtil.addHandler(window, "hashchange", function(event){ console.log(location.hash); })
設備事件
- orientationchange 事件,屏幕轉動。
觸摸與手勢事件
- touchstart: 當手指觸摸屏幕時觸發;即使已經有一個手指放在了屏幕上也會觸發。
- touchmove: 當手指在屏幕上滑動時連續地觸發。在這個事件發生期間,調用preventDefault() 可以阻止滾動。
- touchend:當手指從屏幕上移開時觸發。
- touchcancel:當系統停止跟蹤觸摸時觸發。關於此事件的確切觸發時間,文檔中沒有明確說明。
- event 對象中包含的常見 DOM 屬性有:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey 和metaKey。
- event 對象中還包含以下用於跟蹤觸摸的屬性:
- touches:表示當前跟蹤的觸摸操作的Touch 對象的數組。
- targetTouchs:特定於事件目標的Touch 對象的數組。
- changeTouches:表示自上次觸摸以來發生了什么改變的Touch 對象的數組。每個Touch 對象包含下列屬性:clientX、clientY、pageX、pageY、screenX、screenY、target、identifier(標識觸摸的唯一ID)
function handleTouchEvent(event) { //only for one touch if (event.touches.length == 1) { var output = document.getElementById("output"); switch (event.type) { case "touchstart": output.innerHTML = "Touch started (" + event.touches[0].clientX + "," + event.touches[0].clientY + ")"; break; case "touchend": output.innerHTML += "<br>Touch ended (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; case "touchmove": event.preventDefault(); //prevent scrolling output.innerHTML += "<br>Touch moved (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; } } }
- 一次觸摸的事件觸發順序為:touchstart、mouseover、mousemove(一次)、mousedown、mouseup、click、touchend
- 手勢事件:
- gesturestart:當一個手指已經按在屏幕上而另一個手指又觸摸屏幕時觸發。
- gesturechange:當觸摸屏幕的任何一個手指的位置發生變化時觸發。
- gestureend:當任何一個手指從屏幕上面移開時觸發。
- 屬性有標准的鼠標事件屬性,還有兩個:rotation(正值表示順時針)和scale(從1開始)
內存和性能
- 每個函數都是對象,都會占用內存;內存中的對象越多,性能就越差。
- 必須事先指定所有事件處理程序而導致的 DOM 訪問次數,會延遲整個頁面的交互就緒時間。
事件委托
<body> <ul id="myLinks"> <li id="goSomewhere">Go somewhere</li> <li id="doSomething">Do something</li> <li id="sayHi">Say hi</li> </ul> <script type="text/javascript"> (function(){ var list = document.getElementById("myLinks"); EventUtil.addHandler(list, "click", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "doSomething": document.title = "I changed the document's title"; break; case "goSomewhere": location.href = "http://www.wrox.com"; break; case "sayHi": alert("hi"); break; } }); })(); </script> </body>
- 上面的方法只取得了一個 DOM 元素,只添加了一個事件處理程序,占用的內存更少。
- 如果將事件委托到 document 中,會更有優勢:
- document 對象很快就可以訪問,而且可以在頁面生命周期的任何時點上為它添加事件處理程序(無需等待 DOMContentLoaded 或 load 事件)。
- 在頁面中設置事件處理程序所需的時間少。只添加一個事件處理程序所需的 DOM 引用更少,所花的時間也更少。
- 整個頁面占用的內存空間更少,能夠提升整體性能。
- 最適合采用事件委托技術的事件包塊
click
、mousedown
、mouseup
、keydown
、keyup
和keypress
移除事件處理程序
- 如果你知道某個元素即將被移除,那么最好手工移除事件處理程序,因為有的瀏覽器(尤其是 IE)不會作出恰當地處理,它們很有可能會將對元素和對事件處理程序的引用都保存在內存中。
- IE8 及更早的版本在頁面被卸載(刷新,切換頁面)之前沒有清理干凈事件處理程序,它們會滯留在內存中,可以通過 onunload 事件處理程序移除所有事件處理程序。
模擬事件
- 在測試 Web 應用程序,模擬觸發事件是一種極其有用的技術。DOM2 級規范為此規定了模擬特定事件的方式,IE9、Opera、Firefox、Chrome 和 Safari 都支持這種方式。IE有它自己模擬事件的方式(IE8 及以下才要用到)
DOM 中的事件模擬
- 可以在 document 對象上使用 createEvent 方法創建 event 對象。這個方法接收一個參數,即表示要創建的事件類型的字符串。在 DOM2 級中,所有這些字符串都使用英文復數形式,而在 DOM3 級中變成了單數。這個字符串可以是下列幾個字符串之一:
- UIEvents,DOM3 級中是 UIEvent
- MouseEvents: 一般化的鼠標事件,DOM3 級中是 MouseEvent
- MutationEvents: 一般化的 DOM 變動事件。 ...
- HTMLEvents 一般化的 HTML 事件。沒有對應的 DOM3 級事件(HTML 事件被分割到其他類別中)
模擬鼠標事件
- createEvent 方法返回的 event 對象中,有 initMouseEvent() 方法,需要傳 15 個參數。type(比如"click"),bubbles(Boolean) 是否冒泡,應該設置為 true, cancelable(Boolean) 應該設置為 true,view(幾乎總是document.defaultView), detail(通常設置為0), screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button(表示按下了哪個鼠標,默認0), relatedTarget(只有在模擬 mouseover 或 mouseout時使用)
- 將 event 對象傳給 DOM 節點的 dispatchEvent 方法即可觸發事件,如下:
<body> <input type="button" value="Click me" id="myBtn" /> <input type="button" value="Send click to the other button" id="myBtn2" /> <p>This example works in DOM-compliant browsers (not IE).</p> <script type="text/javascript"> (function(){ var btn = document.getElementById("myBtn"); var btn2 = document.getElementById("myBtn2"); EventUtil.addHandler(btn, "click", function(event){ alert("Clicked!"); alert(event.screenX); //100 }); EventUtil.addHandler(btn2, "click", function(event){ //create event object var event = document.createEvent("MouseEvents"); //initialize the event object event.initMouseEvent("click", true, true, document.defaultView, 0, 100, 0, 0, 0, false, false, false, false, 0, btn2); //fire the event btn.dispatchEvent(event); }); })(); </script> </body>
模擬鍵盤事件
- "DOM2 級事件"的草案中本來包含了鍵盤事件,但在定稿前又被刪除了;Firefox 根據其草案實現了鍵盤事件。但跟 "DOM3 級事件"中的鍵盤事件有很大區別。
- DOM3 級規定,調用
createEvent()
並傳入 "KeyboardEvent" ,返回鍵盤事件,有initKeyEvent()
方法。這個方法接收一下參數- type, bubbles, cancelable, view, key(按下的鍵的鍵碼), location(按下了哪里的鍵,0:主鍵盤,1:左,2:右,3:數字鍵盤,4:虛擬鍵盤,5:手柄), modifiers: 空格分隔的修改鍵列表,如 "Shift", repeat(在一行中按了這個鍵多少次)
DOM3 級不提倡keypress
事件, 因此只能模擬keydown
keyup
IE 中的事件模擬
第一步:document.createEventObject()
第二步: 通過賦值的方式初始化事件對象,就是 event.screenX = 0
這些
第三步:btn.fireEvent("onclick", event);