DOM 事件對象
Event 對象代表事件的狀態(tài),比如事件在其中發(fā)生的元素、鍵盤按鍵的狀態(tài)、鼠標的位置、鼠標按鈕的狀態(tài)。(W3C)
事件對象會在事件被觸發(fā)時獲得,對象包含了當前事件的一些信息,如點擊事件可以獲取到點擊的位置,鍵盤輸入事件可以獲取到按下的鍵。
1. 獲取事件對象
在給 DOM 節(jié)點綁定事件時,需要傳遞一個事件處理器,其本質(zhì)上是個函數(shù),在事件觸發(fā)時被調(diào)用。
在事件處理器被調(diào)用時,默認就會傳遞一個參數(shù),這個參數(shù)就是事件對象。
<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>
輸入一個字符的動作包含按下鍵
和松開鍵
,對應的事件就是 onkeydown
和 onkeyup
,如果使用二級 DOM 事件,則可以不加 on
前綴。
例子中的事件處理器接收了一個參數(shù),這個參數(shù)就是事件對象,參數(shù)名是可以隨意的,一般情況下開發(fā)者會選擇 e
或者 event
作為參數(shù)名。
onkeydown
和 onkeyup
是鍵盤相關的事件,所以可以獲取到按下的鍵是哪個,對應的就是事件對象下的 keyCode
屬性。
keyCode
屬性是按下鍵的 ASCII
碼,如數(shù)字 1 對應的就是 49, 數(shù)字2對應的是 50。具體可以參閱 ASCII 映射表。
2. 常用的事件對象下的屬性和方法
以下內(nèi)容會涉及到事件流相關的內(nèi)容,可以參閱 DOM 事件流章節(jié)。
在符合 DOM2 標準的瀏覽器中,事件對象都具有以下屬性和方法。
2.1 屬性
2.1.1 target
target表示當前事件最終捕獲到的目標。
<div class="a">
我是第一個節(jié)點 a
<div class="b">
我是第二個節(jié)點 b
<div class="c">
我是第三個節(jié)點 c
<div class="d">
我是第四個節(jié)點 d
<div class="e">
我是第五個節(jié)點 e
<div class="f">
我是最里面的一個元素 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é)點上,點擊其子節(jié)點的時候,子節(jié)點就是最終捕獲到的元素。
2.1.2 currentTarget
通過 currentTarget
可以獲取到目前觸發(fā)事件的元素。
<div class="a">
我是第一個節(jié)點 a
<div class="b">
我是第二個節(jié)點 b
<div class="c">
我是第三個節(jié)點 c
<div class="d">
我是第四個節(jié)點 d
<div class="e">
我是第五個節(jié)點 e
<div class="f">
我是最里面的一個元素 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 = [
'當前觸發(fā)事件的元素的類名是:',
e.currentTarget.className,
'。當前捕獲到的元素類名是:',
e.target.className,
].join('');
});
</script>
不論點擊的是哪個子節(jié)點,currentTarget 都是表示當前觸發(fā)事件的節(jié)點。
2.2 方法
2.2.1 stopPropagation
調(dià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>一句簡單的介紹。</p>
<button>點擊我刪除這條</button>
</div>
<div class="item">
<p>兩句簡單的介紹。兩句簡單的介紹。</p>
<button>點擊我刪除這條</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('馬上進入到詳情');
});
});
btns.forEach(function(btn) {
btn.addEventListener('click', function() {
var parent = btn.parentNode;
parent.parentNode.removeChild(parent);
});
});
</script>
上述例子,在點擊按鈕的時候,雖然完成了刪除操作,但還是會彈出一個框,觸發(fā)到了父級的事件,這是冒泡特性導致的,所以需要阻止向上冒泡,
<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>一句簡單的介紹。</p>
<button>點擊我刪除這條</button>
</div>
<div class="item">
<p>兩句簡單的介紹。兩句簡單的介紹。</p>
<button>點擊我刪除這條</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('馬上進入到詳情');
});
});
btns.forEach(function(btn) {
btn.addEventListener('click', function(e) {
// 阻止冒泡
e.stopPropagation();
var parent = btn.parentNode;
parent.parentNode.removeChild(parent);
});
});
</script>
2.2.2 preventDefault
此方法可以取消事件的默認行為,比如超鏈接的點擊,會發(fā)生跳轉(zhuǎn),跳轉(zhuǎn)動作就是默認行為。
給超鏈接綁定點擊事件,調(diào)用事件對象下的 preventDefault
屬性,默認行為就會取消,即不會發(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. 兼容性問題
早期IE下的事件模型與 DOM 標準提供的有些不同。
如事件對象,在 IE8 之前并不是通過傳遞給事件處理器獲取的,而是要通過 window.event
獲取。
<div id="ele">
點我
</div>
<script>
var ele = document.getElementById('ele');
ele.onclick = function(e) {
alert(e); // undefined
alert(window.event);
}
</script>
以下代碼在 IE8 中,第一個 alert 將會返回 undefined,第二個才會是事件對象。
部分事件屬性也不同,如標準中的 target
屬性,在早期 IE 下需要用 srcElement
替代。
建議對兼容性相關內(nèi)容做個了解即可,框架通常會處理好兼容性問題。
4. 小結(jié)
事件對象包含了事件相關的信息,有事件對象,才能對各個事件做更深層次的交互優(yōu)化。