JavaScript高級(jí)程序設(shè)計(jì)(第三版) 第13章

1### Menu

第13章 事 件

13.2 事件處理程序
  • 13.2.1 HTML事件處理程序
  • 13.2.2 DOM0 級(jí)事件處理程序
  • 13.2.3 DOM2 級(jí)事件處理程序
  • 13.2.4 IE事件處理程序
  • 13.2.5 跨瀏覽器的事件處理程序
13.3 事件對(duì)象
  • 13.3.1 DOM中的事件對(duì)象
13.4 事件類型
  • 13.4.1 UI事件
  • 13.4.2 焦點(diǎn)事件
  • 13.4.3 鼠標(biāo)與滾輪事件
  • 13.4.4 鍵盤與文本事件
  • 13.4.6 變動(dòng)事件
  • 13.4.7 HTML5 事件
13.5 內(nèi)存和性能
  • 13.5.1 事件委托
  • 13.5.2 移除事件處理程序
13.6 模擬事件
  • 13.6.1 DOM中的事件模擬

第13章 事 件

13.2 事件處理程序
13.2.1 HTML事件處理程序
<input type="button" value="Click Me" onclick="alert('Clicked')" />
  • 這個(gè)操作是通過指定 onclick 特性并將一些 JavaScript
    代碼作為它的值來定義的。由于這個(gè)值是 JavaScript,因此不能在其中使用未經(jīng)轉(zhuǎn)義的 HTML 語法字符豆混,例如和號(hào)(&)樟插、雙引號(hào)("")验夯、小于號(hào)(<)或大于號(hào)(>)芦圾。為了避免使用 HTML 實(shí)體昔案,這里使用了單引號(hào)乒验。如果想要使用雙引號(hào)愚隧,那么就要將代碼改寫成如下所示:
<input type="button" value="Click Me" onclick="alert(&quot;Clicked&quot;)" />
  • 在 HTML 中定義的事件處理程序可以包含要執(zhí)行的具體動(dòng)作,也可以調(diào)用在頁面其他地方定義的腳本锻全,如下面的例子所示
<script type="text/javascript">
    function showMessage(){
        alert("Hello world!");
    }
</script>
<input type="button" value="Click Me" onclick="showMessage()" />

13.2.2 DOM0 級(jí)事件處理程序
  • 使用 DOM0 級(jí)方法指定的事件處理程序被認(rèn)為是元素的方法狂塘。因此,這時(shí)候的事件處理程序是在
    元素的作用域中運(yùn)行鳄厌;換句話說荞胡,程序中的 this 引用當(dāng)前元素。來看一個(gè)例子了嚎。
<body>
<button id="myBtn">click me11.</button>
<script>
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    this.style.color = "red";   //"myBtn"
    };
    </script>
  • 單擊按鈕泪漂,button的字體會(huì)變成紅色,this就代表當(dāng)前操作的元素歪泳;
  • 也可以刪除通過 DOM0 級(jí)方法指定的事件處理程序萝勤;
btn.onclick = null; //刪除事件處理程序

13.2.3 DOM2 級(jí)事件處理程序
  • “ DOM2 級(jí)事件” 定義了兩個(gè)方法,用于處理指定和刪除事件處理程序的操作: addEventListener()和 removeEventListener()呐伞。所有 DOM 節(jié)點(diǎn)中都包含這兩個(gè)方法敌卓,并且它們都接受 3 個(gè)參數(shù)(“時(shí)間名”,處理事件程序伶氢,一個(gè)布爾值)假哎,最后這個(gè)布爾值參數(shù)如果是 true,表示在捕獲階段調(diào)用事件處理程序鞍历;如果是 false,表示在冒泡階段調(diào)用事件處理程序(布爾值最好填false)肪虎。 代碼如下:
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
  • 使用 DOM2 級(jí)方法添加事件處理程序的主要好處是可以添加多個(gè)事件處理程序劣砍。
// 會(huì)按順序觸發(fā),先alert“mybtn”扇救,再alert“hello world”
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
  • removeEventListener()
    removeEventListener()中的事件處理程序函數(shù)必須與傳入
    addEventListener()中的相同刑枝,如果用匿名函數(shù)的話就無法移除香嗓;
    有效例子如下:
var btn = document.getElementById("myBtn");
var handler = function(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);
//這里省略了其他代碼
btn.removeEventListener("click", handler, false); //有效!
</script>
  • 大多數(shù)情況下装畅,都是將事件處理程序添加到事件流的冒泡階段靠娱,這樣可以最大限度地兼容各種瀏覽器。最好只在需要在事件到達(dá)目標(biāo)之前截獲它的時(shí)候?qū)⑹录幚沓绦蛱砑拥讲东@階段掠兄。如果不是特別需
    要像云,我們不建議在事件捕獲階段注冊事件處理程序。

  • 13.2.5 跨瀏覽器的事件處理程序
  • 第一個(gè)要?jiǎng)?chuàng)建的方法是 addHandler()蚂夕,它的職責(zé)是視情況分別使用 DOM0 級(jí)方法迅诬、 DOM2 級(jí)方法,這個(gè)方法屬于一個(gè)名叫 EventUtil 的對(duì)象婿牍,本書將使用這個(gè)對(duì)象來處理瀏覽器間的差異侈贷。addHandler()方法接受 3 個(gè)參數(shù):要操作的元素、事件名稱和事件處理程序函數(shù)等脂。
  • 這兩個(gè)方法首先都會(huì)檢測傳入的元素中是否存在 DOM2 級(jí)方法俏蛮。如果存在 DOM2 級(jí)方法,則使用該方法:傳入事件類型上遥、事件處理程序函數(shù)和第三個(gè)參數(shù) false(表示冒泡階段)搏屑。注意,為了在 IE8 及更早版本中運(yùn)行露该,此時(shí)的事件類型必須加上"on"前綴睬棚。
    最后一種可能就是使用 DOM0 級(jí)方法(在現(xiàn)代瀏覽器中,應(yīng)該不會(huì)執(zhí)行這里的代碼)解幼。此時(shí)抑党,我們使用的是方括號(hào)語法來將屬性名指定為事件處理程序,或者將屬性設(shè)置為 null撵摆。
