事件,就是在文件或瀏覽器視窗中發生的一些特定的互動瞬間,例如點擊、雙擊、滑鼠懸浮等。
事件流描述的是從頁面中接收事件的順序,或者說是事件在頁面中傳播的順序。
IE的事件流叫做事件冒泡(event bubbling):由最具體的元素開始執行事件,然後逐級向上傳播到window對象。
網景團隊提出的事件流叫做事件擷取(event capturing):由最外層的window 物件開始執行事件,然後逐漸向下傳播到最具體的元素。
下面我們要做個小實驗來說明這兩個事件流的差異:
// html 文档 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>事件流 实验</title> </head> <body> <p id="test" style="font-size:3em;">点我点我,我是p</p> </body> </html>
// 事件冒泡 var p = document.getElementById("test"); p.addEventListener("click",function(){ console.log("i am p"); }, false); document.body.addEventListener("click",function(){ console.log("i am body"); }, false); document.documentElement.addEventListener("click",function(){ console.log("i am html"); }, false); document.addEventListener("click",function(){ console.log("i am document"); }, false); window.addEventListener("click",function(){ console.log("i am window"); }, false);
点击p,控制端会打印如下: i am p i am body i am html i am document i am window
// 事件捕获 var p = document.getElementById("test"); p.addEventListener("click",function(){ console.log("i am p"); }, true); document.body.addEventListener("click",function(){ console.log("i am body"); }, true); document.documentElement.addEventListener("click",function(){ console.log("i am html"); }, true); document.addEventListener("click",function(){ console.log("i am document"); }, true); window.addEventListener("click",function(){ console.log("i am window"); }, true);
圖片經過二次處理,略醜,請見諒!
点击p,控制端会打印如下: i am window i am document i am html i am body i am p
可以看出,事件冒泡和事件捕獲是兩種完全相反的事件流。
平常,一般都是在使用事件冒泡,只在特殊情況下使用事件捕獲。
DOM2級事件規定的事件流包含三個階段:事件擷取階段、處於目標階段、事件冒泡階段。
// 事件冒泡和事件捕获混合一下 var p = document.getElementById("test"); p.addEventListener("click",function(){ console.log("i am p"); }, true); document.body.addEventListener("click",function(){ console.log("i am body"); }, false); // 改为在冒泡阶段调用事件处理程序 document.documentElement.addEventListener("click",function(){ console.log("i am html"); }, true); document.addEventListener("click",function(){ console.log("i am document"); }, true); window.addEventListener("click",function(){ console.log("i am window"); }, false); // 改为在冒泡阶段调用事件处理程序
這張圖片是直接從書上摘得,自己腦補一下,把 window 物件自行添上去吧!
点击p,控制端会打印如下: i am document i am html i am p i am body i am window
事件處理程序:指的是回應處理某個事件的函數
HTML 事件處理程序
某個元素支援的每個事件,都可以使用一個與對應事件處理程序同名的HTML 特性來指定。
// 情况一 <button onclick="alert('I am button!');">按钮</button> // 情况二 <button onclick="show();">按钮</button> <script> function show(){ alert("I am button!"); } </script>
建議永遠不要使用這種方式為元素添加事件,因為這種方法有一個巨大的缺點,就是使得 HTML 與 JavaScript 的程式碼緊密耦合,不符合網頁設計的行為與結構分離。
DOM0 層級事件處理程序
每個元素(包括window 和document)都有自己的事件處理程序屬性,例如onclick、 onmouseup 等。
把上面的程式碼改寫如下:
<button id="test">按钮</button> var button = document.getElementById("test"); button.onclick = function(){ // this 对象指向 button 元素 console.log(this); alert("I am button!"); }; /* 删除指定的事件处理程序 */ button.onclick = null;
DOM2 級事件處理程序
DOM2 級事件 定義了兩個方法,用來處理指定和刪除事件處理程序的動作:addEventListener()和 removeEventListener()。
這兩個方法都有三個參數,第一個參數是事件名,第二個參數是作為事件處理程序的函數,第三個參數是一個布林值,預設值是false,表示在冒泡階段呼叫事件處理程序,如果這個值是true,表示在擷取階段呼叫事件處理程序。
繼續改寫上面的程式碼:
var button = document.getElementById("test"); function show(){ // this 对象指向 button 元素 console.log(this); alert("I am button!"); } // 添加事件处理程序 button.addEventListener("click",show, false); button.addEventListener("click",function(){ alert("I am second alert!"); },false); // 删除事件处理程序 button.removeEventListener("click",show, false); // 删除成功 button.removeEventListener("click",function(){ // 删除失败 alert("I am second alert!"); },false);
使用 DOM2 層級方法新增事件處理程序的主要好處是可以為一個元素新增多個事件處理程序。
如果 addEventListener() 新增的事件處理函數是匿名函數,則無法透過 removeEventListener() 刪除這個事件處理程序。
IE 事件處理程序
IE8 以及更早的IE 版本,只支援事件冒泡,不支援addEventListener() 和removeEventListener(),但是它實作了與這兩個方法類似的兩個方法:attachEvent() 和 detachEvent()。這兩個方法只有兩個參數,一個事件名稱(是 onclick,不是 click),一個事件處理函數。
與 addEventListener() 不同,使用attachEvent() 方法的情況下,事件處理函數會在全域作用域下執行,所以 this 等於 window,這點需要特別注意。
var button = document.getElementById("test"); button.attachEvent("onclick", function(){ // this 对象指向了全局作用域,即 window console.log(this); alert("I am button!"); }); button.attachEvent("onclick",function(){ alert("I am second alert!"); }); // 上面也是为同一个按钮绑定了两个事件处理程序 button.detachEvent("onclick",function(){ // 删除失败 alert("I am second alert!"); });
除非你想讓你的程式相容至 IE8- 瀏覽器,否則不要使用這兩個函數。
對於一些特定的事件,瀏覽器有它預設的行為。例如:點擊連結會進行跳到指定的頁面、點擊滑鼠右鍵會呼出瀏覽器右鍵選單、填寫表單時按回車會自動提交到伺服器等。
預設的瀏覽器行為和冒泡行為是獨立的,取消事件預設行為是不會取消事件冒泡的,反之亦然。同一個元素的多個事件處理函數也是相互獨立的。
<p> <a id="test" href="http://baidu.com">点我点我,我是链接</a> </p> var link = document.getElementById("test"); // 方法一 event.preventDefault() link.onclick = function(event){ console.log("You click!"); event.preventDefault(); }; // 方法二 return false link.onclick = function(event){ console.log("You click!"); return false; };
參考:事件-4.瀏覽器預設行為-宇卿
在觸發DOM 上的某個事件時,會產生一個事件物件event,而這個物件中包含著所有與該事件有關的資訊。
event 对象只存在于事件处理程序执行期间,一旦执行完毕,立即被销毁。
<p> <a id="test" href="http://baidu.com">点我点我,我是链接</a> </p> /* 下列 event 对象的属性和方法都是只读的 */ var link = document.getElementById("test"); link.onclick = function(event){ // 判断当前事件是否会向 DOM 树上层元素冒泡 console.log(event.bubbles); // 判断是否可以取消事件的默认行为 console.log(event.cancelable); // 使用该方法可以取消事件的默认行为(使用前提是 cancelable 属性的值为 true) event.preventDefault(); // 判断是否已经调用了 preventDefault() 方法(DOM3级事件新增) console.log(event.defaultPrevented); // 指向事件遍历 DOM 时,识别事件的当前目标对象 console.log(event.currentTarget); // 指向触发事件的对象 console.log(event.target); // 表示事件流当前处于哪一个阶段 // 值为 1 表示在捕获阶段,值为 2 表示处于目标阶段,值为 3 表示在冒泡阶段 console.log(event.eventPhase); // 返回一个字符串, 表示该事件对象的事件类型 console.log(event.type); // 立即停止当前事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡 event.stopPropagation(); };
为了进一步说明 event.stopPropagation() 的运行效果,借用前面的代码,更改如下:
// 事件冒泡和事件捕获混合一下 var p = document.getElementById("test"); p.addEventListener("click",function(){ console.log("i am p"); }, true); document.body.addEventListener("click",function(){ console.log("i am body"); }, false); // 改为在冒泡阶段调用事件处理程序 document.documentElement.addEventListener("click",function(){ console.log("i am html"); event.stopPropagation(); // 立即停止事件在 DOM 中的传播 }, true); document.addEventListener("click",function(){ console.log("i am document"); }, true); window.addEventListener("click",function(){ console.log("i am window"); }, false); // 改为在冒泡阶段调用事件处理程序
点击p,控制端会打印如下: i am document i am html
看到了吧,后面元素的事件就不会被激发了。
UI 事件指的是简单的用户界面事件
load 事件:当页面所有资源(比如图像、css文件、js文件等资源)完全加载完毕后,就会触发 window 上面的 load 事件。
当然,我们也可以单独为某个元素设置 load 事件,比如为一个图片绑定这个事件,就可以检测这个图片是否加载完毕了。
<img id="myImg" src="1.jpg" / alt="JavaScript DOM 事件初探的圖文程式碼詳細介紹" > var img = document.getElementById("myImg"); img.onload = function(){ alert("Image loaded!"); };
scroll 事件:在文档被滚动期间,重复触发该事件。
window.onscroll = function(){ console.log("Scroll!"); };
焦点事件会在页面元素获得或失去焦点时触发。
注意:默认情况下,只有部分 html 元素能获得鼠标焦点(如 input,a),很大一部分 html 元素是不能获得鼠标焦点的(如 p,img),这些能够获得鼠标焦点的元素就是 focusable 元素。
不过,可以通过为这些默认没有焦点事件的元素添加一个 tabindex 属性,从而使它可以支持焦点事件。
document.activeElement:返回当前页面中获得焦点的元素,如果没有某个元素获得焦点,则该属性的值为当前页面中的元素
document.hasFocus():判断当前文档或者当前文档的子节点是否获得了焦点
HTMLElement.focus():使得指定的元素获得焦点(前提是这个元素是一个可以获得焦点的元素)
HTMLElement.blur():移除当前元素获得的焦点
上面是一些 HTML5 中添加的焦点管理 API。下面说焦点事件:
focus 事件:在元素获得焦点时触发,这个事件不会冒泡
blur 事件:在元素失去焦点时触发,这个事件不会冒泡
focusin 事件:在元素获得焦点时触发,与 focus 事件等价,但会冒泡
focusout 事件:在元素失去焦点时触发,与 blur 事件等价,但会冒泡
<img id="img" src="1.jpg" tabindex="1" / alt="JavaScript DOM 事件初探的圖文程式碼詳細介紹" > var img = document.getElementById("img"); document.body.onscroll = function() { img.blur(); }; img.onfocus = function() { console.log("Img focused!"); }; img.onblur = function() { console.log("Img lose focus!"); }; img.onclick = function() { console.log(document.activeElement); console.log(document.hasFocus()); };
注意: focusin 和 focusout 事件,所有的 Firefox 版本都不支持,看这里。chrome 和 safari 中只有通过 addEventListener 方式绑定这两个事件才能正常使用,其他方式绑定都不行。
说说focus /focusin /focusout /blur 事件(推荐)
(WHATWG)Focus management APIs
DOM3 级事件中定义了9个鼠标事件。
<body> <p id="test"> <button>按钮</button> </p> </body> // css p { width: 300px; height: 300px; margin: 50px auto; border: 1px solid #aaa; }
mouseover 事件:鼠标指针首次进入一个元素边界之内时触发,会冒泡
mouseout 事件:鼠标指针移出这个元素边界时触发,会冒泡
var test = document.getElementById("test"); test.onmouseover = function(event){ event.target.style.border = "3px solid #666"; }; test.onmouseout = function(event){ event.target.style.border = "none"; };
mouseenter 事件:鼠标指针首次移动到元素范围之内时触发,不冒泡
mouseleave 事件:鼠标指针移动到元素范围之外时触发,不冒泡
var test = document.getElementById("test"); test.onmouseenter = function(event){ event.target.style.border = "10px solid #666"; }; test.onmouseleave = function(event){ event.target.style.border = "none"; };
mousemove 事件:当鼠标在元素内部移动时重复触发,会冒泡
// 这里写一个鼠标在 body 内移动时,背景颜色随机变化的脚本 document.body.onmousemove = function(){ var r = Math.floor(Math.random() * 256) g = Math.floor(Math.random() * 256) b = Math.floor(Math.random() * 256); document.body.style.background = "rgb("+r+","+g+","+b+")"; };
mousedown 事件:用户通过按下任意鼠标按钮时触发
mouseup 事件:用户释放鼠标按钮时触发
click 事件:用户单击鼠标左键时或按下回车键时触发
dblclick 事件:用户双击鼠标左键时触发
var body = document.body; body.onmousedown = function(){ console.log("Mouse down!"); }; body.onmouseup = function(){ console.log("Mouse up!"); }; body.onclick = function(){ console.log("One click!"); }; body.ondblclick = function(){ console.log("Double click!"); };
// 双击 body,控制端打印: Mouse down! Mouse up! One click! Mouse down! Mouse up! One click! Double click!
只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件,缺一不可。类似地,只有连续触发两次 click 事件,才会触发 dblclick 事件。
获取点击坐标
clientX 和 clientY:鼠标指针在视口中的坐标
pageX 和 pageY:鼠标指针在页面中的坐标
screenX 和 screenY:鼠标指针在屏幕中的坐标
在页面没有滚动的情况下,pageX 和 pageY 的值与 clientX 和 clientY 的值相等。
document.body.onclick = function() { console.log("Client: " + "(" + event.clientX + "," + event.clientY + ")"); console.log("Page: " + "(" + event.pageX + "," + event.pageY + ")"); console.log("Screen: " + "(" + event.screenX + "," + event.screenY + ")"); };
keydown 事件:当用户按下任意键时触发,而且如果按住不放的话,会重复触发此事件
keypress 事件:当用户按下任意字符键时触发,而且如果按住不放的话,会重复触发此事件
keyup 事件:当用户释放键盘上的按键时触发
在用户按下一个字符键并且立马释放这个按键的过程中,先触发 keydown 事件,再触发 keypress 事件,最后触发 keyup 事件。
window.onkeydown = function() { console.log("on key down"); }; window.onkeyup = function() { if (event.keyCode === 65) { console.log(event.key); } console.log("one key up"); }; window.onkeypress = function() { console.log("one key press"); };
为了知道自己按下的是哪个按键,可以使用 event.which 来获得你按下按键的键码(比如字母 A 的键码为 65)。也可以通过键码属性(event.keyCode)来对特定的按键来进行响应。键码参考表
发生 keypress 事件时,会存在一个 charCode 属性,返回这个按键代表字符的 ASCII 编码。ASCII table
event.which 返回一个 keyCode 或 charCode 值,详情看下面代码
String.fromCharCode() 静态方法可以将 event.which 转化为相应的字符。
// 请自行粘贴复制于浏览器控制端测试 // 分别输入 a 和 A // A和a 的 ASCII码分别为65,97 window.onkeydown = function(){ if(event.keyCode === 65){ console.log("(keydown keyCode)You press 'a' or 'A'!"); } if(event.charCode === 65){ console.log("(keydown charCode)You press 'A'!"); } console.log("keydown event.which = " + event.which); console.log("(keydown:)" + String.fromCharCode(event.which)); }; window.onkeypress = function(){ if(event.keyCode === 65){ console.log("(keypress keyCode)You press 'A'!"); } if(event.charCode === 65){ console.log("(keypress charCode)You press 'A',not 'a'!"); } console.log("keypress event.which = " + event.which); console.log("(keypress:)" + String.fromCharCode(event.which)); };
在 DOM3 级事件中,做了一些变化,不再推荐使用 keyCode、charCode、which 属性,而是出现了两个新属性:
key
和char
,用来替代keyCode
和charCode
属性。但是不建议使用 key、char 属性,因为有一部分浏览器对他支持的不是很好。点这里
参考:JavaScript 事件——“事件类型”中“键盘与文本事件”的注意要点
变动事件是在 DOM 结构发生变化时触发
在 DOM2 中定义了多个变动事件,但是在 DOM3 中又废除了一些,在这里先简单提一下有这个东西,以后用到再作补充。
contextmenu 事件:单击鼠标右键,会触发这个事件并调出页面的上下文菜单。这个事件是鼠标事件的一种,并且支持冒泡。
在上面的例子中,我们演示了利用 contextmenu 事件来设置自定义右键菜单,以及屏蔽右键菜单。
然而,屏蔽右键菜单却难不倒高手,Greasy Fork 上面有一个脚本(网页限制解除),专门来打破对右键菜单有限制的页面。
beforeunload 事件:当浏览器窗口,文档或其资源将要卸载时或者刷新页面时,会触发这个事件【MDN】
下面有几点需要注意一下:
1. 如果处理函数为 Event 对象的 returnValue 属性赋值非空字符串,浏览器会弹出一个对话框,来询问用户是否确定要离开当前页面。没有赋值时,该事件不做任何响应。
2. 从2011年5月25号开始,HTML5规范指出在此事件处理函数中,对于window.alert(), window.confirm(), 和 window.prompt() 的调用会被忽略。
3. 旧版本的浏览器可能会在提示框里显示返回的信息,但是新版本浏览器都默认采用浏览器内设的提示信息。
// 复制粘贴于浏览器控制端,然后关闭页面,会出现提示框 window.addEventListener("beforeunload", function() { var message = "你真的要离开吗?"; event.returnValue = message; return message; }, false);
DOMContentLoaded 事件:在页面文档形成完整的 DOM 树时触发,不理会图像、js文件、css文件等资源是否加载完毕
这个事件可以为 document 或 window 绑定,但实际目标是 document。这个事件会冒泡。
注意:这个事件与 load 事件是不一样的,正常情况下,会早于 load 事件被触发。
感觉篇幅太长了,事件委托、移动设备中的事件、事件对内存性能的影响等内容将在以后逐渐写出。
以上就是JavaScript DOM 事件初探的图文代码详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!