JS學習12(事件)

事件流

IE和Netscape開發團隊提出了完全相反的兩種事件流的概念,事件冒泡流和事件捕獲流。

事件冒泡

事件由最具體的元素開始,逐級向上傳播到較不具體的元素,最終到文檔。

事件捕獲

事件捕獲從document開始,逐級向下,最后傳到最具體的節點。

DOM事件流

DOM2級事件定義的事件流包含3個階段:事件捕獲階段,處于目標階段,事件冒泡階段。
捕獲階段會從文檔節點自上而下傳遞直到目標節點的上一個節點;處于目標階段時傳到目標節點,冒泡階段開始向上傳遞知道文檔節點。
規定是捕獲階段事件不傳遞到目標節點,但是大多數瀏覽器就傳遞到了,這就意味著有2次機會在目標對象上操作事件。

事件處理程序

響應某個事件的函數

HTML事件處理程序

可以使用HTML特性來指定。

<input type="button" value="Click Me" onclick="alert(event.type)">
<input type="button" value="Click Me" onclick="alert(this.value)">
<input type="button" value="hahaah" onclick="clickButton(this)">
function clickButton(element) {
    alert(element.value);
}

這里注意JS代碼中如果出現&""<>要使用轉義字符。
這樣寫事件處理程序是很不靈活的,HTML代碼和JS代碼耦合度太高。
不過這樣寫是有一些方便的地方的,這樣寫的JS語句會被包含在一個動態創建的函數中,這個函數中會存在一個局部變量event事件對象,而且通過this這個變量可以訪問到元素本身。這個函數還會使用with拓展作用域,讓你更方便的直接訪問document及元素本身的成員:

function(){
    with(document){
        with(this){ 
            //你的代碼   
        } 
    }
}
//于是可以直接這樣寫
<input type="button" value="Click Me" onclick="alert(value)">

如果要調用函數,這個函數在JS中要處于全局作用域哦,而且要使用this對象要將this作為參數傳進去,否則是訪問不到的。
這樣寫事件處理程序是有問題的,一個是緊耦合的問題,一個是時間差,如果你的JS文件是放在最下面的,有可能會出現函數已經綁在事件上了可是JS卻還沒給出函數定義,這樣就會報錯,為了避免這樣的情況出現,我們使用try-catch:

<input type="button" value="Click Me" onclick="try{clickButton(this);}catch(ex){}">

不過還是不那么理想是吧。

DOM0級事件處理程序

每個元素都有自己的事件處理程序屬性,以這種方式添加的事件會在事件流的冒泡階段被處理。傳入的處理函數是在元素的作用域中運行。將這個屬性指向空就取消了事件綁定,值得一提的是,如果你使用上面的HTML特性指定事件處理函數,這個屬性里就包含著HTML里你寫的事件函數的代碼,置空也同樣可以取消綁定。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id);    //"myBtn"
};
btn.onclick = null;

DOM2級事件處理程序

IE8及以下不支持DOM2級事件
DOM2級事件定義了兩種方法:addEventListener() removeEventListener()。這兩個方法接受3個參數,要處理的事件名,處理函數,和一個布爾值。這個布爾值代表在捕獲節點調用事件處理程序(true)還是在冒泡過程中調用。
這樣添加意味著可以添加多個事件處理程序,在事件觸發時會按照添加的順序來執行。傳入的處理函數是在元素的作用域中運行。
注銷時要傳入完全相同的函數才能注銷,這就意味著如果你的處理函數是以匿名函數的方式傳遞進去的,那就不可能注銷了哦,因為再傳進去一個匿名函數也不是原先那個了:

var btn = document.getElementById("myButton");
var body = document.body;

//冒泡階段
body.addEventListener("click", function(){
    alert("Hello world!");
}, false);
//捕獲階段
body.addEventListener("click", function(){
    alert("Hello world!");
}, true);
//如果是使用匿名函數注冊的
btn.addEventListener("click", function(){
    alert(this.id + "匿名");
}, false);
//注銷不掉
btn.removeEventListener("click", function(){
    alert(this.id);
}, false);
//這樣就能注銷掉了
var handler = function(){
    alert(this.id + "非匿名");
};
btn.addEventListener("click", handler, false);
btn.removeEventListener("click", handler, false);

