事件冒泡
事件冒泡 : 當(dāng)一個(gè)元素接收到事件的時(shí)候,會(huì)把他接收到的所有傳播給他的父級(jí),一直到頂層window.事件冒泡機(jī)制
<style>
div {padding: 40px;}
#div1 {background:red}
#div2 {background:green}
#div3 {background:blue}
</style>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
var oDiv3 = document.getElementById('div3');
function fn1() {
alert( this.id );
}
oDiv1.onclick = fn1;
oDiv2.onclick = fn1;
oDiv3.onclick = fn1;
</script>
點(diǎn)擊div3, div3 div2 div1上的點(diǎn)擊事件都會(huì)被觸發(fā)
事件冒泡與樣式無(wú)關(guān),與html結(jié)構(gòu)相關(guān)
事件對(duì)象
在觸發(fā)DOM上的某個(gè)事件的時(shí)候會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象包含著所有與事件有關(guān)的信息,包括產(chǎn)生事件的元素、事件類型等相關(guān)信息。所有瀏覽器都支持event對(duì)象,但支持方式不同。
事件對(duì)象必須在一個(gè)事件調(diào)用的函數(shù)里面使用才有內(nèi)容
事件函數(shù):事件調(diào)用的函數(shù),一個(gè)函數(shù)是不是事件函數(shù),不在定義的決定,而是取決于這個(gè)調(diào)用的時(shí)候
兼容
ie/chrome : event是一個(gè)內(nèi)置全局對(duì)象
標(biāo)準(zhǔn)下 : 事件對(duì)象是通過(guò)事件函數(shù)的第一個(gè)參數(shù)傳入
如果一個(gè)函數(shù)是被事件調(diào)用的那么,這個(gè)函數(shù)定義的第一個(gè)參數(shù)就是事件對(duì)象
ie/chrome下
function fn(){
alert(event);
}
document.onclick = fn;
標(biāo)準(zhǔn)下:
function fn(ev){
alert(ev);
}
document.onclick = fn;
兼容性寫(xiě)法:
function fn(ev){
var ev = ev || window.enent;
alert(ev);
}
document.onclick = fn;
clientX clientY
clientX 事件屬性返回當(dāng)事件被觸發(fā)時(shí)鼠標(biāo)指針向?qū)τ跒g覽器頁(yè)面(或客戶區(qū))的水平坐標(biāo)。
clientY 事件屬性返回當(dāng)事件被觸發(fā)時(shí)鼠標(biāo)指針向?qū)τ跒g覽器頁(yè)面(或客戶區(qū))的垂直坐標(biāo)。
例如,當(dāng)你點(diǎn)擊客戶端區(qū)域的左上角時(shí),鼠標(biāo)事件的 clientX 值為 0 ,這一值與頁(yè)面是否有水平滾動(dòng)無(wú)關(guān)。
document.onclick = function(e){
var e = e||window.event;
alert(e.clientX +' '+e.clientY);
}
示例: 方塊跟著鼠標(biāo)移動(dòng)
取消冒泡
標(biāo)準(zhǔn)下:
event.stopPropagation()
ie下:
e.cancelBubble = true;
兼容性寫(xiě)法:
function stopPropagation(e) {
if (e.stopPropagation)
e.stopPropagation();
else
e.cancelBubble = true;
}
阻止默認(rèn)行為
標(biāo)準(zhǔn)下:
event.preventDefault();
ie下:
event.returnValue = false;
兼容性寫(xiě)法:
function preventDefault(e) {
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
}
target
Element 只讀 觸發(fā)事件的目標(biāo)元素
function getTarget(e) {
return e.target || e.srcElement;//ie下為srcElement;
}
dom對(duì)象 事件處理函數(shù)
方法一
綁定事件處理函數(shù)
function fn(){
alert(1);
}
div.onclick = fn;
缺點(diǎn): 只能綁定一個(gè)處理函數(shù)
function fn1(){
alert(1);
}
function fn2(){
alert(1);
}
div.onclick = fn1;
div.onclick = fn2;
div.onclick 屬性中的值會(huì)被覆蓋
移除事件綁定函數(shù)
div.onclick = null;
方法二
DOM2級(jí)事件定義了兩個(gè)方法用于處理指定和刪除事件處理程序的操作:
- 綁定事件處理函數(shù): addEventListener
- 移除事件綁定函數(shù): removeEventListener
所有的DOM節(jié)點(diǎn)都包含這兩個(gè)方法,并且它們都接受三個(gè)參數(shù):
- 事件類型
- 事件處理方法
- 布爾參數(shù),如果是true表示在捕獲階段調(diào)用事件處理程序,如果是false,則是在事件冒泡階段處理
綁定事件處理函數(shù)
function fn1(){
alert(1);
}
function fn2(){
alert(2);
}
oDiv.addEventListener('click', fn1, false);
oDiv.addEventListener('click', fn2, false);
移除事件綁定函數(shù)
function fn1() {
alert(1);
}
function fn2() {
alert(2);
}
document.addEventListener('click', fn1, false);
document.addEventListener('click', fn2, false);
document.removeEventListener('click', fn1, false);
IE兼容性
IE并不支持addEventListener和removeEventListener方法,而是實(shí)現(xiàn)了兩個(gè)類似的方法
- attachEvent
- detachEvent
這兩個(gè)方法都接收兩個(gè)相同的參數(shù)
- 事件處理程序名稱
- 事件處理程序方法
由于IE只支持事件冒泡,所以添加的程序會(huì)被添加到冒泡階段,使用attachEvent添加事件處理程序可以如下
<input id="btnClick" type="button" value="Click Here" />
<script type="text/javascript">
var btnClick = document.getElementById('btnClick');
var handler=function() {
alert(this.id);
}
btnClick.attachEvent('onclick', handler);
</script>
結(jié)果是undefined,很奇怪,一會(huì)兒我們會(huì)介紹到
使用attachEvent添加的事件處理程序可以通過(guò)detachEvent移除,條件也是相同的參數(shù),匿名函數(shù)不能被移除。
<input id="btnClick" type="button" value="Click Here" />
<script type="text/javascript">
var btnClick = document.getElementById('btnClick');
var handler=function() {
alert(this.id);
}
btnClick.attachEvent('onclick', handler);
btnClick.detachEvent('onclick', handler);
</script>
跨瀏覽器的事件處理程序
前面內(nèi)容我們可以看到,在不同的瀏覽器下,添加和移除事件處理程序方式不相同,要想寫(xiě)出跨瀏覽器的事件處理程序,首先我們要了解不同的瀏覽器下處理事件處理程序的區(qū)別
在添加事件處理程序事addEventListener和attachEvent主要有幾個(gè)區(qū)別
- 參數(shù)個(gè)數(shù)不相同
這個(gè)最直觀,addEventListener有三個(gè)參數(shù),attachEvent只有兩個(gè),attachEvent添加的事件處理程序只能發(fā)生在冒泡階段,addEventListener第三個(gè)參數(shù)可以決定添加的事件處理程序是在捕獲階段還是冒泡階段處理(我們一般為了瀏覽器兼容性都設(shè)置為冒泡階段)
- 事件名稱不相同
addEventListener下事件名稱沒(méi)有on,比如說(shuō)click,mouseover, attachEvent下有on,比如說(shuō)onclick
onmousevoer
- 事件函數(shù)觸發(fā)時(shí), this的指向不相同
addEventListener的作用域是元素本身,this是指的觸發(fā)元素,而attachEvent事件處理程序會(huì)在全局變量?jī)?nèi)運(yùn)行,this是window,所以剛才例子才會(huì)返回undefined,而不是元素id
- 為一個(gè)事件添加多個(gè)事件處理程序時(shí),執(zhí)行順序不同,
addEventListener添加會(huì)按照添加順序執(zhí)行,而attachEvent添加多個(gè)事件處理程序時(shí)順序無(wú)規(guī)律(添加的方法少的時(shí)候大多是按添加順序的反順序執(zhí)行的,但是添加的多了就無(wú)規(guī)律了),所以添加多個(gè)的時(shí)候,不依賴執(zhí)行順序的還好,若是依賴于函數(shù)執(zhí)行順序,最好自己處理,不要指望瀏覽器
了解了這四點(diǎn)區(qū)別后我們可以嘗試寫(xiě)一個(gè)瀏覽器兼容性比較好的添加事件處理程序方法
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node.attachEvent('on' + type, handler, );
return true;
}
return false;
}
這樣,首先我們解決了第一個(gè)問(wèn)題參數(shù)個(gè)數(shù)不同,現(xiàn)在三個(gè)參數(shù),采用事件冒泡階段觸發(fā)
第二個(gè)問(wèn)題也得以解決,如果是IE,我們給type添加上on
第四個(gè)問(wèn)題目前還沒(méi)有解決方案,需要用戶自己注意,一般情況下,大家也不會(huì)添加很多事件處理程序
試試這個(gè)方法感覺(jué)很不錯(cuò),但是我們沒(méi)有解決第三個(gè)問(wèn)題,由于處理程序作用域不同,如果handler內(nèi)有this之類操作,那么就會(huì)出錯(cuò)。在IE下,實(shí)際上大多數(shù)函數(shù)都會(huì)有this操作
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node.attachEvent('on' + type, function() { handler.call(node); });
return true;
}
return false;
}
這樣處理就可以解決this的問(wèn)題了,但是新的問(wèn)題又來(lái)了,我們這樣等于添加了一個(gè)匿名的事件處理程序,無(wú)法用detachEvent取消事件處理程序,有很多解決方案,我們可以借鑒大師的處理方式,jQuery創(chuàng)始人John Resig是這樣做的
function addEvent(node, type, handler) {
if (!node) return false;
if (node.addEventListener) {
node.addEventListener(type, handler, false);
return true;
}
else if (node.attachEvent) {
node['e' + type + handler] = handler;
node[type + handler] = function() {
node['e' + type + handler](window.event);
};
node.attachEvent('on' + type, node[type + handler]);
return true;
}
return false;
}
在取消事件處理程序的時(shí)候
function removeEvent(node, type, handler) {
if (!node) return false;
if (node.removeEventListener) {
node.removeEventListener(type, handler, false);
return true;
}
else if (node.detachEvent) {
node.detachEvent('on' + type, node[type + handler]);
node[type + handler] = null;
}
return false;
}
事件的捕獲
事件流包括三個(gè)階段,事件捕獲階段,處于目標(biāo)階段,事件冒泡階段,首先發(fā)生的是事件捕獲,為截取事件提供機(jī)會(huì),然后是實(shí)際目標(biāo)接收事件,最后是冒泡階段
<style>
div {padding: 50px;}
#div1 {background: red;}
#div2 {background: blue;}
#div3 {background: green;}
</style>
<div id="div1">
<div id="div2">
<div id="div3"></div>
</div>
</div>
<script>
var oDiv1 = document.getElementById('div1');
var oDiv2 = document.getElementById('div2');
var oDiv3 = document.getElementById('div3');
function fn1() {
alert( this.id );
}
oDiv1.addEventListener('click', fn1, true);
oDiv2.addEventListener('click', fn1, true);
oDiv3.addEventListener('click', fn1, true);
</script>
鍵盤(pán)事件
onkeydown : 當(dāng)鍵盤(pán)按鍵按下的時(shí)候觸發(fā)
onkeyup : 當(dāng)鍵盤(pán)按鍵抬起的時(shí)候觸發(fā)
document.onkeydown = function(ev) {
var ev = ev || event;
alert(ev.keyCode);
}
與鍵盤(pán)事件相關(guān)的事件對(duì)象的屬性值
- event.keyCode : 數(shù)字類型 鍵盤(pán)按鍵的值 鍵值
- event.ctrlKey, event.shiftKey, event.altKey
這三個(gè)鍵 我們通常稱為功能鍵
當(dāng)一個(gè)事件發(fā)生的時(shí)候,如果ctrl || shift || alt 是按下的狀態(tài),相應(yīng)的屬性值返回true,否則返回false
document.onclick = function(ev) {
var ev = ev || event;
alert(ev.ctrlKey);
}
當(dāng)我們點(diǎn)擊時(shí),如果是按住ctrl鍵則彈出true;
示例: 留言板
當(dāng)輸入完成, 并且按住ctrl+enter(回車),添加留言
<input type="text" id="text1" />
<ul id="ul1"></ul>
var oText = document.getElementById('text1');
var oUl = document.getElementById('ul1');
oText.onkeyup = function(ev) {
var ev = ev || event;
//alert(this.value);
if ( this.value != '' ) {
//不能寫(xiě)成ev.keyCode == 13 && ev.keyCode == 17;
//應(yīng)為ev.keyCode不可能同時(shí)為13 和17
if (ev.keyCode == 13 && ev.ctrlKey) {
var oLi = document.createElement('li');
oLi.innerHTML = this.value;
if ( oUl.children[0] ) {
oUl.insertBefore( oLi, oUl.children[0] );
} else {
oUl.appendChild( oLi );
}
}
}
}
不是所有元素都能夠接收鍵盤(pán)事件,能夠響應(yīng)用戶輸入的元素,能夠接收焦點(diǎn)的元素就能夠接收鍵盤(pán)事件
onkeydown : 如果按下不抬起,那么會(huì)連續(xù)觸發(fā)
示例: 移動(dòng)div
oncontextmenu
右鍵菜單事件,當(dāng)右鍵菜單(環(huán)境菜單)顯示出來(lái)的時(shí)候觸發(fā)
document.oncontextmenu = function(){
alert(1);
return false;//阻止默認(rèn)行為
}
例子 彈出自定義右鍵菜單
var oDiv = document.getElementById('div1');
document.oncontextmenu = function(ev) {
var ev = ev || event;
oDiv.style.display = 'block';
oDiv.style.left = ev.clientX + 'px';
oDiv.style.top = ev.clientY + 'px';
return false;
}
document.onclick = function() {
oDiv.style.display = 'none';
}