滑鼠測試能測出什麼:瀏覽器滑鼠事件看得到與看不到的

圖:網頁收得到按鍵事件,卻拿不到 DPI、回報率和系統的雙擊速度設定。
滑鼠按一下卻變成兩下(俗稱連點),或側鍵好像沒反應時,很多人會先上網找個「滑鼠測試」網站按按看。我最近也做了一個:Device Tester 裝置測試 ↗。它可以測五顆按鍵、雙擊間隔、滾輪,還有每秒點幾下(CPS,Clicks Per Second)。
本來以為這只是個小工具。做完才發現,整個設計都繞著一個問題打轉:網頁到底知道什麼? 答案是:網頁只知道瀏覽器轉交給它的「事件」。滑鼠的硬體、韌體、驅動程式和作業系統設定,網頁一樣都碰不到。所以工具頁面上寫得很直白:它只看瀏覽器收到的事件,不能直接診斷硬體。
這篇把開發時弄懂的幾件事整理出來。程式碼看不懂也沒關係,直接跳過不影響閱讀。
看得到的:按鍵、點擊與滾輪事件
先講什麼是「事件」。你每按一下、放開、滾一下,瀏覽器就會發一則通知給網頁,告訴它剛剛發生了什麼。這則通知就叫事件。網頁能拿到的滑鼠資訊,全都來自下面這幾種:
- pointerdown/pointerup:按下與放開。pointer 是「指標」,滑鼠、觸控筆、手指都算。這兩個事件會附上三項資訊:
pointerType是哪種裝置(mouse 是滑鼠、pen 是觸控筆、touch 是手指),button是這次動到哪一顆鍵,buttons是現在按住了哪些鍵。 - click、dblclick、auxclick、contextmenu:瀏覽器根據你的按下和放開,自己整理出來的事件,分別代表「點一下」「點兩下」「用左鍵以外的鍵(例如中鍵)點了一下」和「叫出右鍵選單」。
- wheel:滾輪。會附上橫向和直向各滾了多少(
deltaX、deltaY),以及這個數字的單位(deltaMode)。
最容易搞混的是 button 和 buttons,因為兩邊的編號不一樣:
| 按鍵 | button 的值 | buttons 的號碼 |
|---|---|---|
| 左鍵 | 0 | 1 |
| 中鍵(滾輪鍵) | 1 | 4 |
| 右鍵 | 2 | 2 |
| 上一頁鍵 | 3 | 8 |
| 下一頁鍵 | 4 | 16 |
button 是一個編號,說的是「這一次動到哪顆鍵」。buttons 則是一個總和:每顆鍵各有一個號碼(1、2、4、8、16),現在按住哪幾顆,就把它們的號碼加起來。例如左鍵和右鍵一起按住,buttons 就是 1 + 2 = 3。這些號碼都是 2 的次方,怎麼加都不會撞號,工程師把這種寫法叫「位元遮罩」(bitmask)。
另外要注意,中鍵和右鍵的順序在兩邊剛好相反:button 是中鍵 1、右鍵 2;buttons 反過來,右鍵 2、中鍵 4。
工具只替這五顆鍵取名字。其他 button 值一律照實列在「其他按鍵代碼」底下,不去猜它是什麼鍵。

圖:按過左鍵和中鍵、右鍵還按著的畫面。事件紀錄的「按鍵位元遮罩」就是 buttons:中鍵按下時是 4,右鍵是 2,跟上面的表一樣。
坑一:兩鍵同按時,第二顆鍵沒有 pointerdown
這個坑是我讓 AI agent 用程式操作 Chrome、做上線前的自動測試時抓到的。我照這個順序按:先按住左鍵,再加按右鍵,接著放開左鍵,最後放開右鍵。實際收到的事件是:
pointerdown button=0 buttons=1 ← 按下左鍵
pointermove button=2 buttons=3 ← 加按右鍵(不是 pointerdown)
pointermove button=0 buttons=2 ← 放開左鍵(不是 pointerup)
pointerup button=2 buttons=0 ← 放開右鍵pointermove 本來是「滑鼠移動了」的事件,這裡卻用來通知按鍵的變化。這不是 Chrome 的 bug。瀏覽器處理滑鼠、觸控筆和手指,有一套共同的標準,叫 Pointer Events 規格。規格對多鍵同按本來就這樣規定:從「一顆鍵都沒按」變成「有鍵按著」,才送 pointerdown;全部放開了,才送 pointerup。中間多按一顆或先放一顆,都藏在 pointermove 裡。
我的第一版只理會 pointerdown 和 pointerup,所以兩顆鍵一起按時,畫面上只有左鍵會亮。

