事件冒泡
事件冒泡 : 當一個元素接收到事件的時候,會把他接收到的所有傳播給他的父級,一直到頂層window.事件冒泡機制
<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>
點擊div3, div3 div2 div1上的點擊事件都會被觸發
事件冒泡與樣式無關,與html結構相關
事件對象
在觸發DOM上的某個事件的時候會產生一個事件對象event,這個對象包含著所有與事件有關的信息,包括產生事件的元素、事件類型等相關信息。所有瀏覽器都支持event對象,但支持方式不同。
事件對象必須在一個事件調用的函數里面使用才有內容
事件函數:事件調用的函數,一個函數是不是事件函數,不在定義的決定,而是取決于這個調用的時候
兼容
ie/chrome : event是一個內置全局對象
標準下 : 事件對象是通過事件函數的第一個參數傳入
如果一個函數是被事件調用的那么,這個函數定義的第一個參數就是事件對象
ie/chrome下
function fn(){
alert(event);
}
document.onclick = fn;
標準下:
function fn(ev){
alert(ev);
}
document.onclick = fn;
兼容性寫法:
function fn(ev){
var ev = ev || window.enent;
alert(ev);
}
document.onclick = fn;
clientX clientY
clientX 事件屬性返回當事件被觸發時鼠標指針向對于瀏覽器頁面(或客戶區)的水平坐標。
clientY 事件屬性返回當事件被觸發時鼠標指針向對于瀏覽器頁面(或客戶區)的垂直坐標。
例如,當你點擊客戶端區域的左上角時,鼠標事件的 clientX 值為 0 ,這一值與頁面是否有水平滾動無關。
document.onclick = function(e){
var e = e||window.event;
alert(e.clientX +' '+e.clientY);
}
示例: 方塊跟著鼠標移動
取消冒泡
標準下:
event.stopPropagation()
ie下:
e.cancelBubble = true;
兼容性寫法:
function stopPropagation(e) {
if (e.stopPropagation)
e.stopPropagation();
else
e.cancelBubble = true;
}
阻止默認行為
標準下:
event.preventDefault();
ie下:
event.returnValue = false;
兼容性寫法:
function preventDefault(e) {
if (e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
}
target
Element 只讀 觸發事件的目標元素
function getTarget(e) {
return e.target || e.srcElement;//ie下為srcElement;
}
dom對象 事件處理函數
方法一
綁定事件處理函數
function fn(){
alert(1);
}
div.onclick = fn;
缺點: 只能綁定一個處理函數
function fn1(){
alert(1);
}
function fn2(){
alert(1);
}
div.onclick = fn1;
div.onclick = fn2;
div.onclick 屬性中的值會被覆蓋
移除事件綁定函數
div.onclick = null;
方法二
DOM2級事件定義了兩個方法用于處理指定和刪除事件處理程序的操作:
- 綁定事件處理函數: addEventListener
- 移除事件綁定函數: removeEventListener
所有的DOM節點都包含這兩個方法,并且它們都接受三個參數:
- 事件類型
- 事件處理方法
- 布爾參數,如果是true表示在捕獲階段調用事件處理程序,如果是false,則是在事件冒泡階段處理
綁定事件處理函數
function fn1(){
alert(1);
}
function fn2(){
alert(2);
}
oDiv.addEventListener('click', fn1, false);
oDiv.addEventListener('click', fn2, false);
移除事件綁定函數
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方法,而是實現了兩個類似的方法
- attachEvent
- detachEvent
這兩個方法都接收兩個相同的參數
- 事件處理程序名稱
- 事件處理程序方法
由于IE只支持事件冒泡,所以添加的程序會被添加到冒泡階段,使用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>
結果是undefined,很奇怪,一會兒我們會介紹到
使用attachEvent添加的事件處理程序可以通過detachEvent移除,條件也是相同的參數,匿名函數不能被移除。
<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>
跨瀏覽器的事件處理程序
前面內容我們可以看到,在不同的瀏覽器下,添加和移除事件處理程序方式不相同,要想寫出跨瀏覽器的事件處理程序,首先我們要了解不同的瀏覽器下處理事件處理程序的區別
在添加事件處理程序事addEventListener和attachEvent主要有幾個區別
- 參數個數不相同
這個最直觀,addEventListener有三個參數,attachEvent只有兩個,attachEvent添加的事件處理程序只能發生在冒泡階段,addEventListener第三個參數可以決定添加的事件處理程序是在捕獲階段還是冒泡階段處理(我們一般為了瀏覽器兼容性都設置為冒泡階段)
- 事件名稱不相同
addEventListener下事件名稱沒有on
,比如說click
,mouseover
, attachEvent下有on
,比如說onclick
onmousevoer
- 事件函數觸發時, this的指向不相同
addEventListener的作用域是元素本身,this是指的觸發元素,而attachEvent事件處理程序會在全局變量內運行,this是window,所以剛才例子才會返回undefined,而不是元素id
- 為一個事件添加多個事件處理程序時,執行順序不同,
addEventListener添加會按照添加順序執行,而attachEvent添加多個事件處理程序時順序無規律(添加的方法少的時候大多是按添加順序的反順序執行的,但是添加的多了就無規律了),所以添加多個的時候,不依賴執行順序的還好,若是依賴于函數執行順序,最好自己處理,不要指望瀏覽器
了解了這四點區別后我們可以嘗試寫一個瀏覽器兼容性比較好的添加事件處理程序方法
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;
}
這樣,首先我們解決了第一個問題參數個數不同,現在三個參數,采用事件冒泡階段觸發
第二個問題也得以解決,如果是IE,我們給type添加上on
第四個問題目前還沒有解決方案,需要用戶自己注意,一般情況下,大家也不會添加很多事件處理程序
試試這個方法感覺很不錯,但是我們沒有解決第三個問題,由于處理程序作用域不同,如果handler內有this之類操作,那么就會出錯。在IE下,實際上大多數函數都會有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的問題了,但是新的問題又來了,我們這樣等于添加了一個匿名的事件處理程序,無法用detachEvent取消事件處理程序,有很多解決方案,我們可以借鑒大師的處理方式,jQuery創始人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;
}
在取消事件處理程序的時候
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;
}
事件的捕獲
事件流包括三個階段,事件捕獲階段,處于目標階段,事件冒泡階段,首先發生的是事件捕獲,為截取事件提供機會,然后是實際目標接收事件,最后是冒泡階段
<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>
鍵盤事件
onkeydown : 當鍵盤按鍵按下的時候觸發
onkeyup : 當鍵盤按鍵抬起的時候觸發
document.onkeydown = function(ev) {
var ev = ev || event;
alert(ev.keyCode);
}
與鍵盤事件相關的事件對象的屬性值
- event.keyCode : 數字類型 鍵盤按鍵的值 鍵值
- event.ctrlKey, event.shiftKey, event.altKey
這三個鍵 我們通常稱為功能鍵
當一個事件發生的時候,如果ctrl || shift || alt 是按下的狀態,相應的屬性值返回true,否則返回false
document.onclick = function(ev) {
var ev = ev || event;
alert(ev.ctrlKey);
}
當我們點擊時,如果是按住ctrl
鍵則彈出true;
示例: 留言板
當輸入完成, 并且按住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 != '' ) {
//不能寫成ev.keyCode == 13 && ev.keyCode == 17;
//應為ev.keyCode不可能同時為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 );
}
}
}
}
不是所有元素都能夠接收鍵盤事件,能夠響應用戶輸入的元素,能夠接收焦點的元素就能夠接收鍵盤事件
onkeydown : 如果按下不抬起,那么會連續觸發
示例: 移動div
oncontextmenu
右鍵菜單事件,當右鍵菜單(環境菜單)顯示出來的時候觸發
document.oncontextmenu = function(){
alert(1);
return false;//阻止默認行為
}
例子 彈出自定義右鍵菜單
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';
}