1. 程式人生 > >《JavaScript高級程序設計》Chapter 13 事件

《JavaScript高級程序設計》Chapter 13 事件

motion nta 規範化 detach 等價 this指向 記錄 del reat

小記

JS與HTML之間的交互通過事件實現,事件發生在交互的瞬間,可以使用事件監聽器(或者事件處理程序)來預定時間。DOM2事件模塊盡量對事件進行規範,然而DOM3又增加了一些額外的處理方式,再加上BOM和瀏覽器之間的差異性,事件處理有的時候會十分的復雜。但仍然需要了解基本的概念。

導航

  • 事件流的概念 go
  • 事件處理程序(HTML、DOM0、DOM2以及IE的大體運作方式,跨瀏覽器處理) go
  • 事件對象(event。DOM和IE的差別,跨瀏覽器處理)go
  • 事件類型(列舉各種常用事件類型,了解一些常用的類型)go
  • 內存與性能(考慮性能問題和內存占用情況,減少使用事件的數量並及時清除,重點是事件委托)go
  • 模擬事件 go

事件流

  • 紙上畫同心,當指向其中一個圓的時候實際上指向了所有的圓,那麽如何確定指向的順序,同理於事件,因此對時間流進行規範。
  • 事件流用以描述從頁面接受事件的順序。
  • 主流:事件冒泡。IE。向上傳遞事件。
  • 其他:事件捕獲。Netscape。與事件冒泡順序相反,主要是為了捕獲事件。
  • 完整的DOM事件流(DOM2級規定):事件處理有三個階段:事件捕獲、處於目標、事件冒泡階段。如圖:
    技術分享

事件處理程序

  • 事件(click)--響應事件的函數:事件處理程序/事件偵聽器(如onclick)
  • 分為HTML事件處理程序、DOM0事件處理程序、DOM2事件處理程序、IE事件處理程序,因此最後需要討論跨瀏覽器的處理。
  • HTML事件處理程序,將事件處理程序作為元素的屬性,並在其值中執行JS代碼。
    • JS代碼註意一些轉義字符的處理。
    • 事件處理程序的代碼可以訪問全局作用域中的任何代碼。
    • 優點:擴展作用域的方式,可以通過event、this來訪問元素對象,並可以通過表單的name值來訪問其他表單的字段。
    • 缺點:1、時差-可通過try-catch解決;2、不同瀏覽器對其中的作用域鏈處理方式不同;3、平穩退化的問題。
  • DOM0級事件處理。
    • 元素通過其屬性進行事件處理:element.onclick = function(){dosomething();};
    • 是元素的方法,即函數內部的this指向這個元素。
    • 刪除方式: element.onclick = null;
  • DOM2級事件處理
    • addEventListener()/removeEventListener()用於處理和刪除事件
    • 3個參數:待處理事件名、處理的函數、布爾值(true--捕獲階段調用事件處理;false--冒泡階段調用)
    • 作用域:this同樣指向調用的元素。
    • 優點:可以綁定多個事件處理程序。而DOM0中不支持。這些程序按照綁定順序執行。
    • 註意:removeEventListener()中的參數需要與addEventListener()中的完全一樣才能夠有效的解除事件,因此若事件處理函數是匿名函數,則無法處理。
    • 一般不會在事件捕獲階段註冊事件處理程序。
  • IE事件處理程序
    • attachEvent()/detachEvent():2個參數:事件處理程序名(!!與DOM2不同,不是click型而是onclick型)、事件處理程序函數。默認為冒泡階段註冊事件處理程序。
    • 作用域:在全局作用域中運行,this指向window。
    • 同樣可以為一個元素綁定多個事件處理程序。這些程序按照逆序執行。
    • detachEvent()同樣需要傳入一樣的參數,同樣無法處理匿名函數的情況。
  • 跨瀏覽器的事件處理程序
    • 利用JS庫
    • 恰當使用能力檢測:PDF P372。將綁定和解綁函數寫在一個對象中(之後還要添加一系列的跨瀏覽器事件對象的方法和屬性);主要關註冒泡階段。仍有不足,但夠用。