IE事件處理程序

IE8及以下不支持DOM2級事件,但是支持兩個類似方法attachEvent()、detachEvent()
只支持冒泡階段
傳入的處理函數是在全局作用域中運行
添加多個事件時觸發順序與添加順序相反
注銷時同樣需要傳入相同的參數,匿名函數無法注銷。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert(this === window); //true
});
var handler = function(){
    alert("Clicked");
};
btn.attachEvent("onclick", handler);
btn.detachEvent("onclick", handler);

跨瀏覽器事件處理程序

優先使用DOM2級的,再不行使用IE專有的,最后使用DOM0級的(一般不可能用到)

var EventUtil = {
    addHandler: function(element, type, handler){
        if (element.addEventListener){
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent){
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler){
        if (element.removeEventListener){
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent){
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        } }
};
var btn = document.getElementById("myButton");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
EventUtil.removeHandler(btn, "click", handler);

事件對象

事件被觸發時會產生一個事件對象,這個對象中包含著所有與事件有關的信息,包括導致事件的元素,事件的類型以及其他各個事件特定的信息。所有瀏覽器都支持event對象,但是實現不同。

DOM中的事件對象

兼容DOM的瀏覽器會將一個event對像傳入到事件處理程序中,三種方式都有

btn.onclick = function(event){ 
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click" 
}, false);
<input type="button" value="Click Me" onclick="alert(event.type)"/>

所有的event對象都會有下面的方法:

屬性/方法 類型 讀寫 說明
bubbles Boolean 只讀 事件是否冒泡
cancelable Boolean 只讀 是否可以取消事件的默認行為
currentTarget Element 只讀 當前正在處理事件的那個元素
defaultPrevented Boolean 只讀 是否已經調用了preventDefault()。DOM3
detail Integer 只讀 與事件相關的細節信息
eventPhase Integer 只讀 觸發事件的階段,1捕獲,2處于目標,3冒泡
preventDefault() Function 只讀 取消事件的默認行為
stopImmediatePropagation() Function 只讀 取消進一步的捕獲或冒泡,同時阻止任何事件處理程序被調用。DOM3
stopPropagation() Function 只讀 取消進一步的捕獲或冒泡
target Element 只讀 事件的目標
trusted Boolean 只讀 true表示是由瀏覽器生成的事件,false說明是JS創建的。DOM3
type String 只讀 事件的類型
view AbstractView 只讀 與事件關聯的抽象視圖,等同于發生事件的window對象

關于currentTarget的解釋
在事件處理程序的內部,this始終指向currentTarget的值,而target則只包含事件的實際目標。

//當事件處理函數就在按鈕上時可能看不出什么區別,但是當在按鈕的父元素上時就不同了
document.body.onclick = function(event){ 
    alert(event.currentTarget === document.body); //true 
    alert(this === document.body); //true 
    alert(event.target === document.getElementById("myBtn")); //true 
};

preventDefault()
比如a標簽的默認行為就是跳轉到URL,使用event.preventDefault()可以阻止。
cancelable是true的事件才可以阻止。

var link = document.getElementById("myLink");
link.onclick = function(event){
    event.preventDefault();
};

IE中的事件對象

event在IE中是window的一個屬性,可以直接獲取

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    var event = window.event;
    alert(event.type);     //"click"
};
btn.attachEvent("onclick", function(event){ 
    alert(event.type); //"click"
});
屬性/方法 類型 讀寫 說明
cancelBubble Boolean 讀寫 默認為false,將其設置為true就可以取消冒泡
returnValue Boolean 讀寫 默認為true,設為false可以取消事件的默認行為
srcElement Element 只讀 與target相同
type String 只讀 事件的類型

