讀javascript高級程序設計11-事件

1、事件流

事件流指從頁面中接收事件的順序。jquery

1.事件冒泡(經常使用)瀏覽器

IE中採用的事件流是事件冒泡,先從具體的接收元素,而後逐步向上傳播到不具體的元素。app

Image

2.事件捕獲(少用)函數

Netscapte採用事件捕獲,先由不具體的元素接收事件,最具體的節點最後才接收到事件。性能

Image(1)

3.DOM事件流測試

Image(2)

DOM2級事件包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。this

2、事件處理程序

事件處理程序就是響應某些事件的函數,如onclick等。spa

1. DOM0級事件處理程序3d

每一個元素都有本身的事件處理程序屬性,如onclick等。能夠經過js將一個函數賦值給元素的事件處理程序屬性。指針

在DOM0事件處理程序中,事件處理程序裏的this指向當前元素。

var objlogo=document.getElementById("site_nav_top");
objlogo.onclick=function(){
alert(this.innerHTML);//代碼改變世界
}

刪除DOM0事件,只需將事件處理程序的值賦爲null便可。

objlogo.onclick=null;

2. DOM2級事件處理程序

DOM2有兩個方法用來添加和移除事件處理程序:addEventListener()和removeEventListener()。它們都有三個參數:第一個參數是事件名(如click);第二個參數是事件處理程序函數;第三個參數若是是true則表示在捕獲階段調用,爲false表示在冒泡階段調用。

  • addEventListener():能夠爲元素添加多個事件處理程序,觸發時會按照添加順序依次調用。
  • removeEventListener():不能移除匿名添加的函數。
//不能移除 
var obj = document.getElementById('site_nav_top');
obj.addEventListener('click', function () {
  alert(this.innerHTML);
}, false);
obj.removeEventListener('click', function () {
  alert(this.innerHTML);
}, false);
//能移除 var obj=document.getElementById("site_nav_top"); var show=function(){ alert(this.innerHTML); } obj.addEventListener("click",show,false); obj.removeEventListener("click",show,false);

3.IE事件處理程序

IE的事件處理程序也有兩個相似的方法:attachEvent()和detachEvent()。它們有兩個參數:第一個是事件名(如onlick);第二個參數是事件處理程序的函數。

注意:

  • 事件名前面有on前綴;
  • 在事件處理程序的函數中,this再也不指向當前元素,而是指向window對象。
var obj=document.getElementById("site_nav_top");
var show=function(){
alert(obj.innerHTML);
}
obj.attachEvent("onclick",show); 
obj.detachEvent("onclick",show);

4.跨瀏覽器的事件處理程序

綜合前面幾種狀況,進行瀏覽器能力檢測,就能夠寫出跨瀏覽器的事件處理程序了.

EventUtil = {
  addHandler: function (obj, event, handler) {
    if (obj.addEventListener) {
      obj.addEventListener(event, handler, false);
    } else if (obj.attachEvent) {
      obj.attachEvent('on' + event, handler);
    } else {
      obj['on' + event] = handler;
    }
  },
  removeHandler: function (obj, event, handler) {
    if (obj.removeEventListener) {
      obj.removeEventListener(event, handler, false);
    } else if (obj.detachEvent) {
      obj.detachEvent('on' + event, handler);
    } else {
      obj['on' + event] = null;
    }
  }
};
var obj = document.getElementById('site_nav_top');
var show = function () {
  alert(obj.innerHTML);
}

EventUtil.addHandler(obj, 'click', show);
EventUtil.removeHandler(obj, 'click', show);

3、事件對象

當觸發DOM事件時,會產生一個事件對象event。不一樣瀏覽器對該對象的支持略有不一樣。

1.DOM中的事件對象

在支持DOM0或DOM2的瀏覽器中,會將event對象傳入到事件處理程序中。

var obj=document.getElementById("site_nav_top");
var show=function(event){
alert(event.type);
}
obj.addEventListener("click",show,false);//點擊運行結果 click

event對象包含於建立它的事件對象相關的屬性和方法。幾種經常使用屬性和方法:

① this, currentTarget 與 target:

event.this與event.currentTarget始終相等,表示事件處理程序當前正在處理的元素;

event.target表示事件的實際目標元素。

var obj=document.getElementById("site_nav_top");
var show=function(event){
  console.log(this);
  console.log(event.currentTarget);
  console.log(event.target);
}
obj.addEventListener("click",show,false); 
//點擊頭部「代碼改變世界」結果 <div id="site_nav_top">,<div id="site_nav_top">,<div id="site_nav_top"> 

var show=function(event){
  console.log(this);
  console.log(event.currentTarget);
  console.log(event.target);
}
document.body.addEventListener("click",show,false);
//點擊頭部「代碼改變世界」結果: <body>, <body>, <div id="site_nav_top">

②event.type:被觸發的事件類型,如click。經過判斷該屬性,能夠用一個函數中處理多個事件。

