事件
事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。
JavaScript 與 HTML 之間的交互是通過事件實現的。
對於 Web 應用來說,有下面這些代表性的事件:點擊某個元素、將鼠標移動至某個元素上方、
按下鍵盤上某個鍵等等。
我們可以在事件對應的屬性中設置一些js代碼,這樣當事件被觸發時,這些代碼將會執行。
這種寫法我們稱爲結構和行爲耦合,不方便維護,不推薦使用
<button id="btn" onmousemove="alert('討厭,你點我幹嘛!');">
我是一個按鈕
</button>
可以爲按鈕的對應事件綁定處理函數的形式來響應事件,這樣當事件被觸發時,
其對應的函數將會被調用,像這種爲單擊事件綁定的函數,我們稱爲單擊響應函數。
// html
<button id="btn">我是一個按鈕</button>
// js
var btn = document.getElementById("btn");
btn.onclick = function(){
alert("你還點~~~");
};
常見的HTML事件:
事件 | 描述 |
---|---|
onchange | HTML 元素改變 |
onclick | 用戶點擊 HTML 元素 |
onmouseover | 用戶在一個HTML元素上移動鼠標 |
onmouseout | 用戶從一個HTML元素上移開鼠標 |
onmousemove | 鼠標在元素中移動時被觸發 |
onkeydown | 用戶按下鍵盤按鍵 |
onload | 瀏覽器已完成頁面的加載 |
事件的兼容
當事件的響應函數被觸發時,瀏覽器每次都會將一個事件對象作爲實參傳遞進響應函數,
在事件對象中封裝了當前事件相關的一切信息,比如:鼠標的座標、鍵盤哪個按鍵被按下、鼠標滾輪滾動的方向。。。
在IE8中,響應函數被觸發時,瀏覽器不會傳遞事件對象,在IE8及以下的瀏覽器中,
是將事件對象作爲window對象的屬性保存的。
// 解決事件對象的兼容性問題
event = event || window.event;
滾動條的兼容
chrome認爲瀏覽器的滾動條是body的,可以通過body.scrollTop來獲取;
火狐等瀏覽器認爲瀏覽器的滾動條是html的。
// 獲取滾動條滾動的距離
var st = document.body.scrollTop || document.documentElement.scrollTop;
var sl = document.body.scrollLeft || document.documentElement.scrollLeft;
事件的冒泡(Bubble)
所謂的冒泡指的就是事件的向上傳導,當後代元素上的事件被觸發時,
其祖先元素的相同事件也會被觸發
在開發中大部分情況冒泡都是有用的,如果不希望發生事件冒泡,可以通過事件對象來取消冒泡
取消冒泡:
w3c 的方法是 e.stopPropagation
IE 則是使用 e.cancelBubble = true
IE9 之前的IE不支持 stopPropagation() 方法。
相反,IE事件對象有一個 cancleBubble 屬性,設置這個屬性爲 true 能阻止事件進一步傳播。
( IE8 及之前版本不支持事件傳播的捕獲階段,所以冒泡是唯一待取消的事件傳播。)
// 取消事件冒泡兼容方法
function stopHandler(event){
window.event ? window.event.cancelBubble=true : event.stopPropagation();
}
事件的委派
爲每一個超鏈接都綁定一個單擊響應函數,這種操作比較麻煩,
而且這些操作只能爲已有的超鏈接設置事件,而新添加的超鏈接必須重新綁定。
我們希望,只綁定一次事件,即可應用到多個的元素上,
即使元素是後添加的也可以將事件綁定給元素的共同的祖先元素。
事件的委派
指將事件統一綁定給元素的共同的祖先元素,這樣當後代元素上的事件觸發時,會一直冒泡到祖先元素,從而通過祖先元素的響應函數來處理事件。
事件委派是利用了冒泡,通過委派可以減少事件綁定的次數,提高程序的性能
event中的target表示的觸發事件的對象(event.target)
事件的綁定
使用 對象.事件 = 函數 的形式綁定響應函數,
它只能同時爲一個元素的一個事件綁定一個響應函數,不能綁定多個,如果綁定了多個,則後邊會覆蓋掉前邊的。
addEventListener()
通過這個方法也可以爲元素綁定多個響應函數
參數:
1.事件的字符串,不要on
2.回調函數,當事件觸發時該函數會被調用
3.是否在捕獲階段觸發事件,需要一個布爾值,一般都傳false
使用addEventListener()可以同時爲一個元素的相同事件同時綁定多個響應函數
這樣當事件被觸發時,響應函數將會按照函數的綁定順序執行
這個方法不支持IE8及以下的瀏覽器
使用addEventListener()方法綁定響應函數,取消默認行爲時不能使用return false,
需要使用event來取消默認行爲event.preventDefault();
但是IE8不支持event.preventDefault()這個玩意,如果直接調用會報錯。
attachEvent()
在IE8中可以使用attachEvent()來綁定事件
使用 attachEvent 方法有個缺點,this 的值會變成 window 對象的引用而不是觸發事件的元素。
參數:
1.事件的字符串,要on
2.回調函數
這個方法也可以同時爲一個事件綁定多個處理函數,不同的是它是後綁定先執行,執行順序和addEventListener()相反。
綁定事件的兼容代碼:
// addEventListener()中的this,是綁定事件的對象
// attachEvent()中的this,是window
// 需要統一兩個方法this
/*
* 參數:
* el 要綁定事件的對象
* eventStr 事件的字符串(不要on)
* callback 回調函數
*/
function bindEventListener(el , eventStr , callback){
if(el.addEventListener){
//大部分瀏覽器兼容的方式
obj.addEventListener(eventStr , callback , false);
}else{
//IE8及以下
obj.attachEvent("on"+eventStr , function(){
// 在匿名函數中調用回調函數
// 綁定this指向綁定事件的對象
callback.call(el);
});
}
}
事件的傳播
關於事件的傳播網景公司和微軟公司有不同的理解:
微軟公司認爲事件應該是由內向外傳播,也就是當事件觸發時,
應該先觸發當前元素上的事件,然後再向當前元素的祖先元素上傳播,
也就說事件應該在冒泡階段執行。
網景公司認爲事件應該是由外向內傳播的,也就是當前事件觸發時,
應該先觸發當前元素的最外層的祖先元素的事件,然後在向內傳播給後代元素。
W3C綜合了兩個公司的方案,將事件傳播分成了三個階段
1. 捕獲階段
在捕獲階段時從最外層的祖先元素,向目標元素進行事件的捕獲,
但是默認此時不會觸發事件
2. 目標階段
事件捕獲到目標元素,捕獲結束開始在目標元素上觸發事件
3. 冒泡階段
事件從目標元素向他的祖先元素傳遞,依次觸發祖先元素上的事件,
如果希望在捕獲階段就觸發事件,可以將addEventListener()的第三個參數設置爲true,
一般情況下我們不會希望在捕獲階段觸發事件,所以這個參數一般都是false。
IE8及以下的瀏覽器中沒有捕獲階段。
自定義元素拖曳API
/*
* 提取一個專門用來設置拖拽的函數
* 參數:開啓拖拽的元素
*/
function drag(obj){
//當鼠標在被拖拽元素上按下時,開始拖拽 onmousedown
obj.onmousedown = function(event){
//設置box1捕獲所有鼠標按下的事件
/*
* setCapture()
* - 只有IE支持,但是在火狐中調用時不會報錯,
* 而如果使用chrome調用,會報錯
*/
/*if(box1.setCapture){
box1.setCapture();
}*/
obj.setCapture && obj.setCapture();
event = event || window.event;
//div的偏移量 鼠標.clentX - 元素.offsetLeft
//div的偏移量 鼠標.clentY - 元素.offsetTop
var ol = event.clientX - obj.offsetLeft;
var ot = event.clientY - obj.offsetTop;
//爲document綁定一個onmousemove事件
document.onmousemove = function(event){
event = event || window.event;
//當鼠標移動時被拖拽元素跟隨鼠標移動 onmousemove
//獲取鼠標的座標
var left = event.clientX - ol;
var top = event.clientY - ot;
//修改box1的位置
obj.style.left = left+"px";
obj.style.top = top+"px";
};
//爲document綁定一個鼠標鬆開事件
document.onmouseup = function(){
//當鼠標鬆開時,被拖拽元素固定在當前位置 onmouseup
//取消document的onmousemove事件
document.onmousemove = null;
//取消document的onmouseup事件
document.onmouseup = null;
//當鼠標鬆開時,取消對事件的捕獲
obj.releaseCapture && obj.releaseCapture();
};
/*
* 當我們拖拽一個網頁中的內容時,瀏覽器會默認去搜索引擎中搜索內容,
* 此時會導致拖拽功能的異常,這個是瀏覽器提供的默認行爲,
* 如果不希望發生這個行爲,則可以通過return false來取消默認行爲
*
* 但是這招對IE8不起作用
*/
return false;
};
}
滾輪事件
onmousewheel
鼠標滾輪滾動的事件,會在滾輪滾動時觸發,但是火狐不支持該屬性。
event.wheelDelta 可以獲取鼠標滾輪滾動的方向
向上滾 120 向下滾 -120
wheelDelta這個值我們不看大小,只看正負
wheelDelta這個屬性火狐中不支持
DOMMouseScroll
在火狐中需要使用 DOMMouseScroll 來綁定滾動事件,
注意該事件需要通過addEventListener()函數來綁定
在火狐中使用event.detail來獲取滾動的方向
向上滾 -3 向下滾 3
和event.wheelDelta相反
使用addEventListener()方法綁定響應函數, 取消默認行爲時不能使用return false,
需要使用event來取消默認行爲event.preventDefault();
但是IE8不支持event.preventDefault();這個玩意,
event.preventDefault && event.preventDefault();
滾輪事件Demo:
元素隨鼠標滾輪變長或縮短
// Html:
<body style="height: 2000px;">
<div id="box1"></div>
</body>
// css:
<style type="text/css">
#box1{
width: 100px;
height: 100px;
background-color: red;
}
</style>
// js:
window.onload = function(){
//獲取id爲box1的div
var box1 = document.getElementById("box1");
//爲box1綁定一個鼠標滾輪滾動的事件
/*
* onmousewheel鼠標滾輪滾動的事件,
* 會在滾輪滾動時觸發,
* 但是火狐不支持該屬性
*
* 在火狐中需要使用 DOMMouseScroll 來綁定滾動事件
* 注意該事件需要通過addEventListener()函數來綁定
*/
box1.onmousewheel = function(event){
event = event || window.event;
//event.wheelDelta 可以獲取鼠標滾輪滾動的方向
//向上滾 120 向下滾 -120
//wheelDelta這個值我們不看大小,只看正負
//wheelDelta這個屬性火狐中不支持
//在火狐中使用event.detail來獲取滾動的方向
//向上滾 -3 向下滾 3
/*
* 當鼠標滾輪向下滾動時,box1變長
* 當滾輪向上滾動時,box1變短
*/
//判斷鼠標滾輪滾動的方向
if(event.wheelDelta > 0 || event.detail < 0){
//向上滾,box1變短
box1.style.height = box1.clientHeight - 10 + "px";
}else{
//向下滾,box1變長
box1.style.height = box1.clientHeight + 10 + "px";
}
/*
* 使用addEventListener()方法綁定響應函數,
* 取消默認行爲時不能使用return false
* 需要使用event來取消默認行爲event.preventDefault();
* 但是IE8不支持event.preventDefault();這個玩意,如果直接調用會報錯
*/
event.preventDefault && event.preventDefault();
/*
* 當滾輪滾動時,如果瀏覽器有滾動條,
* 滾動條會隨之滾動,
* 這是瀏覽器的默認行爲,如果不希望發生,則可以取消默認行爲
*/
return false;
};
//爲火狐綁定滾輪事件
bindEventListener(box1,"DOMMouseScroll",box1.onmousewheel);
};
function bindEventListener(obj , eventStr , callback){
if(obj.addEventListener){
//大部分瀏覽器兼容的方式
obj.addEventListener(eventStr , callback , false);
}else{
//IE8及以下
obj.attachEvent("on"+eventStr , function(){
//在匿名函數中調用回調函數
callback.call(obj);
});
}
}
鍵盤事件:
onkeydown
按鍵被按下
對於onkeydown來說如果一直按着某個按鍵不鬆手,
則事件會一直觸發
當onkeydown連續觸發時,第一次和第二次之間會間隔稍微長一點,
其他的會非常的快這種設計是爲了防止誤操作的發生。
onkeyup
按鍵被鬆開
鍵盤事件一般都會綁定給一些可以獲取到焦點的對象或者是document。