要注意在IE中的事件處理程序最好不要使用this,使用event.srcElement比較保險

btn.attachEvent("onclick", function(event){
    alert(event.srcElement === this); //false
});

跨瀏覽器的事件對象

根據瀏覽器之間的區別,在剛才的EventUtil中添加下列方法:

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.stopPropagation){
       event.stopPropagation();
    } else {
        event.cancelBubble = true;
    } 
}

這樣使用~~

var link = document.getElementById("link");
var body = document.body;
var handler = function (event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    EventUtil.preventDefault(event);
    EventUtil.stopPropagation(event);
    alert(target);
    alert(event.target);
}
link.addEventListener("click", handler, false);
body.addEventListener("click", handler, false);

事件類型

DOM3級事件規定了如下幾類事件:

  • UI事件
  • 焦點事件
  • 鼠標事件
  • 滾輪事件
  • 文本事件
  • 鍵盤事件
  • 合成事件
  • 變動事件

除了這幾類事件,HTML5也定義了一組事件,有的瀏覽器還有一些專有事件。

UI事件

  • DOMActivate:表示元素已經被用戶操作,DOM3已經棄用了
  • load:當頁面完全加載后在window上觸發,當所有框架加載完畢時在框架集上觸發,圖像加載完時在img上觸發,當嵌入內容加載完畢時在object元素上觸發
  • unload:load對應的那些,完全卸載后觸發
  • abort:在用戶下載過程中,如果內容沒下載完,在object元素上觸發
  • error:JavaScript出錯時在window上觸發,無法加載圖片時在omg上觸發,有一個或多個框架無法加載時在框架集上觸發
  • select:用戶選擇文本(input,texture)中的一個或多個字符時觸發
  • resize:當窗口或框架大小改變時在window或框架觸發
  • scroll:當用戶滾動帶滾動條的元素時在該元素上觸發,body元素有頁面的滾動條

要確定瀏覽器是否支持:

var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");
var isSupported = document.implementation.hasFeature("UIEvent", "3.0");

load事件
DOM2級事件規范是在document上觸發load事件,不過為了兼容所有瀏覽器都在window上實現了load

EventUtil.addHandler(window, "load", function(){
    var image = document.createElement("img");
    EventUtil.addHandler(image, "load", function(event){
        event = EventUtil.getEvent(event);
        alert(EventUtil.getTarget(event).src);
    });
    //就算不吧img添加到文檔,只要設置了SRC就會開始下載
    //document.body.appendChild(image);
    image.src = "http://www.feng.com/images/icon_01.png";
});

有的瀏覽器在script和link上也會有load事件

unload事件
這個事件在文檔在完全被卸載后觸發,只要用戶從一個頁面切換到另一個頁面就回觸發這個事件。利用這個事件可以清除引用

EventUtil.addHandler(window, "unload", function(event){
    alert("Unloaded");
});

resize事件

EventUtil.addHandler(window, "resize", function(event){
    alert("Resized");
});

瀏覽器可能會在窗口大小改變1px的時候就觸發這個事件,要小心不要在這里放開銷大的方法。
scroll事件
這個事件可以添加到任何有滾動條的元素上。也是會被重復觸發的。頁面的

EventUtil.addHandler(document.getElementById("scrollTest"), "scroll", function(event){
    alert("div Scrolled");
});
EventUtil.addHandler(window, "scroll", function(event){
    alert("window Scrolled");
});

焦點事件

與document.hasFocus()和document.activeElement配合可以知曉用戶在頁面上的行蹤。

  • blur:在元素失去焦點時觸發,不會冒泡
  • DOMFocusIn:元素獲得焦點時觸發,冒泡,DOM3廢棄
  • DOMFocusOut:元素失去焦點,DOM3廢棄
  • focus:元素獲得焦點時觸發,不冒泡
  • focusin:與focus等價,但冒泡,不是所有瀏覽器都支持,較新的都支持
  • focusout:失去焦點,冒泡,較新的支持