事件對象(event等)

  • 分為DOM中事件對象和IE中對象,因此還考慮了跨瀏覽器的情況。
  • event在事件處理程序執行的過程中才存在。一旦事件處理程序執行完成,event就被銷毀。
  • DOM(無論DOM0還是DOM2)處理事件處理程序的時候都會傳入event對象。
    • 具有一些屬性和方法
    • 註意this、currentTarget和target:在事件處理程序內部,this始終等於currentTarget,指向事件程序註冊的位置,而target指向按鈕。
    • event.type屬性可以確定事件類型:click、mouseover、mouseout等
    • 阻止默認行為:event.preventDefault();(或者return false?)當event.cancelable屬性為true的時候才可以生效。
    • 阻止冒泡或者捕獲階段:event.stopPropagation();
    • 確定事件執行階段:event.eventPhase屬性。
  • IE中的對象
    • 獲取event:window.event或者(當用attachEvent()添加事件orHTML屬性訪問事件的時候)event
    • 有一些屬性和方法,功能等同DOM中的。
    • srcElement代替target
    • 阻止默認行為:returnValue = false,相當於preventDefault。
    • 阻止冒泡:cancelBubble = true 相當於stopPropagation(),不過只能阻止冒泡。
  • 跨瀏覽器的事件對象,完善上一節中的那個對象。見PDF 379頁。