var EventUtil = {
    addHandler: function(element, type, handler){
        if (element.addEventListener){
            element.addEventListener(type, handler, false);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler){
        if (element.removeEventListener){
            element.removeEventListener(type, handler, false);
        } else {
            element["on" + type] = null;
        }
    }
};
  • 可以像下面這樣使用 EventUtil 對(duì)象:
var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//這里省略了其他代碼
EventUtil.removeHandler(btn, "click", handler);

13.3 事件對(duì)象
  • 13.3.1 DOM中的事件對(duì)象
  • 兼容 DOM 的瀏覽器會(huì)將一個(gè) event 對(duì)象傳入到事件處理程序中底靠。無論指定事件處理程序時(shí)使用什么方法(DOM0 級(jí)或 DOM2 級(jí)) ,都會(huì)傳入 event 對(duì)象特铝。來看下面的例子暑中。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click"
}, false);
  • event 對(duì)象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法。觸發(fā)的事件類型不一樣鲫剿,可用的屬性和方法也不一樣鳄逾。不過,所有事件都會(huì)有下表列出的成員灵莲。


    event的屬性和方法

    event的屬性和方法
  • 最新的屬性和方法去w3c看雕凹;

  • 在事件處理程序內(nèi)部,對(duì)象 this 始終等于 currentTarget 的值,而 target 則只包含事件的實(shí)際目標(biāo)枚抵。如果直接將事件處理程序指定給了目標(biāo)元素线欲,則 this、 currentTarget 和 target 包含相同的值汽摹。來看下面的例子李丰。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
alert(event.currentTarget === this); //true
alert(event.target === this); //true
};
  • 這個(gè)例子檢測了 currentTarget 和 target 與 this 的值。由于 click 事件的目標(biāo)是按鈕逼泣,因此這三個(gè)值是相等的趴泌。如果事件處理程序存在于按鈕的父節(jié)點(diǎn)中(例如 document.body),那么這些值是不相同的圾旨。再看下面的例子踱讨。
document.body.onclick = function(event){
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("myBtn")); //true
};
  • 在需要通過一個(gè)函數(shù)處理多個(gè)事件時(shí),可以使用 type 屬性砍的。例如:
<button id="myBtn">click me11.</button>
<script>
    var btn = document.getElementById("myBtn");
    var handler = function(event){
        switch(event.type){
            case "click":
                alert("Clicked");
                break;
            case "mouseover":
                event.target.style.backgroundColor = "red";
                break;
            case "mouseout":
                event.target.style.backgroundColor = "";
                break;
        }
    };
    btn.onclick = handler;
    btn.onmouseover = handler;
    btn.onmouseout = handler;
</script>
  • 只有 cancelable 屬性設(shè)置為 true 的事件痹筛,才可以使用 preventDefault()來取消其默認(rèn)行為。
  • 要阻止特定事件的默認(rèn)行為廓鞠,可以使用 preventDefault()方法帚稠。例如,鏈接的默認(rèn)行為就是在被單擊時(shí)會(huì)導(dǎo)航到其 href 特性指定的 URL床佳。如果你想阻止鏈接導(dǎo)航這一默認(rèn)行為滋早,那么通過鏈接的onclick 事件處理程序可以取消它,如下面的例子所示:
var link = document.getElementById("myLink");
link.onclick = function(event){
    event.preventDefault();
};
  • 另外砌们, stopPropagation()方法用于立即停止事件在 DOM 層次中的傳播杆麸,即取消進(jìn)一步的事件
    捕獲或冒泡。例如浪感,直接添加到一個(gè)按鈕的事件處理程序可以調(diào)用 stopPropagation()昔头,從而避免觸
    發(fā)注冊在 document.body 上面的事件處理程序,如下面的例子所示影兽。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert("Clicked");
    // 調(diào)用后揭斧,阻止向上冒泡  body注冊的onclick就不會(huì)運(yùn)行了。
    event.stopPropagation();
};
document.body.onclick = function(event){
    alert("Body clicked");
};
  • 事件對(duì)象的 eventPhase 屬性峻堰,可以用來確定事件當(dāng)前正位于事件流的哪個(gè)階段讹开。如果是在捕獲階段調(diào)用的事件處理程序,那么 eventPhase 等于 1捐名;如果事件處理程序處于目標(biāo)對(duì)象上旦万,則 eventPhase 等于 2;如果是在冒泡階段調(diào)用的事件處理程序镶蹋, eventPhase 等于 3成艘。這里要注意的是拇砰,盡管“處于目標(biāo)”發(fā)生在冒泡階段,但 eventPhase 仍然一直等于 2狰腌。來看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase); //2
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase); //1
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase); //3
};