當焦點從一個元素移到另一個,依次觸發下面的事件:

  1. focusout
  2. focusin
  3. blur
  4. DOMFocusOut
  5. focus
  6. DOMFocusIn

focusin和focus還是有一些區別的,試試這兩個你就知道啦:

<input type="text" id="textField">
var btn = document.getElementById("textField");
btn.addEventListener("focus",function () {
    alert("button On Focus");
},false);
btn.addEventListener("focusin",function () {
    alert("button On Focus");
},false);

focus事件會在alert被關掉之后,焦點回到input時再次觸發,alert會不停的出現。
focusing就只會alert一次。

鼠標與滾輪事件

DOM3級事件中定義了9個鼠標事件:

  • click:單擊主鼠標按鈕或按下回車鍵時觸發
  • dblclick:雙擊主鼠標按鈕
  • mousedown:用戶按下任意鼠標按鈕
  • mouseup:釋放鼠標按鈕時觸發
  • mouseenter:光標從元素外首次移到元素內觸發。不冒泡。而且在光標移動到后代元素上時不會觸發。
  • mouseleave:移出元素時觸發,不冒泡,移出后代元素時不會觸發。
  • mousemove:在元素內部移動時重復觸發
  • mouseout:指針位于一個元素上方,移入另一個元素時觸發,移入的元素是當前元素的子元素時也會觸發
  • mouseover:移入本元素時觸發,移入本元素的某個子元素時也會觸發
  • mousewheel:跟蹤鼠標滾輪或觸控板滾動

其中click事件的觸發依賴在同一個元素上相繼觸發mousedown,mouse。dblclick則要在同一個元素上觸發兩次click。
客戶區坐標位置
位置信息保存在clientX,clientY。表示鼠標事件發生時鼠標指針在視口中的位置。要注意的是,這個是指針相對于視口的位置,如果你的頁面發生滾動而鼠標沒動,這個坐標不會有變化。

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    alert(event.clientX + "," + event.clientY);
});

頁面坐標位置
這個就真的是在頁面中的位置了pageX,pageY

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    alert("Page coordinates: " + event.pageX + "," + event.pageY);
});

屏幕坐標位置
鼠標相對屏幕的坐標信息screenX,screenY
修改鍵
在鼠標按下的時候可以檢查Shift、Ctrl、Alt、Meta哪個鍵被按下了。由此做一些特定的操作。由event的4個屬性來檢查:shiftKey、ctrlKey、altKey、metaKey。

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    var keys = new Array();
    if (event.shiftKey){
        keys.push("shift");
    }
    if (event.ctrlKey){
        keys.push("ctrl");
    }
    if (event.altKey){
        keys.push("alt");
    }
    if (event.metaKey){
        keys.push("meta");
    }
    alert("Keys: " + keys.join(","));
});

相關元素
mouseover、mouserout這兩個事件是有相關元素的。比如mouseover時你移出的那個元素就是相關元素。可以通過event的relatedTarget屬性來獲得。IE8及以下通過fromElement和toElement來獲得。由于又有兼容問題,再次拓展我們的工具包咯:

getRelatedTarget: function(event){
    if (event.relatedTarget){
        return event.relatedTarget;
    } else if (event.toElement){
        return event.toElement;
    } else if (event.fromElement){
        return event.fromElement;
    } else {
        return null;
    }
}

鼠標按鈕
click只在鼠標主鍵被按下時觸發,所以并不存在檢測按鈕的問題,不過對于mousedown和mouseup來說就不一樣了,在他們的event中有一個button屬性。DOM的button屬性有如下3個值:0主鍵,1滾輪鍵,2次鍵。
IE8之前也有button,不過差別比較大,有8個值:

  • 0:沒有按鈕
  • 1:主
  • 2:次
  • 3:主次同時
  • 4:滾輪
  • 5:主,滾輪同時
  • 6:次,滾輪同時
  • 7:3個同時

是時候拓展工具包了:

getButton: function(event){
    if (document.implementation.hasFeature("MouseEvents", "2.0")){
        return event.button;
    } else {
        switch(event.button){
            case 0:
            case 1:
            case 3:
            case 5:
            case 7:
                return 0;
            case 2:
            case 6:
                return 2;
            case 4:
                return 1;
        } 
    }
}

mousewheel事件
在滾輪滾動時觸發,event有一個特殊的屬性wheelDelta向上滾是負值,向下滾是正值。Opera早期版本相反。

EventUtil.addHandler(document, "mousewheel", function(event){
    event = EventUtil.getEvent(event);
    var delta = (client.engine.opera && client.engine.opera < 9.5 ?
        -event.wheelDelta : event.wheelDelta);
    alert(delta);
});

除firefox外都支持這個事件,firefox支持的是DOMMouseScroll。

EventUtil.addHandler(window, "DOMMouseScroll", function(event){
    event = EventUtil.getEvent(event);
    alert(event.detail);
});

觸摸設備
觸摸設備不支持dblclick事件,雙擊會放大頁面
輕擊可單擊的元素會觸發mousemove事件。如果此操作會導致內容變化,將不再有其他事件發生;如果屏幕沒有因此發生變化,那么會依次發生mousedown、mouseup、click事件
輕擊不可單擊的元素不會觸發任何事件。
可單擊元素是指那些單擊有默認操作的元素,或者你指定了onclick事件處理函數的元素。
mousemove事件也會觸發mouseover和mouseout事件。
兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發mousewheel和scroll事件。
無障礙性問題
無障礙主要就是針對盲人的設計,盲人會使用屏幕閱讀器,而屏幕閱讀器使用鍵盤來觸發事件。
所以盡量使用click事件,因為回車可以直接觸發。
如果一定要使用那些鍵盤默認觸發不了的操作,比如onmouseover、dblclick,應添加鍵盤快捷方式。

鍵盤與文本事件

鍵盤事件:

  • keydown:當用戶按下任意鍵觸發,按住不放重復觸發
  • keypress:當用戶按下鍵盤上字符鍵時觸發,按住不放時重復觸發
  • keyup:當用戶釋放鍵盤上的鍵時觸發

文本事件:

  • textInput:文本插入文本框前會觸發

在用戶按下字符鍵時會觸發keydown,緊接著keypress,如果是在文本框里輸入文字這時會觸發textInput,如果用戶抬起來了按鍵就會觸發keyup,不抬起來會重復觸發keydown,keypress,textInput。
鍵盤事件也支持shiftKey、ctrlKey、altKey、metaKey。
鍵碼
在發生keydown和keyup事件時,event對象的keyCode屬性中會包含一個代碼,數字字母鍵就是ASCII碼。
字符編碼
發生keypress意味著按下的鍵會影響到屏幕中文本的顯示,按下能插入或刪除字符的鍵都會觸發keypress事件。
屬性charCode只有在keypress事件時才包含值,這個值是按下的那個鍵所帶代表字符的ASCII碼,而此時的keyCode可能等于0,也可能等于鍵碼,所以跨瀏覽器要小心:

getCharCode: function(event){
    if (typeof event.charCode == "number"){
        return event.charCode;
    } else {
        return event.keyCode;
    }
}
EventUtil.addHandler(textbox, "keypress", function(event) {
    event = EventUtil.getEvent(event);
    alert(String.fromCharCode(EventUtil.getCharCode(event)));
});

DOM3級變化
DOM3中不再包含charCode,改為key和char。key就是鍵名字符串:“K”、“g”、“Shift”等。char在字符鍵情況下與key相同,非字符鍵時為null。
textInput事件
只有可編輯區域才會觸發textInput。且只有有效的輸入會觸發。這個事件的事件對象里有個data,這個屬性里保存的就是實實在在的輸入數據,你輸入a或A或粘貼進來的asdfasdf在這里都會得到體現。

var textbox = document.getElementById("myText");
EventUtil.addHandler(textbox, "textInput", function(event){ 
    event = EventUtil.getEvent(event);
    alert(event.data);
});