圖:兩顆鍵一起按時,中間兩步只送出 pointermove,要看 buttons 的數字才知道哪些鍵還按著。
修法是:每收到一次 pointermove,就拿 buttons 重新對一次「現在按住哪些鍵」。滑鼠一動,pointermove 就會連續送來一大串,所以只要 buttons 沒變,程式就直接沿用原本的狀態,畫面也不必跟著重畫(這個工具的畫面是用 Vue 做的)。pointermove 也不寫進事件紀錄、不送任何統計,它只負責校正按鍵狀態。
也因為這樣,工具的「最近的瀏覽器事件」清單只記按下、放開和點擊。兩鍵同按時,清單可能少了第二顆鍵的「按下」,或是多出一筆「放開」,卻找不到跟它配對的「按下」。這點我也寫進了工具頁面的「結果判讀」。
細節:在測試區外面放開按鍵
還有一種情況:在測試區按下,拖到外面才放開。為了不漏掉這一下,按下的當下,我會替測試區呼叫 setPointerCapture。意思是「這個指標接下來的事件,就算游標跑出去了,也都先交給我」,等所有按鍵都放開才交還。
另外,我也讓整個視窗(window)接收 pointerup 和 pointercancel,當作保險:萬一測試區漏接,視窗還接得到。pointercancel 是瀏覽器中途取消這次操作時送來的事件。它不會告訴你是哪顆鍵(button 會是 -1),而且在它之後,瀏覽器也不會再送這個指標的事件。所以一收到它,就把所有按住的鍵一起清掉,免得畫面上某顆鍵永遠停在「按下中」。
坑二:click 不一定是滑鼠點的
手機上用手指點一下螢幕,會觸發 click。用鍵盤選到按鈕(例如按 Tab 移過去)再按 Enter,也會觸發 click。如果照單全收,手指點螢幕、鍵盤按 Enter,都會被當成滑鼠左鍵。所以我訂了三條規則:
detail記的是「連續點到第幾下」。鍵盤觸發的 click,這個數字會是 0,所以detail是 0 的 click 不算。- 現在的瀏覽器送來的 click 會附上
pointerType,有附就只認 mouse。 - 有些瀏覽器送來的 click 沒有
pointerType,這時就看同一個測試區上一次按下(pointerdown)是什麼裝置。
這樣做有個代價:完全不支援 Pointer Events 的瀏覽器,在雙擊測試區會一直沒有數字。我寧可它「沒有資料」,也不要它「把觸控當成滑鼠」。這也跟頁面上「只記錄實體滑鼠輸入」的說明一致。
點擊速度(CPS)測試更嚴格:只算滑鼠左鍵「按下」的那一刻(pointerType 是 mouse、button 是 0 的 pointerdown),不看 click。
看不到的:這幾件事網頁測不出來
作業系統的雙擊速度
Windows 和 macOS 都能設定「兩下要多快才算雙擊」。網頁讀不到這個設定,也就沒辦法照系統的標準,判斷這兩下算不算雙擊。所以雙擊測試區把兩件事分開顯示:
- 自己算的間隔:同一顆鍵前後兩次點擊隔了幾毫秒,最新的排最前面。
- 瀏覽器的 dblclick 次數:瀏覽器依照系統設定,判斷前後兩下夠快、算成一次雙擊時,會另外送出一個 dblclick,這個數字才會加一。