var obj=document.getElementById("nav_menu");
var handler=function(event){
  switch(event.type){
    case "mouseover":
      event.target.style.backgroundColor="red";
      break;
    case "mouseout":
       event.target.style.backgroundColor="";
      break;
    case "click":
      alert("click menu");
      break;
  }
}
obj.addEventListener("click",handler,false);
obj.addEventListener("mouseover",handler,false);
obj.addEventListener("mouseout",handler,false);

③event.preventDefault():阻止事件的默認行爲。

例如在上面的例子中,單擊站點導航條在彈出「click menu」後,仍然打開了連接。若是想阻止其默認行爲,能夠這樣處理:

case "click":
      alert('click menu')
      event.preventDefault();
      break;

④event.stopPropagation():阻止事件進一步冒泡或捕獲。

var obj = document.getElementById('nav_menu');
var handler = function (event) {
  alert('handler');
}
var handler1 = function (event) {
  alert('handler1');
}
obj.addEventListener('click', handler, false);
document.body.addEventListener('click', handler1, false);

在這個例子中給菜單和body分別添加了單擊事件,當點擊菜單時會出現兩次彈窗。若是想阻止事件冒泡,能夠作以下修改:

var handler=function(event){
  alert('handler');
  event.stopPropagation();
}

2. IE中的事件對象

①event對象

使用DOM0添加的事件處理程序,event做爲window對象的屬性存在。

var obj=document.getElementById("nav_menu"); 

obj.onclick=function(event){
alert(window.event.type);
}

使用attachEvent添加的事件處理程序,能夠將event做爲函數的參數傳入,也能夠直接使用window.event。

var obj=document.getElementById("nav_menu");
obj.attachEvent("onclick",function(event){
alert(event.type); 
alert(window.event.type);
});

②event.srcElement:事件的目標元素,等同於DOM中的target屬性。

③event.returnValue:將該屬性設置爲false能夠阻止默認事件。至關於DOM中的preventDefault()。

④event.cancelBubble:將該屬性設爲true能夠阻止事件冒泡。至關於DOM中的 stopPropagation()。

3.跨瀏覽器的事件對象

綜合考慮DOM和IE中的事件對象,寫出跨瀏覽器的事件對象,放在以前的EventUtil中。

EventUtil = {
addHandler: function (obj, event, handler) {
  ......
  },
  removeHandler: function (obj, event, handler) {
......
  },
  getEvent: function (event) {
    return event ? event : window.event;
  },
  getTarget: function (event) {
    return event.target || event.srcElement;
  },
  preventDefault: function (event) {
    if (event.preventDefault) {
      event.preventDefault();
    } else {
      event.returnValue = false;
    }
  },
  stopPropagation: function (event) {
    if (event.stopPropation) {
      event.stopPropation();
    } else {
      event.cancelBubble = true;
    }
  }
};
//以園子首頁菜單欄爲例調用
var obj = document.getElementById('nav_menu');
EventUtil.addHandler(obj, 'click', function (event) {
  var event = EventUtil.getEvent(event);
  var target = EventUtil.getTarget(event);
  alert(target);
  EventUtil.preventDefault(event);
  EventUtil.stopPropagation(event);
});
EventUtil.addHandler(document.body, 'click', function () {
  alert('body');
})

4、事件類型

1.load事件

load能夠用來判斷圖片加載完畢。注意:新建立的圖像元素不是在加載到頁面中才開始下載,而是設置src以後就開始下載。

var obj = document.getElementById('site_nav_top');
EventUtil.addHandler(obj, 'click', function () {
  var img = new Image();
  EventUtil.addHandler(img, 'load', function () {
    alert('load');
  });
  img.src = 'http://static.cnblogs.com/images/logo_small.gif';
  obj.appendChild(img);
})

load能夠用來判斷js加載完成。<script>元素能夠觸發load事件,來判斷動態加載的js文件是否加載完成。它和img不一樣,必須設置了src屬性而且添加到文檔以後纔會開始下載。

EventUtil.addHandler(document.body,"click",function(){
var script=document.createElement("script");
  EventUtil.addHandler(script,"load",function(){
  alert('load');
  });
  script.src="http://common.cnblogs.com/script/jquery.js";
  document.body.appendChild(script);
})

2.resize事件:當瀏覽器的寬度或高度變化時觸發該事件。有些瀏覽器窗口變化1像素時就觸發該事件,有的瀏覽器在中止調整窗口大小時纔出發該事件。所以不要在該方法中寫大量的計算代碼,以避免減緩瀏覽器速度。

3.獲取鼠標位置

  • clientX和clientY:客戶區座標位置。表示鼠標在視口中的水平位置和垂直位置。
  • pageX和pageY:頁面座標位置。表示鼠標在頁面中的位置,從頁面自己而不是視口的左邊和頂部開始計算的。
  • screenX和screenY:屏幕座標位置。鼠標指針相對於整個屏幕的座標位置。