13.4 事件類型
  • 13.4.1 UI事件
  • UI 事件指的是那些不一定與用戶操作有關(guān)的事件牧氮。這些事件在 DOM 規(guī)范出現(xiàn)之前琼腔,都是以這種或
    那種形式存在的,而在 DOM 規(guī)范中保留是為了向后兼容□飧穑現(xiàn)有的 UI 事件如下丹莲。
    1. load:當(dāng)頁面完全加載后在 window 上面觸發(fā),當(dāng)所有框架都加載完畢時(shí)在框架集上面觸發(fā)尸诽,
      當(dāng)圖像加載完畢時(shí)在<img>元素上面觸發(fā)甥材,或者當(dāng)嵌入的內(nèi)容加載完畢時(shí)在<object>元素上面
      觸發(fā)。
    2. unload 事件:與 load 事件對(duì)應(yīng)的是 unload 事件性含,這個(gè)事件在文檔被完全卸載后觸發(fā)洲赵。只要用戶從一個(gè)頁面切換到另一個(gè)頁面,就會(huì)發(fā)生 unload 事件商蕴。而利用這個(gè)事件最多的情況是清除引用叠萍,以避免內(nèi)存泄漏。
    3. resize 事件:當(dāng)瀏覽器窗口被調(diào)整到一個(gè)新的高度或?qū)挾葧r(shí)绪商,就會(huì)觸發(fā) resize 事件苛谷。這個(gè)事件在 window(窗口)上面觸發(fā),因此可以通過 JavaScript 或者<body>元素中的 onresize 特性來指定事件處理程序格郁。
function myResize(event){
    li.innerHTML = document.documentElement.clientWidth  +" " + document.documentElement.clientHeight    // 顯示現(xiàn)在的窗口clientSize
}
document.body.onresize = myResize;     
  1. scroll 事件:雖然 scroll 事件是在 window 對(duì)象上發(fā)生的腹殿,但它實(shí)際表示的則是頁面中相應(yīng)元素的變化。在混雜模式下例书,可以通過<body>元素的 scrollLeft 和 scrollTop 來監(jiān)控到這一變化锣尉;

  • 13.4.2 焦點(diǎn)事件
  • 焦點(diǎn)事件會(huì)在頁面元素獲得或失去焦點(diǎn)時(shí)觸發(fā)。利用這些事件并與 document.hasFocus()方法及document.activeElement 屬性配合雾叭,可以知曉用戶在頁面上的行蹤悟耘。有以下 6 個(gè)焦點(diǎn)事件。
  • blur:在元素失去焦點(diǎn)時(shí)觸發(fā)织狐。這個(gè)事件不會(huì)冒泡暂幼;所有瀏覽器都支持它。
  • focus:在元素獲得焦點(diǎn)時(shí)觸發(fā)移迫。這個(gè)事件不會(huì)冒泡旺嬉;所有瀏覽器都支持它
  • focusin:在元素獲得焦點(diǎn)時(shí)觸發(fā)。這個(gè)事件與 HTML 事件 focus 等價(jià)厨埋,但它冒泡邪媳。
  • focusout:在元素失去焦點(diǎn)時(shí)觸發(fā)。這個(gè)事件是 HTML 事件 blur 的通用版本。
  • 要確定瀏覽器是否支持這些事件雨效,可以使用如下代碼:
    var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");

  • 13.4.3 鼠標(biāo)與滾輪事件

  • 要檢測瀏覽器是否支持上面的所有事件迅涮,可以使用以下代碼:
    var isSupported = document.implementation.hasFeature("MouseEvent", "3.0")

  • click:在用戶單擊主鼠標(biāo)按鈕(一般是左邊的按鈕)或者按下回車鍵時(shí)觸發(fā)。這一點(diǎn)對(duì)確保
    易訪問性很重要徽龟,意味著 onclick 事件處理程序既可以通過鍵盤也可以通過鼠標(biāo)執(zhí)行叮姑。

  • dblclick:在用戶雙擊主鼠標(biāo)按鈕(一般是左邊的按鈕)時(shí)觸發(fā)。從技術(shù)上說据悔,這個(gè)事件并不
    是 DOM2 級(jí)事件規(guī)范中規(guī)定的传透,但鑒于它得到了廣泛支持,所以 DOM3 級(jí)事件將其納入了標(biāo)準(zhǔn)极颓。

  • mousedown:在用戶按下了任意鼠標(biāo)按鈕時(shí)觸發(fā)朱盐。不能通過鍵盤觸發(fā)這個(gè)事件。

  • mouseenter:在鼠標(biāo)光標(biāo)從元素外部首次移動(dòng)到元素范圍之內(nèi)時(shí)觸發(fā)菠隆。這個(gè)事件不冒泡兵琳,而且
    在光標(biāo)移動(dòng)到后代元素上不會(huì)觸發(fā)。 DOM2 級(jí)事件并沒有定義這個(gè)事件浸赫,但 DOM3 級(jí)事件將它
    納入了規(guī)范闰围。 IE、 Firefox 9+和 Opera 支持這個(gè)事件既峡。

  • mouseleave:在位于元素上方的鼠標(biāo)光標(biāo)移動(dòng)到元素范圍之外時(shí)觸發(fā)羡榴。這個(gè)事件不冒泡,而且
    在光標(biāo)移動(dòng)到后代元素上不會(huì)觸發(fā)运敢。 DOM2 級(jí)事件并沒有定義這個(gè)事件校仑,但 DOM3 級(jí)事件將它
    納入了規(guī)范。 IE传惠、 Firefox 9+和 Opera 支持這個(gè)事件迄沫。

  • mousemove:當(dāng)鼠標(biāo)指針在元素內(nèi)部移動(dòng)時(shí)重復(fù)地觸發(fā)。不能通過鍵盤觸發(fā)這個(gè)事件卦方。

  • mouseout:在鼠標(biāo)指針位于一個(gè)元素上方羊瘩,然后用戶將其移入另一個(gè)元素時(shí)觸發(fā)。又移入的另
    一個(gè)元素可能位于前一個(gè)元素的外部盼砍,也可能是這個(gè)元素的子元素尘吗。不能通過鍵盤觸發(fā)這個(gè)事件。

  • mouseover:在鼠標(biāo)指針位于一個(gè)元素外部浇坐,然后用戶將其首次移入另一個(gè)元素邊界之內(nèi)時(shí)觸
    發(fā)睬捶。不能通過鍵盤觸發(fā)這個(gè)事件。

  • mouseup:在用戶釋放鼠標(biāo)按鈕時(shí)觸發(fā)近刘。不能通過鍵盤觸發(fā)這個(gè)事件擒贸。

    1. 客戶區(qū)坐標(biāo)位置: 鼠標(biāo)事件都是在瀏覽器視口中的特定位置上發(fā)生的臀晃。這個(gè)位置信息保存在事件對(duì)象的 clientX 和 clientY 屬性中。
    1. 頁面坐標(biāo)位置: 通過事件對(duì)象的 pageX 和pageY 屬性介劫,能告訴你事件是在頁面中的什么位置發(fā)生的徽惋。
    1. 屏幕坐標(biāo)位置 : 通過 screenX 和 screenY 屬性就可以確定鼠標(biāo)事件發(fā)生時(shí)鼠標(biāo)指針相對(duì)于整個(gè)屏幕的坐標(biāo)信息。