還有一個屬性叫inputMethod,表示把文本輸入到文本框的方式,粘貼,拖放,手寫之類的,只有IE實現了。

復合事件

只有IE9+支持
DOM3級中新添加的事件用來處理IME的輸入序列,IME是輸入法編輯器,可以讓用戶輸入在物理鍵盤上找不到的字符,IME通常需要按住多個鍵最終只輸入一個字符。有3個事件:

  • compositionstart:IME被打開,這是event里的data屬性是正在編輯的文本(比如已經被選中馬上要被替換的文本,正常輸入情況下一般是空)
  • compositionupdate:在向輸入字段插入新字符時觸發,data包含正插入的新字符
  • compositionend:IME關閉,返回正常鍵盤輸入狀態時,data包含此次插入的所有字符。
EventUtil.addHandler(textbox, "compositionstart", function(event){ 
    event = EventUtil.getEvent(event);
    alert(event.data);
});

變動事件

DOM2級的變動事件在DOM中某一部分發生變化時給出提示,跟少用到

  • DOMSubtreeModified
  • DOMNodeInserted
  • DOMNodeRemoved
  • DOMNodeInsertedIntoDocument
  • DOMNodeInserted
  • DOMNodeRemovedFromDocument
  • DOMNodeRemoved
  • DOMAttrModified
  • DOMCharacterDataModified

HTML5事件

contextmenu事件
這個事件在右鍵調出上下文菜單時被觸發,可以通過取消默認事件來彈出自己的右鍵菜單,這個事件冒泡。

EventUtil.addHandler(document, "contextmenu", function(event){
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event);
    var menu = document.getElementById("myMenu");
    menu.style.left = event.pageX + "px";
    menu.style.top = event.pageY + "px";
    menu.style.visibility = "visible";
});

beforeunload事件
這是為了讓開發人員在頁面被卸載前有可能提醒用戶

EventUtil.addHandler(window, "beforeunload", function(event){                           
    event = EventUtil.getEvent(event);
    var message = "I'm really going to miss you if you go.";
    event.returnValue = message;
    return message;
});

DOMContentLoaded事件
這個事件和window的load事件不同,這個不理會各種圖片,JS等文件是否加載完成,只要完整的DOM結構OK了就觸發。
這允許我們盡早的添加事件處理程序和DOM操作等。
這個事件的目標實際上是document,雖然會冒泡到windows

EventUtil.addHandler(document, "DOMContentLoaded", function(event){ 
    alert("Content loaded");
    alert(event.target);//[object HTMLDocument]
});

readystatechange事件
IE、Firfox 4+、Opera。
為某些元素提供的事件,目的是提供與文檔或元素的加載狀態有關的信息,支持這個事件的每個對象都有一個readyState屬性,有5個可能值:uninitialized、loading、loaded、interactive、complete
interactive,complete不一定哪個先出現,所以要快的話兩個一起檢測,為避免執行兩次,檢測到了就注銷事件

EventUtil.addHandler(document, "readystatechange", function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    if (target.readyState == "loaded" || target.readyState == "complete"){
        EventUtil.removeHandler(target, "readystatechange", arguments. callee);
        alert("Content Loaded");
    }
});

pageshow和pagehide
有的瀏覽器有一種特性叫往返緩存,使用戶在使用前進后退按鈕時更快,這個緩存完全保存了整個頁面,包括JS的執行狀態,這就意味著不會再觸發load事件。
pageshow無論這個頁面是新打開的還是在往返緩存中的,都會在這個頁面顯示的時候觸發。新打開的會在load后觸發。
其event對象中有一個persisted屬性,是true時代表是從往返緩存中恢復的。

(function(){
    var showCount = 0;
    EventUtil.addHandler(window, "load", function(){
        alert("Load fired");
    });
    EventUtil.addHandler(window, "pageshow", function(){
        showCount++;
        alert("Show has been fired " + showCount +
            " times. Persisted? " + event.persisted);
    });
})();