var obj = document.getElementById('headline_block');
EventUtil.addHandler(obj, 'click', function (event) {
  var result = event.clientX + ',' + event.clientY + '\r\n';
  result += event.pageX + ',' + event.pageY+'\r\n';
  result+=event.screenX+','+event.screenY;
  alert(result);
})

4.beforeunload事件

事件在瀏覽器卸載頁面以前觸發,能夠經過它來取消卸載並繼續使用原來的頁面。

要在卸載頁面時顯示彈窗提示,須要將event.returnValue設置爲提示語,而且將該提示語做爲函數的返回值。

EventUtil.addHandler(window,'beforeunload',function(event){
event=EventUtil.getEvent(event);
  var msg='確認關閉嗎';
  event.returnValue=msg;
  return msg;
});

5、內存和性能

1.事件委託

事件委託能夠解決頁面中事件處理程序過多的問題。事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠處理某一類型的全部事件。

EventUtil.addHandler(document.body, 'click', function (event) {
  event = EventUtil.getEvent(event);
  var target = EventUtil.getTarget(event);
  switch (target.id) {
  case 'site_nav_top':
    alert('口號');
    break;
  case 'nav_menu':
    alert('點擊菜單');
    EventUtil.preventDefault(event);
    break;
  case 'editor_pick_lnk':
    alert('推薦區');
    EventUtil.preventDefault(event);
    break;
  }
});

2.移除事件處理程序

若是內存中保留大量無用的事件處理程序,會影響性能。因此必定要在不須要的時候及時移除事件處理程序。尤爲注意如下狀況:

使用innerHTML刪除帶有事件處理程序的元素時,要先將事件處理程序設置爲null。使用委託也能夠解決這個問題,不直接將事件加載會被innerHTML替換的元素,而是將事件賦給其父元素,這樣就能夠避免了。

卸載頁面時,最好手工清除全部的事件處理程序。

6、DOM中的事件模擬

1. DOM中的事件模擬分三步:

  • 使用document.createEvent()建立event對象。經過賦值不一樣的參數,能夠模擬不一樣的事件類型
  • 初始化event對象;
  • 觸發事件,調用dispatchEvent()方法,全部支持事件的DOM節點均可以支持這個方法。

2. 模擬鼠標事件

首先createEvent()方法傳入參數是「MouseEvent」來模擬鼠標事件。

返回的event對象有一個initMouseEvent()方法,用來初始化事件信息。該方法有15個參數:

  • type(字符串):要觸發的事件類型,好比「click」。
  • bubbles(bool):事件是否冒泡。通常設爲true。
  • cancelable(bool):事件是否能夠取消。通常設爲true。
  • view:與事件關聯的視圖,通常設置爲document.defaultView。
  • detail(整數):與事件有關的詳細信息,通常設置爲0.
  • screenX:事件相對於屏幕的X座標。
  • screenY:事件相對於屏幕的Y座標。
  • clientX:事件相對於視口的X座標。
  • clientY:事件相對於視口的Y座標。
  • ctrlKey(bool):是否俺下了Ctrl鍵,默認爲false。
  • altKey:是否按下了alt鍵,默認false。
  • shiftKey:是否按下了shift鍵,默認false。
  • metaKey:是否按下了meta鍵,默認false.
  • button(整數):按下了哪一個鼠標鍵,默認0。
  • relatedTarget:與事件相關的對象,通常爲null.只在模擬mouseover和mouseout的時候會用到。

最後給DOM元素調用dispatchEvent()方法來觸發事件。

//執行位置博客園首頁 
var objmenu = document.getElementById('nav_menu');
objmenu.onclick = function () {
  console.log('menu');
}
document.body.onclick = function () {
  console.log('body');
}
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
for (var i = 0; i < 5; i++) {
  objmenu.dispatchEvent(event);
}

3. 自定義DOM事件

DOM3中支持自定義DOM事件。要建立新的自定義事件,能夠調用document.createEvent()方法,返回的對象有一個initCustomEvent()方法,包含四個參數:

  • type:觸發事件的類型;
  • bubbles:事件是否冒泡;
  • cancelable:事件是否能夠取消;
  • detail:任意值,保存在event.detail屬性中。

最後在DOM元素調用dispatchEvent()方法觸發事件。

var objmenu = document.getElementById('nav_menu');
EventUtil.addHandler(objmenu, 'myevent', function (event) {
  console.log('menu' + event.detail);
});
EventUtil.addHandler(document.body, 'myevent', function (event) {
  console.log('body' + event.detail);
})
if (document.implementation.hasFeature('CustomEvents', '3.0')) {
  var event = document.createEvent('CustomEvent');
  event.initCustomEvent('myevent', true, true, '測試事件detail');
  for (var i = 0; i < 5; i++) {
    objmenu.dispatchEvent(event);
  }
}
相關文章
相關標籤/搜索