function myCoor(event){
           li.innerHTML = "client size = "+ event.clientX + " " + event.clientY + "<br>" + 
" page size = " + event.pageX + " " + event.pageY + "<br>" + 
" screen size = " +  event.screenX + " " + event.screenY
}
  1. 修改鍵 : 雖然鼠標(biāo)事件主要是使用鼠標(biāo)來觸發(fā)的座韵,但在按下鼠標(biāo)時(shí)鍵盤上的某些鍵的狀態(tài)也可以影響到所要采取的操作寂曹。這些修改鍵就是 Shift、 Ctrl回右、 Alt 和 Meta(在 Windows 鍵盤中是 Windows 鍵,在蘋果機(jī)中
    是 Cmd 鍵)漱挚,它們經(jīng)常被用來修改鼠標(biāo)事件的行為翔烁。 DOM 為此規(guī)定了 4 個(gè)屬性,表示這些修改鍵的狀
    態(tài): shiftKey旨涝、 ctrlKey蹬屹、 altKey 和 metaKey。這些屬性中包含的都是布爾值白华,如果相應(yīng)的鍵被按
    下了慨默,則值為 true,否則值為 false弧腥。當(dāng)某個(gè)鼠標(biāo)事件發(fā)生時(shí)厦取,通過檢測這幾個(gè)屬性就可以確定用戶
    是否同時(shí)按下了其中的鍵。