pagehide是在unload之前被觸發的。
指定了onunload事件的頁面不會被存在往返緩存中,因為onunload事件一般是為了撤銷在load事件中做的事。
hashchange事件
這個事件在URL參數列表發生改變時觸發,這個事件要添加到window對象

EventUtil.addHandler(window, "hashchange", function(event){
    alert("Current hash: " + location.hash);
});

設備事件

這些事件是針對移動設備的
orientationchange事件
這是Apple在移動版本的Safari中加入的事件,其window.orientation中可能包含3個值:0、90、-90。只要用戶改變了設備的查看模式就會觸發該事件。iOS設備都支持這個事件。

EventUtil.addHandler(window, "orientationchange", function(event){
   alert(window.orientation);
});

MozOrientation事件
這是火狐引入的檢測設備旋轉的事件,其event中有x、y、z三個值,有加速計的設備就可以用

EventUtil.addHandler(window, "MozOrientation", function(event){
    var output = document.getElementById("output");
    output.innerHTML = "X=" + event.x + ", Y=" + event.y + ", Z=" + event.z +"<br>";
});

deviceorientation事件
這個事件也是檢測加速計來獲得設備方向的變化,變化時在window上觸發。設備在平躺時z軸向上。
事件的event對象中有如下5個屬性:

  • alpha:圍繞z軸旋轉時,y軸的度數差,0~360的浮點數
  • beta:圍繞x軸旋轉時,z軸的度數差-180~180的浮點數
  • gamma:圍繞y軸旋轉時,z軸的度數差,-90~90的浮點數
  • absolute:布爾值,設備是否返回一個絕對值
  • compassCalibrated:布爾值,指南針是否校準
EventUtil.addHandler(window, "deviceorientation", function(event){
    alert("Alpha=" + event.alpha + ", Beta=" + event.beta + ", Gamma=" + event.gamma);
});

devicemotion事件
這個事件告訴開發人員設備什么時候移動,其事件對象包含以下屬性:

  • acceleration:一個包含xyz的對象,排除重力每個方向的加速度
  • accelerationIncludingGravity:考慮重力,每個方向的加速度
  • interval:以毫秒表示的時間值,在每次事件中是常量。
  • rotationRate:包含表示方向的alpha、beta、gamma屬性的對象

以上這些值讀不到就是null,用前先檢測

EventUtil.addHandler(window, "devicemotion", function(event){
    var output = document.getElementById("output");
    if (event.rotationRate !== null){
        output.innerHTML += "Alpha=" + event.rotationRate.alpha + ", Beta=" +
            event.rotationRate.beta + ", Gamma=" +
            event.rotationRate.gamma;
    }
});

觸摸與手勢事件

觸摸事件
在iOS和Android上實現了

  • touchstart:每根手指觸摸屏幕時觸發
  • touchmove:手指在屏幕上滑動時連續觸發,調用preventDefault()可以阻止頁面滾動
  • touchend:手指移開
  • touchcancel:系統停止跟蹤觸摸時觸發
    上面幾個事件都會冒泡,也都可以取消。
    event對象中常見的鼠標事件的屬性都有:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey、metaKey。
    還包含三個用于跟蹤觸摸事件的屬性:
  • touches:表示當前跟蹤的觸摸操作的Touch對象的數組
  • targetTouchs:特定于事件目標的Touch對象的數組
  • changeTouches:表示自上次觸摸以來發生什么改變的Touch對象的數組

每個Touch對象有下列屬性:

  • clientX
  • clientY
  • identifier:表示觸摸的唯一ID
  • pageX
  • pageY
  • screenX
  • screenY
  • target
