過去,在網路上指向某個項目很簡單。你有滑鼠,你移動滑鼠,有時按下按鈕,就這樣而已。所有非滑鼠的裝置都會模擬成滑鼠,開發人員也能確切掌握要依賴的裝置。
不過,簡單不一定是好事。隨著時間推移,我們發現並非所有裝置都需要 (或假裝成) 滑鼠,因此使用壓力感應和傾斜感應筆,可讓你盡情發揮創意;你也可以使用手指,只要有裝置和手就行;不過,為何不使用多個手指呢?
我們已經有了觸控事件,可以協助我們解決這個問題,但這些事件是專門用於觸控的完全獨立 API,因此如果您想同時支援滑鼠和觸控,就必須編寫兩個獨立的事件模型。Chrome 55 提供新標準,可整合這兩種模型:指標事件。
單一事件模型
指標事件會將瀏覽器的指標輸入模式統一,將觸控、觸控筆和滑鼠整合為一組事件。例如:
document.addEventListener('pointermove',
ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
ev => console.log('The pointer is now over foo.'));
以下是所有可用事件的清單,如果您熟悉滑鼠事件,應該會覺得相當熟悉:
pointerover
|
游標已進入元素的定界框。對於支援懸停的裝置,這會立即發生;對於不支援的裝置,則會在 pointerdown 事件前發生。 |
pointerenter
|
與 pointerover 類似,但不會向上傳遞,並以不同方式處理子項。規格詳細資料。 |
pointerdown
|
指標已進入有效按鈕狀態,視輸入裝置的語意而定,可能是按下按鈕或建立了接觸。 |
pointermove
|
游標已變更位置。 |
pointerup
|
指標已離開啟用按鈕狀態。 |
pointercancel
|
發生某些事件,表示指標不太可能再發出任何事件。也就是說,您應取消任何進行中的動作,並返回中性輸入狀態。 |
pointerout
|
游標已離開元素或畫面的邊界框。同樣地,如果裝置不支援懸停,pointerup 後也會發生這種情況。 |
pointerleave
|
與 pointerout 類似,但不會向上傳遞,並以不同方式處理子項。規格詳細資料。 |
gotpointercapture
|
元素已收到指標擷取。 |
lostpointercapture
|
已擷取的指標已釋放。 |
不同的輸入類型
一般來說,指標事件可讓您以輸入無關的方式編寫程式碼,而不需要為不同的輸入裝置註冊個別的事件處理常式。當然,您仍需留意輸入類型之間的差異,例如是否適用懸停概念。如果您想區分不同的輸入裝置類型 (例如為不同輸入提供不同的程式碼/功能),可以使用 PointerEvent
介面的 pointerType
屬性,在同一個事件處理常式中執行此操作。舉例來說,如果您正在為側邊導覽匣編寫程式碼,可以在 pointermove
事件中使用下列邏輯:
switch(ev.pointerType) {
case 'mouse':
// Do nothing.
break;
case 'touch':
// Allow drag gesture.
break;
case 'pen':
// Also allow drag gesture.
break;
default:
// Getting an empty string means the browser doesn't know
// what device type it is. Let's assume mouse and do nothing.
break;
}
預設動作
在支援觸控的瀏覽器中,使用特定手勢可捲動、縮放或重新整理網頁。在觸控事件的情況下,您仍會在這些預設動作發生時收到事件,例如,在使用者捲動時,系統仍會觸發 touchmove
。
使用指標事件時,只要觸發捲動或縮放等預設動作,就會收到 pointercancel
事件,讓您知道瀏覽器已控制指標。例如:
document.addEventListener('pointercancel',
ev => console.log('Go home, the browser is in charge now.'));
內建速度:與觸控事件相比,這個模型預設可提供更佳效能,您需要使用被動事件監聽器才能達到相同的回應速度。
您可以使用 touch-action
CSS 屬性,停止瀏覽器的控制權。如果在元素上將其設為 none
,系統就會停用所有在該元素上啟動的瀏覽器定義動作。不過,還有許多其他值可用於更精細的控制,例如 pan-x
,可讓瀏覽器對 x 軸的移動做出反應,但不會對 y 軸的移動做出反應。Chrome 55 支援以下值:
auto
|
預設值;瀏覽器可以執行任何預設動作。 |
none
|
瀏覽器不得執行任何預設動作。 |
pan-x
|
瀏覽器只能執行水平捲動的預設動作。 |
pan-y
|
瀏覽器只能執行垂直捲動的預設動作。 |
pan-left
|
瀏覽器只能執行水平捲動的預設動作,且只能將頁面平移至左側。 |
pan-right
|
瀏覽器只能執行水平捲動的預設動作,且只能將頁面平移至右側。 |
pan-up
|
瀏覽器只能執行垂直捲動的預設動作,且只能將頁面平移至上方。 |
pan-down
|
瀏覽器只能執行垂直捲動的預設動作,且只能向下平移網頁。 |
manipulation
|
瀏覽器只能執行捲動和縮放動作。 |
游標擷取
您是否曾經花費一小時來偵錯錯誤的 mouseup
事件,最後才發現問題是使用者在點擊目標之外放開按鈕?沒有?好吧,那麼可能只有我有這個問題。
不過,目前還沒有很好的方法可以解決這個問題。當然可以,您可以在文件上設定 mouseup
處理常式,並在應用程式上儲存部分狀態,以便追蹤相關內容。不過,如果您正在建構網頁元件,並希望將所有內容保持良好且隔離的狀態,這並非最簡潔的解決方案。
使用指標事件可提供更優質的解決方案:您可以擷取指標,確保能取得 pointerup
事件 (或其他難以捉摸的事件)。
const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
console.log('Button down, capturing!');
// Every pointer has an ID, which you can read from the event.
foo.setPointerCapture(ev.pointerId);
});
foo.addEventListener('pointerup',
ev => console.log('Button up. Every time!'));
瀏覽器支援
在撰寫本文時,Internet Explorer 11、Microsoft Edge、Chrome 和 Opera 支援指標事件,Firefox 則部分支援。如要查看最新清單,請前往 caniuse.com。
您可以使用指標事件 polyfill 來填補空白。或者,您也可以在執行階段檢查瀏覽器支援情形,方法如下:
if (window.PointerEvent) {
// Yay, we can use pointer events!
} else {
// Back to mouse and touch events, I guess.
}
指標事件是漸進式強化的絕佳候選項目:只要修改初始化方法來進行上述檢查、在 if
區塊中新增指標事件處理常式,並將滑鼠/觸控事件處理常式移至 else
區塊即可。
歡迎試用,並與我們分享你的想法!