function myClick(event){
        li.innerHTML = "ctrl = " + String(event.ctrlKey) + " " + "shift = 
" + String(event.shiftKey)+ " " + "microKey = " + String(event.metaKey)
}
  1. 相關(guān)元素 : 在發(fā)生 mouseover 和 mouserout 事件時(shí)管搪,還會(huì)涉及更多的元素虾攻。這兩個(gè)事件都會(huì)涉及把鼠標(biāo)指
    針從一個(gè)元素的邊界之內(nèi)移動(dòng)到另一個(gè)元素的邊界之內(nèi)。對(duì) mouseover 事件而言更鲁,事件的主目標(biāo)是獲得光標(biāo)的元素霎箍,而相關(guān)元素(event.relatedTarget)就是那個(gè)失去光標(biāo)的元素。類似地澡为,對(duì) mouseout 事件而言漂坏,事件的主目標(biāo)是失去光標(biāo)的元素,而相關(guān)元素(event.relatedTarget)則是獲得光標(biāo)的元素媒至。來看下面的例子顶别。
    <div id="myDivA" style="height:50px">in DIV A</div>
    <div id="myDivB" style="height:50px">in DIV B</div>

    <script>
        function mouseOver(event){
            event.target.style.fontSize = "50px";
            event.relatedTarget.style.fontSize="5px";
        }
        myDivA = document.getElementById("myDivA");
        myDivB = document.getElementById("myDivB");
        myDivA.style.backgroundColor = "yellow";
        myDivB.style.backgroundColor = "orange";
        myDivA.addEventListener("mouseover", mouseOver, false);
        myDivB.addEventListener("mouseover", mouseOver, false);
  • 6. 鼠標(biāo)按鈕
  • 只有在主鼠標(biāo)按鈕被單擊(或鍵盤回車鍵被按下)時(shí)才會(huì)觸發(fā) click 事件,因此檢測按鈕的信息并不是必要的塘慕。但對(duì)于 mousedown 和 mouseup 事件來說筋夏,則在其 event 對(duì)象存在一個(gè) button 屬性,表示按下或釋放的按鈕图呢。
  • A number representing a given button:
    0: Main button pressed, usually the left button or the un-initialized state
    1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
    2: Secondary button pressed, usually the right button
    3: Fourth button, typically the Browser Back button
    4: Fifth button, typically the Browser Forward button
    <button onmousedown="mouseClick(event)">click</button>
    <script>
        function mouseClick(event) {
            alert(event.button);    // event.button 當(dāng)鼠標(biāo)左鍵按下時(shí)条篷,值為0骗随,滾輪為1,右鍵為2赴叹;
        }
  • 7. 更多的事件信息

  • “DOM2 級(jí)事件”規(guī)范在 event 對(duì)象中還提供了 detail 屬性鸿染,用于給出有關(guān)事件的更多信息。對(duì)
    于鼠標(biāo)事件來說乞巧, detail 中包含了一個(gè)數(shù)值涨椒,表示在給定位置上發(fā)生了多少次單擊。(最高只能計(jì)數(shù)到3)绽媒;

  • altLeft:布爾值蚕冬,表示是否按下了 Alt 鍵。如果 altLeft 的值為 true是辕,則 altKey 的值也為 true囤热。

  • ctrlLeft:布爾值,表示是否按下了 Ctrl 鍵。如果 ctrlLeft 的值為 true,則 ctrlKey 的值
    也為 true磕潮。

  • shiftLeft:布爾值,表示是否按下了 Shift 鍵棺聊。如果 shiftLeft 的值為 true,則 shiftKey
    的值也為 true贞谓。

  • offsetX:光標(biāo)相對(duì)于目標(biāo)元素邊界的 x 坐標(biāo)限佩。

  • offsetY:光標(biāo)相對(duì)于目標(biāo)元素邊界的 y 坐標(biāo)。

  • 8. 鼠標(biāo)滾輪事件

  • IE 6.0 首先實(shí)現(xiàn)了 mousewheel 事件裸弦。除了火狐其它都支持犀暑。當(dāng)用戶通過鼠標(biāo)滾輪與頁面交互、在垂直方向上滾動(dòng)頁面時(shí)(無論向上還是向下)烁兰,就會(huì)觸發(fā) mousewheel事件耐亏。當(dāng)用戶向上滾動(dòng)鼠標(biāo)滾輪時(shí),wheelDelta 是 120 的倍數(shù)沪斟;當(dāng)用戶向下滾動(dòng)鼠標(biāo)滾輪時(shí)广辰, wheelDelta 是-120 的倍數(shù)。

  • Firefox 支持一個(gè)名為 DOMMouseScroll 的類似事件主之,也是在鼠標(biāo)滾輪滾動(dòng)時(shí)觸發(fā)择吊。與 mousewheel事件一樣, DOMMouseScroll 也被視為鼠標(biāo)事件槽奕,因而包含與鼠標(biāo)事件有關(guān)的所有屬性几睛。而有關(guān)鼠標(biāo)滾輪的信息則保存在 detail 屬性中,當(dāng)向前滾動(dòng)鼠標(biāo)滾輪時(shí)粤攒,這個(gè)屬性的值是-3 的倍數(shù)所森,當(dāng)向后滾動(dòng)鼠標(biāo)滾輪時(shí)囱持,這個(gè)屬性的值是 3 的倍數(shù)。


  • 13.4.4 鍵盤與文本事件
  • 有 3 個(gè)鍵盤事件:
    • keydown:當(dāng)用戶按下鍵盤上的任意鍵時(shí)觸發(fā)焕济,而且如果按住不放的話纷妆,會(huì)重復(fù)觸發(fā)此事件。
    • keypress:當(dāng)用戶按下鍵盤上的字符鍵時(shí)觸發(fā)晴弃,而且如果按住不放的話掩幢,會(huì)重復(fù)觸發(fā)此事件。
      按下 Esc 鍵也會(huì)觸發(fā)這個(gè)事件上鞠。Safari 3.1 之前的版本也會(huì)在用戶按下非字符鍵時(shí)觸發(fā) keypress
      事件际邻。
    • keyup:當(dāng)用戶釋放鍵盤上的鍵時(shí)觸發(fā)。
    • 雖然所有元素都支持以上 3 個(gè)事件芍阎,但只有在用戶通過文本框輸入文本時(shí)才最常用到枯怖。
  • 1. 鍵碼
    • 在發(fā)生 keydown 和 keyup 事件時(shí), event 對(duì)象的 keyCode 屬性中會(huì)包含一個(gè)代碼能曾,與鍵盤上一個(gè)特定的鍵對(duì)應(yīng)。對(duì)數(shù)字字母字符鍵肿轨, keyCode 屬性的值與 ASCII 碼中對(duì)應(yīng)小寫字母或數(shù)字的編碼相同寿冕。
        input = document.getElementById("myinput");

        function keyPre(event){
            li.innerHTML = event.key     // event.key 返回字符本身
            alert(event.keyCode);     // event.keyCode 返回鍵碼, 
        }
  • 使用 String.fromCharCode()將字符的ASCII碼轉(zhuǎn)換成實(shí)際的字符
    <ul>
        <li id="myLi"></li>
    </ul>
    <input type="text" id="myInput" onkeypress="keyEvent(event)">
    <script>
        li = document.getElementById("myLi");
        function keyEvent(event) {
            cc = event.charCode
            li.innerHTML = cc + " " + String.fromCharCode(cc)
        }
    </script>

  • 13.4.6 變動(dòng)事件
    (DOM3 級(jí)事件模塊作廢了很多變動(dòng)事件, 建議改用MutationObserver構(gòu)造函數(shù)代替椒袍。)
    • DOM2 級(jí)的變動(dòng)(mutation)事件能在 DOM 中的某一部分發(fā)生變化時(shí)給出提示驼唱。
    • DOMSubtreeModified:在 DOM 結(jié)構(gòu)中發(fā)生任何變化時(shí)觸發(fā)。這個(gè)事件在其他任何事件觸發(fā)后都會(huì)觸發(fā)驹暑。
    • DOMNodeInserted:在一個(gè)節(jié)點(diǎn)作為子節(jié)點(diǎn)被插入到另一個(gè)節(jié)點(diǎn)中時(shí)觸發(fā)玫恳。
    • DOMNodeRemoved:在節(jié)點(diǎn)從其父節(jié)點(diǎn)中被移除時(shí)觸發(fā)。
    • DOMNodeInsertedIntoDocument:在一個(gè)節(jié)點(diǎn)被直接插入文檔或通過子樹間接插入文檔之后
      觸發(fā)优俘。這個(gè)事件在 DOMNodeInserted 之后觸發(fā)京办。
    • DOMNodeRemovedFromDocument:在一個(gè)節(jié)點(diǎn)被直接從文檔中移除或通過子樹間接從文檔中移
      除之前觸發(fā)。這個(gè)事件在 DOMNodeRemoved 之后觸發(fā)帆焕。
    • DOMAttrModified:在特性被修改之后觸發(fā)惭婿。
    • DOMCharacterDataModified:在文本節(jié)點(diǎn)的值發(fā)生變化時(shí)觸發(fā)。
    • 使用下列代碼可以檢測出瀏覽器是否支持變動(dòng)事件:
var isSupported = document.implementation.hasFeature("MutationEvents", "2.0");
  • 1. 刪除節(jié)點(diǎn)

    • 在使用removeChild()或replaceChild()從 DOM中刪除節(jié)點(diǎn)時(shí)叶雹,首先會(huì)觸發(fā)DOMNodeRemoved事件财饥。
    • 如果被移除的節(jié)點(diǎn)包含子節(jié)點(diǎn),那么在其所有子節(jié)點(diǎn)以及這個(gè)被移除的節(jié)點(diǎn)上會(huì)相繼觸發(fā)DOMNodeRemovedFromDocument 事件折晦。但這個(gè)事件不會(huì)冒泡钥星,所以只有直接指定給其中一個(gè)子節(jié)點(diǎn)的事件處理程序才會(huì)被調(diào)用。
    • 緊隨其后觸發(fā)的是 DOMSubtreeModified 事件满着。這個(gè)事件的目標(biāo)是被移除節(jié)點(diǎn)的父節(jié)點(diǎn)谦炒;
  • 2. 插入節(jié)點(diǎn)

    • 在使用 appendChild()贯莺、 replaceChild()或 insertBefore()向 DOM 中插入節(jié)點(diǎn)時(shí),首先會(huì)觸發(fā) DOMNodeInserted 事件编饺。
    <button id="myBtn">delete</button>
    <script>
        btn = document.getElementById("myBtn");
        btn.addEventListener("click", delElement);
        function delElement(event){
            img = document.createElement("img");
            img.src = "box.jpg";
            img.style.width = "100px";
            img.style.height = "100px";
            document.body.appendChild(img)
        }
        document.addEventListener("DOMNodeInserted", keyEvent)
        function keyEvent(event) {
            input.value = event.type + " " + event.target;
        }
    </script>

  • 13.4.7 HTML5 事件
  • 1. contextmenu 事件
    • 用以表示何時(shí)應(yīng)該顯示上下文菜單乖篷,以便開發(fā)人員取消默認(rèn)的上下文菜單而提供自定義的菜單。上下文菜單是指在用戶交互(例如右鍵點(diǎn)擊)時(shí)出現(xiàn)的菜單透且。 HTML5允許我們自定義此菜單撕蔼。
<div id="myDiv">Right click or Ctrl+click me to get a custom context menu. Click anywhere else to get the default context menu.</div>
    <ul id="myMenu" style="position:absolute; visibility:hidden; background-color:silver">
        <li><a >Nicholas’ site</a></li>
        <li><a >Wrox site</a></li>
        <li><a >Yahoo!</a></li>
    </ul>
    <script>
        function addContextMenu(event){
            event.preventDefault();              //隱藏默認(rèn)上下文菜單;
            menu = document.getElementById("myMenu");
            menu.style.left = event.clientX + "px";
            menu.style.top = event.clientY + "px";
            menu.style.visibility = "visible";
        }
        div  = document.getElementById("myDiv");
        div.addEventListener("contextmenu", addContextMenu)
        div.addEventListener("click", function (event) {
            menu = document.getElementById("myMenu");
            menu.style.visibility = "hidden";
        })
    </script>
  • 所有瀏覽器都支持 contextmenu 事件秽誊, contextmenu元素只有 Firefox 瀏覽器支持鲸沮。
<div contextmenu="mymenu">右擊這里顯示上下文菜單。
    <menu type="context" id="mymenu">
        <menuitem label="Refresh"></menuitem>
        <menuitem label="Twitter"></menuitem>
    </menu>
</div>
  • 在所有瀏覽器中都可以取消這個(gè)事件:在兼容 DOM 的瀏覽器中锅论,使用 event.preventDefalut()讼溺;在 IE 中,將 event.returnValue 的值設(shè)置為 false最易。
  • 2. beforeunload 事件
    • 可以監(jiān)聽到瀏覽器關(guān)閉操作怒坯,能夠在關(guān)閉之前,彈出一個(gè)對(duì)話框藻懒,讓用戶選擇是否關(guān)閉剔猿。(只有IE支持?)
    • 為了顯示這個(gè)彈出對(duì)話框嬉荆,必須將 event.returnValue 的值設(shè)置為要顯示給用戶的字符串(對(duì)
      IE 及 Fiefox 而言)归敬,同時(shí)作為函數(shù)的值返回(對(duì) Safari 和 Chrome 而言)(?鄙早?汪茧?Chrome和火狐不支持);