function handleTouchEvent(event){
    if (event.touches.length == 1){
        var output = document.getElementById("output");
        switch(event.type){
            case "touchstart":
                output.innerHTML = "Touch started (" +
                    event.touches[0].clientX +
                    "," + event.touches[0].clientY + ")";
                break;
            case "touchend":
                output.innerHTML += "<br>Touch ended (" +
                    event.changedTouches[0].clientX + "," +
                    event.changedTouches[0].clientY + ")";
                break;
            case "touchmove":
                event.preventDefault(); //
                output.innerHTML += "<br>Touch moved (" +
                    event.changedTouches[0].clientX + "," +
                    event.changedTouches[0].clientY + ")";
                break;
        }
    }
}
EventUtil.addHandler(document, "touchstart", handleTouchEvent);
EventUtil.addHandler(document, "touchend", handleTouchEvent);
EventUtil.addHandler(document, "touchmove", handleTouchEvent);

觸摸屏幕上的元素時,事件發生的順序如下:

  1. touchstart
  2. mouseover
  3. mousemove (單次)
  4. mousedown
  5. mouseup
  6. click
  7. touchend

手勢事件
當兩個手指觸摸屏幕時會產生手勢,iOS中的Safari定義了3個手勢事件:
gesturestart

  • gesturestart:一個手指已在屏幕上另一個手指又觸摸屏幕
  • gesturechange:當觸摸屏幕的任何一個手指發生變化的時候
  • gestureend:任何一個手指移開

事件都冒泡
鼠標事件的標準屬性都有
兩個特殊事件:rotation、scale
rotation表示手指變化引起的旋轉角度,負值表示逆時針
scale表示兩個手指間距離的變化,從1開始變

function handleGestureEvent(event) {
    var output = document.getElementById("output");
    switch (event.type) {
        case "gesturestart":
            output.innerHTML = "Gesture started (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
        case "gestureend":
            output.innerHTML += "<br>Gesture ended (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
        case "gesturechange":
            output.innerHTML += "<br>Gesture changed (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
    }
}

document.addEventListener("gesturestart", handleGestureEvent, false);
document.addEventListener("gestureend", handleGestureEvent, false);
document.addEventListener("gesturechange", handleGestureEvent, false);

內存和性能

在JS中添加到頁面上的事件處理程序數量將直接關系到頁面的整體運行性能。

事件委托

利用事件的冒泡特性,在DOM樹種盡量高的層次上添加一個事件處理程序:

EventUtil.addHandler(document, "click", function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch(target.id){
        case "myButton":
            document.title = "I changed the document's title";
            break;
        case "highDiv":
            location.;
            break;
        case "myDiv":
            alert("hi");
            break;
    }
});

這樣做優點很多

  • 只需要取得一個DOM元素或直接加在document上,快
  • 只添加了一個事件處理函數,內存占用小
  • 如果直接添加在document上,document不需要等待load事件,也就是說按鈕只要出現立即可用

移除事件處理程序

事件與元素的連接越多,頁面就越慢。限制事件數量剛才我們已經做了。另外就是在不需要的時候移除事件處理程序。
當我們移除或替換節點時,原有節點的事件處理程序卻還在,這時最好手工將其事件解綁。如果一開始你的事件處理就像剛才那樣寫在了高層次的元素中,這個問題就不存在啦。
還有就是在我們卸載頁面時,有的瀏覽器處理的并不好,頁面被卸載了但是處理程序還在,來回來回切換打開的過程中內存里就多了很多沒用或重復的事件。我們可以在onunload事件中移除所有事件處理程序。這就體現了將事件處理集中起來的優點——移除時很輕松。

模擬事件

通過JS來觸發本該由瀏覽器觸發的事件

DOM中的事件模擬

鼠標事件

var div = document.getElementById("myDiv");
//先創建一個事件,會返回一個特定的事件對象
var event = document.createEvent("MouseEvents");
//用各種參數初始化事件對象
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
//在目標元素上觸發
div.dispatchEvent(event);

可以模擬下面這些事件UIEvents、MouseEvents、MutationEvents、HTMLEvents。具體的就不寫了大體一致的思路。

IE中的事件模擬

一樣的思路,方法不太一樣

var btn = document.getElementById("myBtn");
var event = document.createEventObject();
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;
btn.fireEvent("onclick", event);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容