DOM 事件對(duì)象
Event 對(duì)象代表事件的狀態(tài),比如事件在其中發(fā)生的元素、鍵盤按鍵的狀態(tài)、鼠標(biāo)的位置、鼠標(biāo)按鈕的狀態(tài)。(W3C)
事件對(duì)象會(huì)在事件被觸發(fā)時(shí)獲得,對(duì)象包含了當(dāng)前事件的一些信息,如點(diǎn)擊事件可以獲取到點(diǎn)擊的位置,鍵盤輸入事件可以獲取到按下的鍵。
1. 獲取事件對(duì)象
在給 DOM 節(jié)點(diǎn)綁定事件時(shí),需要傳遞一個(gè)事件處理器,其本質(zhì)上是個(gè)函數(shù),在事件觸發(fā)時(shí)被調(diào)用。
在事件處理器被調(diào)用時(shí),默認(rèn)就會(huì)傳遞一個(gè)參數(shù),這個(gè)參數(shù)就是事件對(duì)象。
<input type="text" class="input-here">
<div class="log"></div>
<script>
var inputEle = document.querySelector('.input-here');
var logEle = document.querySelector('.log');
inputEle.onkeydown = function(event) {
var ele = document.createElement('p');
ele.innerText = '按下了' + event.keyCode;
logEle.appendChild(ele);
}
inputEle.addEventListener('keyup', function(event) {
var ele = document.createElement('p');
ele.innerText = '彈起了' + event.keyCode;
logEle.appendChild(ele);
});
</script>
輸入一個(gè)字符的動(dòng)作包含按下鍵
和松開(kāi)鍵
,對(duì)應(yīng)的事件就是 onkeydown
和 onkeyup
,如果使用二級(jí) DOM 事件,則可以不加 on
前綴。
例子中的事件處理器接收了一個(gè)參數(shù),這個(gè)參數(shù)就是事件對(duì)象,參數(shù)名是可以隨意的,一般情況下開(kāi)發(fā)者會(huì)選擇 e
或者 event
作為參數(shù)名。
onkeydown
和 onkeyup
是鍵盤相關(guān)的事件,所以可以獲取到按下的鍵是哪個(gè),對(duì)應(yīng)的就是事件對(duì)象下的 keyCode
屬性。
keyCode
屬性是按下鍵的 ASCII
碼,如數(shù)字 1 對(duì)應(yīng)的就是 49, 數(shù)字2對(duì)應(yīng)的是 50。具體可以參閱 ASCII 映射表。
2. 常用的事件對(duì)象下的屬性和方法
以下內(nèi)容會(huì)涉及到事件流相關(guān)的內(nèi)容,可以參閱 DOM 事件流章節(jié)。
在符合 DOM2 標(biāo)準(zhǔn)的瀏覽器中,事件對(duì)象都具有以下屬性和方法。
2.1 屬性
2.1.1 target
target表示當(dāng)前事件最終捕獲到的目標(biāo)。
<div class="a">
我是第一個(gè)節(jié)點(diǎn) a
<div class="b">
我是第二個(gè)節(jié)點(diǎn) b
<div class="c">
我是第三個(gè)節(jié)點(diǎn) c
<div class="d">
我是第四個(gè)節(jié)點(diǎn) d
<div class="e">
我是第五個(gè)節(jié)點(diǎn) e
<div class="f">
我是最里面的一個(gè)元素 f
</div>
</div>
</div>
</div>
</div>
</div>
<div class="result" style="margin-top: 16px;"></div>
<script>
var resultEle = document.querySelector('.result');
document.querySelector('.a').addEventListener('click', function(e) {
resultEle.innerText = '捕獲到的元素類名是' + e.target.className;
});
</script>
可以看到事件綁定在類名為 a
的節(jié)點(diǎn)上,點(diǎn)擊其子節(jié)點(diǎn)的時(shí)候,子節(jié)點(diǎn)就是最終捕獲到的元素。
2.1.2 currentTarget
通過(guò) currentTarget
可以獲取到目前觸發(fā)事件的元素。
<div class="a">
我是第一個(gè)節(jié)點(diǎn) a
<div class="b">
我是第二個(gè)節(jié)點(diǎn) b
<div class="c">
我是第三個(gè)節(jié)點(diǎn) c
<div class="d">
我是第四個(gè)節(jié)點(diǎn) d
<div class="e">
我是第五個(gè)節(jié)點(diǎn) e
<div class="f">
我是最里面的一個(gè)元素 f
</div>
</div>
</div>
</div>
</div>
</div>
<div class="result" style="margin-top: 16px;"></div>
<script>
var resultEle = document.querySelector('.result');
document.querySelector('.a').addEventListener('click', function(e) {
resultEle.innerText = [
'當(dāng)前觸發(fā)事件的元素的類名是:',
e.currentTarget.className,
'。當(dāng)前捕獲到的元素類名是:',
e.target.className,
].join('');
});
</script>
不論點(diǎn)擊的是哪個(gè)子節(jié)點(diǎn),currentTarget 都是表示當(dāng)前觸發(fā)事件的節(jié)點(diǎn)。
2.2 方法
2.2.1 stopPropagation
調(diào)用此方法就會(huì)阻止事件的冒泡,使用到的場(chǎng)景大多為某個(gè)父元素和元素本身綁定了相同事件時(shí)。
<style>
.list {
width: 300px;
margin: 0 auto;
}
.list .item {
width: 100%;
border: 1px dashed #4caf50;
border-bottom: 0;
border-radius: 2px;
padding: 16px;
}
.list .item:last-child {
border-bottom: 1px dashed #4caf50;
}
.list .item button {
border-radius: 2px;
font-size: 14px;
color: #666;
outline: none;
}
.list .item button:active {
background: #eee;
}
</style>
<div class="list">
<div class="item">
<p>一句簡(jiǎn)單的介紹。</p>
<button>點(diǎn)擊我刪除這條</button>
</div>
<div class="item">
<p>兩句簡(jiǎn)單的介紹。兩句簡(jiǎn)單的介紹。</p>
<button>點(diǎn)擊我刪除這條</button>
</div>
</div>
<script>
var items = document.querySelectorAll('.list .item');
var btns = document.querySelectorAll('.list .item button');
items.forEach(function(item) {
item.addEventListener('click', function() {
alert('馬上進(jìn)入到詳情');
});
});
btns.forEach(function(btn) {
btn.addEventListener('click', function() {
var parent = btn.parentNode;
parent.parentNode.removeChild(parent);
});
});
</script>
上述例子,在點(diǎn)擊按鈕的時(shí)候,雖然完成了刪除操作,但還是會(huì)彈出一個(gè)框,觸發(fā)到了父級(jí)的事件,這是冒泡特性導(dǎo)致的,所以需要阻止向上冒泡,
<style>
.list {
width: 300px;
margin: 0 auto;
}
.list .item {
width: 100%;
border: 1px dashed #4caf50;
border-bottom: 0;
border-radius: 2px;
padding: 16px;
}
.list .item:last-child {
border-bottom: 1px dashed #4caf50;
}
.list .item button {
border-radius: 2px;
font-size: 14px;
color: #666;
outline: none;
}
.list .item button:active {
background: #eee;
}
</style>
<div class="list">
<div class="item">
<p>一句簡(jiǎn)單的介紹。</p>
<button>點(diǎn)擊我刪除這條</button>
</div>
<div class="item">
<p>兩句簡(jiǎn)單的介紹。兩句簡(jiǎn)單的介紹。</p>
<button>點(diǎn)擊我刪除這條</button>
</div>
</div>
<script>
var items = document.querySelectorAll('.list .item');
var btns = document.querySelectorAll('.list .item button');
items.forEach(function(item) {
item.addEventListener('click', function() {
alert('馬上進(jìn)入到詳情');
});
});
btns.forEach(function(btn) {
btn.addEventListener('click', function(e) {
// 阻止冒泡
e.stopPropagation();
var parent = btn.parentNode;
parent.parentNode.removeChild(parent);
});
});
</script>
2.2.2 preventDefault
此方法可以取消事件的默認(rèn)行為,比如超鏈接的點(diǎn)擊,會(huì)發(fā)生跳轉(zhuǎn),跳轉(zhuǎn)動(dòng)作就是默認(rèn)行為。
給超鏈接綁定點(diǎn)擊事件,調(diào)用事件對(duì)象下的 preventDefault
屬性,默認(rèn)行為就會(huì)取消,即不會(huì)發(fā)生跳轉(zhuǎn)。
<a href="https://imooc.com">沖鴨!!前往慕課網(wǎng)??!</a>
<script>
var aTag = document.querySelector('a');
aTag.onclick = function(e) {
e.preventDefault();
alert('跳轉(zhuǎn)被終止!');
};
</script>
3. 兼容性問(wèn)題
早期IE下的事件模型與 DOM 標(biāo)準(zhǔn)提供的有些不同。
如事件對(duì)象,在 IE8 之前并不是通過(guò)傳遞給事件處理器獲取的,而是要通過(guò) window.event
獲取。
<div id="ele">
點(diǎn)我
</div>
<script>
var ele = document.getElementById('ele');
ele.onclick = function(e) {
alert(e); // undefined
alert(window.event);
}
</script>
以下代碼在 IE8 中,第一個(gè) alert 將會(huì)返回 undefined,第二個(gè)才會(huì)是事件對(duì)象。
部分事件屬性也不同,如標(biāo)準(zhǔn)中的 target
屬性,在早期 IE 下需要用 srcElement
替代。
建議對(duì)兼容性相關(guān)內(nèi)容做個(gè)了解即可,框架通常會(huì)處理好兼容性問(wèn)題。
4. 小結(jié)
事件對(duì)象包含了事件相關(guān)的信息,有事件對(duì)象,才能對(duì)各個(gè)事件做更深層次的交互優(yōu)化。