function myFunc(event){
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message;
}
window.addEventListener("beforeunload", myFunc)
  • 3. DOMContentLoaded 事件

    • 在形成完整的 DOM 樹之后就會(huì)觸發(fā)限番,不理會(huì)圖像舱污、 JavaScript 文件、 CSS 文件或其他資源是否已經(jīng)下載完畢弥虐。而 load 事件是在頁面中的一切都加載完畢時(shí)觸發(fā)慌闭。
    • 可以為 document 或 window 添加 DOMContentLoaded 事件(盡管
      這個(gè)事件會(huì)冒泡到 window,但它的目標(biāo)實(shí)際上是 document)躯舔。
    • 這個(gè)事件始終都會(huì)在 load 事件之前觸發(fā)驴剔。
  • 4. readystatechange 事件

    • 提供與文檔或元素的加載狀態(tài)有關(guān)的信息。
    • 當(dāng)對(duì)象的readyState屬性值發(fā)生變化時(shí)出發(fā)此事件粥庄。
    • readyState屬性值有:
      • loading:加載(document 仍在加載丧失。)
      • interactive / 互動(dòng)(文檔已經(jīng)完成加載,文檔已被解析惜互,但是諸如圖像布讹,樣式表和框架之類的子資源仍在加載琳拭。)
      • complete / 完成(T文檔和所有子資源已完成加載。狀態(tài)表示 load 事件即將被觸發(fā)描验。 )
document.addEventListener("readystatechange", function(event){
    alert("load completed")
})
  • 對(duì)于 document 而言白嘁,值為"interactive"的 readyState 會(huì)在與 DOMContentLoaded 大致相同的時(shí)刻觸發(fā) readystatechange 事件。
// 以下類似使用DOMContentLoaded 
document.addEventListener("readystatechange", function(event){
    if (document.readyState == "interactive"){
        alert("Content loaded");
    }
})
  • 交互階段可能會(huì)早于也可能會(huì)晚于完成階段出現(xiàn)膘流,無法確保順序絮缅。在包含
    較多外部資源的頁面中,交互階段更有可能早于完成階段出現(xiàn)呼股;而在頁面中包含較少外部資源的情況下耕魄,完成階段先于交互階段出現(xiàn)的可能性更大。因此彭谁,為了盡可能搶到先機(jī)吸奴,有必要同時(shí)檢測交互和完成階段。
document.addEventListener("readystatechange", function(event){
    if (document.readyState == "interactive" || document.readyState == "complete"){
        document.removeEventListener("readystatechange", arguments.callee);
        alert("Content loaded");
    }
})
  • 另外缠局, <script>(在 IE 和 Opera 中)和<link>(僅 IE 中)元素也會(huì)觸發(fā) readystatechange事件则奥,可以用來確定外部的 JavaScript 和 CSS 文件是否已經(jīng)加載完成。

  • 5. pageshow 和 pagehide 事件

    • Firefox 和 Opera 有一個(gè)特性狭园,名叫“往返緩存”(back-forward cache读处,或 bfcache),可以在用戶使用瀏覽器的“后退”和“前進(jìn)”按鈕時(shí)加快頁面的轉(zhuǎn)換速度妙啃。將整個(gè)頁面都保存在了內(nèi)存里。如果頁面位于 bfcache 中俊戳, 再次打開該頁面時(shí)就不會(huì)觸發(fā) load 事件揖赴。但為了更形象地說明 bfcache 的行為, Firefox 還是提供了一些新事件:
    • pageshow:這個(gè)事件在頁面顯示時(shí)觸發(fā)抑胎,無論該頁面是否來自 bfcache燥滑。
      • 在重新加載的頁面中, pageshow 會(huì)在 load 事件觸發(fā)后觸發(fā)阿逃;而對(duì)于 bfcache 中的頁面铭拧, pageshow 會(huì)在頁面狀態(tài)完全恢復(fù)的那一刻觸發(fā)。另外要注意的是恃锉,雖然這個(gè)事件的目標(biāo)是 document搀菩,但必須將其事件處理程序添加到 window。
// 這個(gè)例子使用了私有作用域破托,以防止變量 showCount 進(jìn)入全局作用域肪跋。
(function(event){
    var showCount = 0;
    window.addEventListener("load", function(){
        alert("Load fired");
    });
// 離開之后,又單擊“后退”按鈕返回該頁面土砂,就會(huì)看到 showCount每次遞增的值州既。
    window.addEventListener("pageshow", function(event){
        showCount++;
        alert("Show has been fired " + showCount + " times.");
        alert("Show has been fired " + showCount + " times. Persisted? " + event.persisted);  // event有個(gè)persisted谜洽, 如果頁面被保存在了 bfcache 中,則這個(gè)屬性的值為 true吴叶;
    });
})();
  • pagehide 事件(阐虚??蚌卤?实束??造寝?):該事件會(huì)在瀏覽器卸載頁面的時(shí)候觸發(fā)磕洪,而且是在unload 事件之前觸發(fā)。
    • 這個(gè)事件的 event 對(duì)象也包含 persisted 屬性诫龙,不過其用途稍有不同析显;如果頁面在卸載之后會(huì)被保存在 bfcache 中,那么 persisted 的值也會(huì)被設(shè)置為 true签赃。因此谷异,當(dāng)?shù)谝淮斡|發(fā) pageshow 時(shí), persisted 的值一定是 false锦聊,而在第一次觸發(fā) pagehide 時(shí)歹嘹, persisted 就會(huì)變成 true(除非頁面不會(huì)被保存在 bfcache 中)
        (function(){
            window.addEventListener("pagehide", function(event){
                alert("Hiding. Persisted? " + event.persisted);
            });
        })();
  • **6. hashchange 事件 **???????
    • 在 URL 的參數(shù)列表(及 URL 中“ #”號(hào)后面的所有字符串)
      發(fā)生變化時(shí)觸發(fā);
window.addEventListener("hashchange", function(event){
    alert("Old URL: " + event.oldURL + "\nNew URL: " + event.newURL);
});

13.5 內(nèi)存和性能
  • 13.5.1 事件委托
    • 對(duì)“事件處理程序過多”問題的解決方案就是事件委托。事件委托利用了事件冒泡孔庭,只指定一個(gè)事件處理程序尺上,就可以管理某一類型的所有事件。
    <ul id="myLinks">
        <li id="goSomewhere">Go somewhere</li>
        <li id="doSomething">Do something</li>
        <li id="sayHi">Say hi</li>
    </ul>
    <script>
        var list = document.getElementById("myLinks");
        list.addEventListener("click", function(event){
            switch(event.target.id){
                case "doSomething":
                    document.title = "I changed the document's title";
                    break;
                case "goSomewhere":
                    location.;
                    break;
                case "sayHi":
                    alert("hi");
                    break;
            }
        });
    </script>
  • 如果可行的話圆到,也可以考慮為 document 對(duì)象添加一個(gè)事件處理程序怎抛,用以處理頁面上發(fā)生的某種特定類型的事件。

    • document 對(duì)象很快就可以訪問芽淡,而且可以在頁面生命周期的任何時(shí)點(diǎn)上為它添加事件處理程序(無需等待 DOMContentLoaded 或 load 事件)马绝。換句話說,只要可單擊的元素呈現(xiàn)在頁面上挣菲,就可以立即具備適當(dāng)?shù)墓δ堋?/li>
    • 在頁面中設(shè)置事件處理程序所需的時(shí)間更少富稻。只添加一個(gè)事件處理程序所需的 DOM 引用更少,
      所花的時(shí)間也更少白胀。
    • 整個(gè)頁面占用的內(nèi)存空間更少椭赋,能夠提升整體性能。
  • 最適合采用事件委托技術(shù)的事件包括 click或杠、mousedown纹份、mouseup、keydown、keyup 和 keypress蔓涧。

  • 13.5.2 移除事件處理程序

    • 使用 innerHTML 替換頁面中某一部分的時(shí)候件已, 或使用 removeChild()和 replaceChild()方法時(shí),內(nèi)存中會(huì)留下那些過時(shí)不用的“空事件處理程序”(dangling event handler)元暴,也是造成 Web 應(yīng)用程序內(nèi)存與性能問題的主要原因篷扩。
    • 如果你知道某個(gè)元素即將被移除,那么最好手工移除事件處理程序茉盏,如下面的例子所示鉴未。