圖:在雙擊測試區點了幾下。每次的間隔排成一列,瀏覽器的 dblclick 次數另外計算。
如果你明明只按一下,卻多了一個間隔很短的新紀錄,或 dblclick 次數增加了,代表這一下讓瀏覽器收到了兩次點擊。這很像微動開關(滑鼠按鍵底下的小開關)彈跳造成的連點:按一下,開關卻接觸了兩次。但網頁沒辦法確認,因為瀏覽器、擴充套件和系統的輸入設定都可能影響結果。
所以工具只把事件列出來,不替硬體下結論。它會建議你:換個瀏覽器比較、檢查系統的雙擊速度、暫時停用滑鼠相關的擴充套件,或把滑鼠接到另一台電腦試試。
上一頁、下一頁側鍵
很多滑鼠側邊有「上一頁」「下一頁」兩顆鍵。瀏覽器和作業系統常把它們拿來做換頁之類的事,滑鼠附的驅動軟體也可能改掉按鍵設定。所以按下去,網頁不一定收得到。側鍵顯示「未偵測」,意思是「這個頁面還沒收到它的事件」,不是「它壞了」。
滾輪到底滾了幾格
滾輪的 deltaY 很容易被當成「滾了幾格」。其實它的單位要看 deltaMode:0 是像素、1 是行、2 是頁。數字本身也是瀏覽器和系統處理過的結果,不同裝置、不同平台給的差很多。所以工具照原樣顯示數字和單位,方向則看橫向和直向哪邊滾的量比較大(不管正負號),就判斷成那個方向。滾輪測試區也不攔截捲動,頁面照樣可以捲,不會把人困在測試區裡。

圖:滾輪測試區照原樣列出 deltaX、deltaY 和單位。負數代表往左或往上,方向看橫向、直向哪邊滾的量比較大。
DPI、回報率與硬體延遲
同樣的道理,DPI、回報率和硬體延遲,網頁也都讀不到。DPI 是滑鼠每移動一英吋回報幾個點,很多人叫它靈敏度;回報率是滑鼠每秒向電腦回報幾次;硬體延遲是從你按下或移動滑鼠,到訊號送進電腦,中間花的時間。拿回報率來說,瀏覽器通常會把密集的移動事件合併,配合畫面更新的節奏才交給網頁,所以網頁數到的 pointermove 次數,不等於滑鼠實際回報的次數。與其給一個看起來很專業、其實不準的數字,我乾脆不顯示這些項目,並在說明裡寫清楚原因。
點擊速度怎麼算才不灌水
CPS 就是每秒點幾下。測試從你點第一下開始計時,這一下也算在內。時間可以選 1、5、10、30 秒,也可以自己輸入 1 到 60 之間的整數秒數。
剛開始的一瞬間,經過的時間太短,算出來的即時速度會暴衝。例如才過 0.05 秒就點了兩下,換算起來就是每秒 40 下。所以前 250 毫秒先不顯示即時速度,畫面停在 0.00,點擊還是照算。時間到了之後,成績一律用「總點擊數 ÷ 完整秒數」計算。

圖:5 秒測試結束的畫面。28 下除以 5 秒,CPS 是 5.60。
最佳成績存在 localStorage。這是瀏覽器內建的一小塊儲存空間,資料只會留在你用的這個瀏覽器裡。每種測試時間各留一筆最高分,一個按鈕就能清掉。
隱私:事件不離開你的瀏覽器
滑鼠測試看起來無害,但事件資料其實很詳細:每一次按鍵的時間、間隔、滾輪數值都有。所以規則訂得很嚴:
- 只用標準的 Pointer、Mouse 與 Wheel 事件,不要求相機、麥克風或任何裝置權限。
- 按鍵代碼、座標、事件時間、點擊間隔、滾輪數值和成績,都不會送到分析服務(Google Analytics)。
- 工具自己送出的分析事件只有幾種固定的:開始了哪一項測試,以及「完成」「重設」「清除本機紀錄」這幾個動作,另外附上用的是哪個語言。
- 「頁面瀏覽」事件(page_view)和站上其他工具一樣,照 Google Analytics 的預設記錄:打開頁面時記一筆,在站內切換工具時(例如從滑鼠測試換到鍵盤測試)也各記一筆。記的網址不含
#之後的片段,所以在滑鼠測試裡切換四種模式不會另記。 - Google Signals(Google 利用使用者登入的帳號,把不同裝置的造訪串起來統計的功能)和廣告個人化,我都關掉了。
- 原始事件只放在記憶體裡,離開或重新整理頁面就會消失。
小結
- 網頁只看得到事件:滑鼠硬體、驅動程式和作業系統設定,網頁都碰不到。
- 兩鍵同按藏在 pointermove 裡:要用
buttons的總和對帳,不能只看按下和放開。 - 先排除不是滑鼠的輸入:鍵盤觸發的 click 和手指觸控都不算。
- 測不到的就不假裝測得到:系統的雙擊速度設定、DPI、回報率測不到,就老實寫出來,比硬給一個數字有用。
工具在這裡:Device Tester 裝置測試 ↗,打開瀏覽器就能測,不用安裝。各項測試的用法,可以看 Device Tester 工具介紹。