事件類型

  • DOM3級事件模塊在DOM2級事件模塊的基礎上進行更新,具體規定了下列的一些事件
    • UI事件:用戶與頁面上的元素交互時觸發
    • 焦點事件
    • 鼠標事件
    • 滾輪事件
    • 文本事件:在文檔中輸入文本的時候觸發
    • 鍵盤事件
    • 合成事件:當為IME(Input Method Editor)輸入字符時觸發
    • 變動事件:底層DOM結構發生變化的時候觸發
    • 變動名稱事件:廢除了
  • 另外HTML5定義了一組事件
  • 瀏覽器會在BOM和DOM上實現一些專有事件
  • UI事件:多與window對象或者表單控件有關,多在DOM2中被歸為HTML事件。
    var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");
        var isSupported = document.implementation.hasFeature("UIEvent", "3.0");
    • load事件:
      • 兩種定義onload事件處理程序的方式:通過調用JS代碼(可調用上一節中的跨瀏覽器事件對象處理)、在HTML的屬性中處理(對應於window的話需要在<body>元素上指定)。
      • 可在window(document)上指定,實際上,在給其他元素(如img、link、script等)指定onload事件之前一般需要確保window的onload事件已經執行。
      • 一般來說,DOM2級事件要求在<body>元素而非window對象上綁定onload事件,然而所有瀏覽器都在window上實現了onload事件,以便向後兼容。
      • 註意到<img>在指定onload事件的時候,在指定src屬性之前先指定事件。一般來說,新圖像不一定要從添加文檔之後才下載,只要設置了src屬性就會開始下載。(註意image對象的處理)
      • <script><link>元素同樣可以指定onload事件,而他們在指定了src/href屬性添加到文檔之後才會開始下載。一般可以用來確定腳本或者外部樣式表是否加載完畢。
    • unload事件:顧名思義,只要用戶從一個頁面切換到另一個頁面,即觸發。可用於解除引用,防止內存泄漏。onunload事件大體上的使用同onload事件。但註意由於unload是解綁後觸發,而這個時候它綁定的元素或者對象不一定存在了,需要小心處理。
    • resize事件:顧名思義。在window對象或者<body>元素上綁定,因此event.target值會是document。由於各個瀏覽器對其的處理方式不同,可能導致頻繁的重復觸發,所以註意不要傳入過多代碼。
    • sroll事件:在window對象或者<body>元素上綁定,實際上會反應在元素or容器的位置上,可以在混雜模式下通過body元素的scrollLeft/scrollTop監控,或者標準模式下(除了Safari)通過html來進行監控。同樣可能頻繁重復的觸發,所以傳入代碼需謹慎。
  • 焦點事件

    var isSupported = document.implementation.hasFeature("FocusEvent","3.0");
    • 會在頁面獲得或者失去焦點的時候觸發,與document.hasFocus()方法以及document.activeElement屬性配合可以知曉用戶動態。
    • blur/focus:元素失去/獲得焦點,不會冒泡。
    • DOMFocusOut/DOMFocusIn:元素失去/獲得焦點,會冒泡是HTML事件blur/focus的通用版本。Opera使用,DOM3級事件棄用。
    • focusout/focusin:等價於HTML事件blur/focus,會冒泡,相對於上面一條的事件,得到更廣泛的支持。
    • 焦點從頁面中的一個元素移動到另一個元素上,按以下順序觸發:
      • focusout--focusin---blur--DOMFocusOut--focus---DOMFocusIN
      • target明顯,分別是失去焦點的元素以及獲得焦點的元素。
  • 鼠標與滾輪事件
    var isSupported = document.implementation.hasFeature("MouseEvents","2.0");
        var isSupported = document.implementation.hasFeature("MouseEvent","3.0");
    • click(回車)/dbclick,mousedown/mouseup, mouseenter/mouseleave,mouseover/mouseout; mousewheel(滾輪事件)註意到除了click可以用回車觸發之外,其他事件都不能用鍵盤觸發,所以考慮無障礙瀏覽器的時候避免使用到其他的時間。除了mouseenter和mouseleave之外的時間都會冒泡。
    • mousedown/mouseup和click以及dbclick的關系表現為這樣的觸發順序:mousedown->mouseup->click->mousedown->mouseup->dbclick
    • 一些坐標位置:客戶區坐標位置clientX/clientY(鼠標事件觸發的時候,鼠標在視口的坐標信息)、頁面坐標位置pageX/pageY(鼠標事件觸發的時候,鼠標在頁面的坐標信息,實際上pageX = clientX+document.body.scrollLeft/document.documentElement.scrollLeft)、屏幕坐標位置screenX/screenY(相對於整個電腦屏幕的位置信息)。
    • 修改鍵:鼠標事件觸發的時候event.shiftKey/ctrlKey/altKey/metaKey,為布爾值。
    • 相關元素(mouseover和mouseup):概念好理解。屬性even.relatedTarget指向這個相關元素。IE中對於mouseover有fromElement屬性,mouseup有toElement屬性,可以進行跨瀏覽器處理。PDF P392。
    • 鼠標按鈕(mousedown或者mouseup):event.button屬性的0,1,2分別對應主按鈕、滾輪按鈕和次按鈕,IE8中規定了0-7個數字表示,可以將IE模型規範化為DOM方式,進行跨瀏覽器處理。PDF 393。
    • 更多的事件信息:
      • detail屬性:記錄在同一個像素區域中相繼發生一次mousedown和mouseup(單擊)的次數。
      • IE的altLeft、ctrlLeft、offsetX、offsetY、shifitLeft
    • 滾輪事件mousewheel,會冒泡
      • 屬性wheelDelta記錄偏移量:向前為120的倍數、向後為-120的倍數。
      • Opera9.5之前(client.engine.opera && client.engine.opera < 9.5 見客戶端檢測一章),正負號規定相反。
      • Firefox的DOMMouseScroll事件功能相同,然而其detail屬性在向前滾輪的時候為-3的倍數,向後為3的倍數。
      • 綜上,可以進行能力檢測,實現跨瀏覽器應用。見PDF P396。
    • 觸摸設備:觸屏。
  • 鍵盤與文本事件
    • DOM2級並沒有具體定義相關事件,基本上沿用DOM0的,而DOM3定義了一些新的事件。
    • 3個鍵盤事件:keydown(所有鍵)/keypress(字符鍵)/keyup。1個文本事件textInput
    • 按下字符鍵,觸發順序:keydown->keypress->keyup
    • 按以下非字符鍵,觸發順序:keydown -> keyup
    • 如果按住相應的鍵不放,keydown或者keypress會一直觸發。
    • textInput是對keypress的補充,區別有兩個:1、前者只對可編輯區域有效,後者對任意可以獲得焦點的元素有效。2、前者只在輸入確切字符的時候觸發,後者在輸入類似backspace的時候同樣有效。
    • 鍵碼(keydown、keyup事件):event.keyCode屬性,返回對應鍵的ASCII碼,且與shift狀態無關。不同瀏覽器之間會有一些差異。借助String.fromCharCode()可以將獲得的ASCII碼轉換為實際字符。
    • 字符編碼(keypress):event.charCode屬性,只在keypress事件的時候有效,返回對應的字符的鍵碼,此時event.keyCode屬性可能是0或者按鍵鍵碼。在跨瀏覽器處理的時候,一般需要確定是否支持charCode,如果不支持的話就使用keyCode。見pdf P400。
    • DOM3新增的事件、屬性及方法,大多還未普及
      • key/char屬性(不推薦使用):key表示文本字符(k/K)或者鍵名(“Shift”),char在按下的是字符的時候同樣表示文本字符,其他情況下為空。IE9只支持key,Safari 5和Chrome有替代的keyIdentifier屬性對非字符進行同樣處理、字符則按照一定格式返回表示Unicode的字符串。此處需要進行跨瀏覽器處理。見PDF P401。
      • location屬性(不推薦使用):表示按下的鍵在鍵盤上的什麽位置或者屬於哪種外設。Safari和Chrome有keyLoction替代屬性。註意跨瀏覽器處理。
      • 修改鍵:DOM3新增的getModifierState()方法,然而用DOM屬性shiftKey/altKey/ctrlKey/metaKey足夠。
      • textInput事件:與keypress的差異上面提到過。
      • inputMethod屬性:表示將文本輸入到文本框中的方式。用以檢測文本輸入到控件中的方式,以驗證其有效性。
    • 設備中的鍵盤事件:各種外設。觸摸設備。
  • 復合事件(DOM3新增,處理IME輸入,用處不大)
        isSupported = document.implementation.hasFeature("CompositonEvent","3.0");
    • IME輸入:經常用來輸入鍵盤無法直接輸入的字符(如日語等),多數情況下需要多個鍵同時按下以輸入某一個字符(因此“復合”)。
    • 事件:compositionstart/compositionupdate/compositionend
    • 屬性:event.data
  • 變動事件(DOM結構發生變動)
    • 許多事件見名字就知道作用,不再贅述。DOM3廢除了許多,舉兩個刪除和添加節點的時候觸發變動事件的例子。
    • 從DOM結構中刪除節點,觸發順序:
      • 在刪除的節點上觸發DOMNodeRemoved事件:event.target是該節點,event.relatedNode與該節點的parentNode屬性一樣,指向其父節點,冒泡。
      • 在這個節點及其所有子節點上觸發DOMNodeRemovedFromDocument事件,不冒泡(註意一般測試的時候會測試子節點,而非這個節點,以防止該節點從結構中刪除影響綁定事件的發生)。
      • 在其父節點上觸發DOMSubtreeModified事件。
    • 向DOM結構中插入節點,觸發順序:
      • 在該節點上觸發DOMNodeInserted事件,event.target指向該節點,event.relatedNode同樣指向其父節點。冒泡。
      • 在該節點上觸發DOMNodeInsertedIntoDocument事件,不冒泡,因此必須在插入節點之前為它添加這個事件處理程序。
      • 在其父節點上觸發DOMSubtreeModifed事件。冒泡。
    • HTML5事件(規定一些DOM3沒來得及規範的事件)
      • contextmenu事件(處理元素的上下文,在對元素右鍵單擊的時候出現):冒泡。鼠標事件(一般右鍵or Ctrl+單擊)。可以屏蔽(用一般的事件方法)。
      • beforeunload事件:在頁面卸載之前實現一些操作以提供可以阻止用戶離開界面的機會。與event.returnvalue結合使用。
      • DOMContentLoaded事件:與load事件相比,在完成完整的DOM樹之後就會觸發,而不理會JS文件、CSS文件等是否已經下載完畢。意義在於:使用戶較早的與頁面交互。會冒泡。一般都在window(document)上綁定。
      • readystatechange事件(IE):提供與文檔或元素加載狀態相關的信息,一般有readyState屬性,可以表示5個狀態值(以確認對象的存在及初始化、加載情況)。可以應用在許多元素上以檢測其狀況,但是元素不一定必然經歷5個狀態、同時並非所有元素的狀態含義都一樣,需要實際考慮。
      • pageshow和pagehide事件(Firefox、Opera):針對“往返緩存”特性(即加快頁面對“後退”和“前進”按鈕的響應)。理解bfcache。屬性event.persisted表示是否從bfcache中加載得到。
      • haschange事件:URL參數列表(包括#後的所有字符串)發生變動的時候觸發,主要用在Ajax中,用URL參數數列來保存狀態或者導航信息。屬性oldURL、newURL可以實現location.hash的功能。一般使用後者,因為支持oldURL和newURL的瀏覽器不多。
    • 設備事件(手機or平板,具體用到再研究)
      • orientationchange事件 for IOS(瀏覽模式:橫向還是縱向)
      • MozOrientation事件 for Firefox 3.6
      • deviceorientation事件
      • devicemotion事件
    • 觸摸與手勢事件(for 觸屏,具體用到再研究):基本上是在iOS2.0的Safari中引入。

內存和性能

  • JS需要慎重使用事件處理程序的數量,是單進程運行的代碼,會延遲腳本運行事件。同時對象(函數)越多,占用的內存越大。有一些優化手段:
  • 事件委托(解決事件處理程序過多的問題)
    • 利用冒泡程序來解決,例如click會冒泡,那麽只在DOM樹中盡量最高(而不影響功能實現的質量的)層次指定一個處理程序。
    • 甚至直接在document上委托事件。
  • 移除事件處理程序:在不需要的時候移除過時不同的“空事件處理程序”。還可以幫助處理邏輯上的問題。
    • “空事件處理程序”的兩個成因:
      1、從頁面中移除帶有事件處理程序的元素時,這個元素漂浮著而無法收回其事件處理程序和一些引用,而這些仍然保存在內存中。
      2、卸載頁面之前,沒有清理事件處理程序,導致滯留對象數目一再增加。
    • 解決方法
      1、在移除元素前先解除事件處理程序。有效利用事件委托,不直接把事件處理程序添加到需要刪除的元素上,而是委托到高層次元素中。
      2、卸載頁面前,通過onunload事件處理程序移除所有的事件處理程序,要想減輕移除的工作量,可以有效利用事件委托。這麽看來,通過onload添加的東西,需要通過onunload刪除。

模擬事件

  • DOM中:document.createEvent()。傳入相應參數模擬鼠標、鍵盤或者其他事件,甚至可以自定義DOM事件。返回對應的方法或者對象來進行進一步的操作。
  • IE中:模擬任何事件都具有相同的模式:用document.createEventObject()創建一個event對象--為這個event對象添加屬性值和方法進行初始化(一個自定義的過程)---最後通過fireEvent()方法調用這個事件,傳入兩個參數:事件名稱和event對象。

《JavaScript高級程序設計》Chapter 13 事件