<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");btn.onclick = function(){
        //先執(zhí)行某些操作
        btn.onclick = null; //移除事件處理程序
        document.getElementById("myDiv").innerHTML = "Processing...";
    };
</script>
  • 導(dǎo)致“空事件處理程序”的另一種情況,就是卸載頁面的時(shí)候鸠姨。在頁面卸載之前铜秆,先通過 onunload 事件處理程序移除所有事件處理程序。

  • 13.6 模擬事件

  • 13.6.1 DOM中的事件模擬

  • 創(chuàng)建和分派DOM事件讶迁。這些事件通常稱為合成事件连茧,而不是瀏覽器本身觸發(fā)的事件。

    • 創(chuàng)建自定義事件: Events 可以使用構(gòu)造函數(shù)創(chuàng)建巍糯;
//創(chuàng)建自定義事件
var event = new Event('click');
// 添加事件監(jiān)聽
elem.addEventListener('click', function (event) { do something.. }, false);
// 派發(fā)事件
elem.dispatchEvent(event);
  • 添加自定義數(shù)據(jù) – CustomEvent()
  • 使用CustomEvent接口的事件可用于攜帶自定義數(shù)據(jù)啸驯。
  • IE瀏覽器不支持此方法,需要用 var event = document.createEvent('CustomEvent');
<script type="text/javascript">
    function onClick(event){alert(event.detail)}  // 調(diào)用函數(shù)
    div = document.getElementById("myDiv");
    nowDate = new Date(); // 時(shí)間實(shí)例
    // 創(chuàng)建CustomEvent Dict的可用event按鍵取值祟峦,可放任何數(shù)據(jù)罚斗;
    // 還有更多可選參數(shù) 可在MDN里查看
    var myCustomEvent = new CustomEvent('click', {'detail': nowDate.toLocaleDateString()});
    div.addEventListener("click", onClick, false);  // 添加監(jiān)聽;
    div.dispatchEvent(myCustomEvent)   // 向元素派發(fā)事件
</script>

2018/11/7

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宅楞,隨后出現(xiàn)的幾起案子针姿,更是在濱河造成了極大的恐慌,老刑警劉巖厌衙,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件距淫,死亡現(xiàn)場離奇詭異,居然都是意外死亡迅箩,警方通過查閱死者的電腦和手機(jī)溉愁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門处铛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饲趋,“玉大人,你說我怎么就攤上這事撤蟆∞人埽” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵家肯,是天一觀的道長龄砰。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么换棚? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任式镐,我火速辦了婚禮,結(jié)果婚禮上固蚤,老公的妹妹穿的比我還像新娘娘汞。我一直安慰自己,他們只是感情好夕玩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布你弦。 她就那樣靜靜地躺著,像睡著了一般燎孟。 火紅的嫁衣襯著肌膚如雪禽作。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天揩页,我揣著相機(jī)與錄音旷偿,去河邊找鬼。 笑死碍沐,一個(gè)胖子當(dāng)著我的面吹牛狸捅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播累提,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼尘喝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了斋陪?” 一聲冷哼從身側(cè)響起朽褪,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎无虚,沒想到半個(gè)月后缔赠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡友题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年嗤堰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片度宦。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踢匣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戈抄,到底是詐尸還是另有隱情离唬,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布划鸽,位于F島的核電站输莺,受9級(jí)特大地震影響戚哎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫂用,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一型凳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘱函,春花似錦啰脚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亮航,卻和暖如春荸实,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缴淋。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工准给, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人重抖。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓露氮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钟沛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子畔规,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容