前言
預約打疫苗正夯,有關注相關資訊的人應該對這個網站不陌生:COVID-19 公費疫苗預約平台(以下簡稱預約平台)。
不過這網站用起來不大順手、介面也醜醜的(雖然說關貿不意外 XDD… 但還是要感謝趕出這個系統救台灣的團隊啦 ),看到推特上、設計師朋友都有發文討論該平台的 UI/UX,作為一個前端工程師也來蹭一下熱度 (x) 發掘可以探討的地方 (o)。
那麼就進入正題,這篇文章想探討的是預約平台表單 tab 鍵切換焦點的體驗問題。
預約網站表單 tab 鍵切換焦點的體驗問題
先交代一下使用背景:平常我在填寫表單時,很習慣使用鍵盤的 tab 鍵(使用桌面版網頁進行操作)在表單的欄位間進行焦點切換,例如填寫完第一欄,就會按個 tab 鍵跳到下一欄,這樣就不用鍵盤打一打再換去滑鼠點再回鍵盤,能一氣呵成打完表單噢。
不過在預約平台填寫預約表單時,填寫完「身分證號」後,習慣性地按下 tab 鍵後,發現焦點卻飛到頂部 Logo 位置旁的一個隱藏元素:「跳到主要內容」了(那尼摳勒?)。只好摸摸鼻子移動滑鼠再點一次下一個填寫欄位繼續操作…。
放個影片展示一下這個體驗:(正如推特上有些針對預約平台的建議,某天就默默被修正了,我想這篇文章提到的問題也有機會被修好,所以放影片備存一下 XD)
為了符合無障礙網站規範?事情好像沒那麼單純…
不過好奇為什麼這樣正常不過的操作流程,會特意要改成反直覺的焦點順序呢?看了一下網頁原始碼(裡面的 HTML 滿可怕的,各種誤用跟拼寫錯誤 XD),不少 HTML 標籤上特別指定了 tabindex,而底部的 <script>
標籤裡面某一段還特別標明了 //tab鍵觸發focus
,看起來就是特別用上 JavaScript 偵聽 tab 按鍵的事件來處理,大概就是這些部分改變了焦點變更時的順序。可是為什麼要作這些事情呢?
繼續尋找線索,從預約平台連到「衛生福利部疾病管制署全球資訊網」後,在其底部發現了「無障礙標章 2.0」的圖示。雖然不清楚預約平台是否歸屬於「衛生福利部疾病管制署全球資訊網」底下,且是否符合同一份規範,不過應該可以猜測可能就是為了政府的網站無障礙規範所做的設計吧?
註:是說我看了一下無障礙網路空間服務網的標章查詢,也沒能查到預約平台的檢測狀態。
我沒有製作無障礙網站的經驗(若有說錯的地方請多包涵),就來看一下它裡面寫了什麼跟這個有關的相關要求吧。翻找了一翻,跟 tabindex
有關係的有這條「8.3:EV1080301 在鏈結、表單控制元件、物件間建立合乎邏輯的跳位順序」:
1.檢查是否使用tabindex,在網頁上按右鍵->選擇檢查網頁原始碼。
2.如果有使用tabindex,檢查tabindex屬性所指定的內容來決定Tab鍵的順序。如下圖所顯示,新郎的姓氏、名字與出生地分別為tabindex=1、tabindex=2、tabindex=3,表示按Tab鍵的順序,輸入完新郎後,再輸入新娘的資料內容。
還有這條「8.1:EV1080100 在每一個頁面頂端加入一個鏈結,直接連往主要的內容區域」:
1.開啟網頁。
2.按下鍵盤Tab鍵遊走,並檢視頁面頂端是否出現「跳到主要內容區」連結。
這大概就是為什麼會在我第一次按下 tab 後會跑到「跳到主要內容」隱藏元素的原因了。
來看看「國家通訊傳播委員會無障礙網路空間服務網」本身是怎麼做這件事呢?(它本身有通過 AAA 等級的無障礙檢測),也找一個有表單的頁面來觀察一下行為,那就登入頁好了:
試著用滑鼠點擊「帳號」的輸入框,然後按下 tab 鍵,符合預期地跑到「密碼」輸入框了。好,然後重整網頁來試一下另一個規範,重整後按下 tab 鍵,果然在最上方出現了「跳到主要內容」。
看一下它的原始碼,它的 HTML 內並沒有特別設定 tabindex
屬性,應該也沒有特別寫什麼 JavaScript 處理 tab 的按鍵事件。其實這樣就既符合規範,且不會讓使用者操作體驗不佳(指 tab 切換表單欄位焦點這件事)了。那麼,預約平台究竟是為什麼要搞這麼一齣呢?
我們回到預約平台再看一下,看看 code、操作看看,發現它似乎是一個用 jQuery 做的 Single Page Application 單頁式網頁應用程式,在不載入新的 HTML 情況下,透過點擊改變內容的呈現。
接著把那些我們認為冗餘的、處理 tab 事件的 JavaScript 程式碼都先註解掉(這邊我透過 Chrome DevTools 的 Local Overrides 功能達成,改天我們再寫一篇文章為大家講解 XD),再跑跑看,欸… 在點擊預約進到表單頁後,按 tab 後焦點居然變得不會跑到「跳到主要內容」的元素上了。
試著理解一下那段 JavaScript 想做的事情:e.keyCode === 9
(tab 按鍵的 keyCode 為 9)這整段大概就是處理「當按下 tab 按鍵」後,若是首次點進某子頁面(firstTimePage
+ 頁面),就讓焦點移到「跳到主要內容」元素(即 #gotoContent
)上。
我想大概就是用這個方式在現行的 jQuery Single Page 架構下,達成無障礙規範的「8.1:EV1080100」需求。看來無障礙網站規範真的如傳聞地難搞? XD 難道就沒有兼顧使用者表單 tab 按鍵體驗跟符合這條無障礙規範的方法嗎?就來嘗試看看吧~
試著修修看:兼顧兩者
- 首先我想把
tabindex
全拔掉,畢竟tabindex
> 0 的用法其實是種 anti-pattern 不建議再使用(文末的補充文件有提到),而且我想應該不用用上tabindex
也能讓 tab 按鍵切換焦點符合 DOM 元素 + 視覺呈現的順序的。 - 然後是那段處理 tab keydown 事件的 JavaScript 也拔掉。讓這些行為回復到比較純淨的狀態再繼續。
來解析一下載入的 js 檔案,主要的邏輯似乎是在 js/tax/min/vab.login.min.js
中處理的。將其 format 成比較容易閱讀的格式後,可找到下面這段:
$("#btnNhca,#btnOtp,#btnID,#btnNhCardNo").off("click").on("click", function(c) {
主要在處理下面這個畫面的按鈕點擊事件。

- 按照我們註解掉原先頁面上 JavaScript 那段的內容,在此
pageNum
應該可以指定為var d = $(this).attr("code");
的d
。 - 然後在一個 ajax
success
後,會去執行: - 再找到 f 函式是:
看起來是呈現相應的內容 div,並捲到最上方。想在這邊加上:進入相應內容 div 後,按下 tab 也能移動焦點到「跳到主要內容」元素上的行為。
想了一下,暫時沒有想到漂亮的解法,以下是我臨時想到的解法:
- 在頁面的
<header id="topnav">
標籤上加上tabindex="-1"
讓它成為一個:「不出現在 tab 按鍵順序中,但是能被 JavaScriptfocus
的元素」。 - 接著在上面提到的 f 函式的
$("html, body").scrollTop(0)
後方加上,$("#topnav").focus()
,讓點擊按鈕進入子頁面後,焦點便移動到#topnav
上。 - 為什麼不直接加在
#gotoContent
上呢?因為讓它 focus 的話,就會直接看到「跳到主要內容」元素秀出來了,我們想要的是按下 tab 鍵之後才出來,所以才指定到它的父容器#topnav
。
至此,似乎勉強達成了「8.1:EV1080100 在每一個頁面頂端加入一個鏈結,直接連往主要的內容區域」規範,以及在表單欄位按下 tab 也能正確切換焦點順序的需求了。
成果如下:
我想應該有更好的解法,不過玩到這邊有點累了,也試著猜測了一些前因後果,奇怪的知識增加了,心滿意足,就先打住吧。
上個免責聲明:這樣改是否真能通過無障礙網站的檢測標準我也不知道就是了
結語
站在自己的角度試著解釋了一番,試著改改看讓它能 work 也是滿有趣的。
本文存有許多臆測的內容,不在那個情境與時空背景下也難以體會,至於事情的真相為何?也許,只有預約平台的開發團隊自己知道了。
最後還是要說句,為台灣疫情付出的人們辛苦了! 希望大家都健康平安,疫情也能早日平息囉~
補充文件
- Google 的 Web Fundamentals 提到:
- 「使用大於 0 的 tabindex 被視爲反面模式」。
- 「這在標頭、圖像或文章標題之類的非輸入元素上體現得尤爲明顯。 爲上述類型的元素添加
tabindex
會適得其反。」 - 「如果您一定要使用
tabindex
,請將其使用範圍限定在按鈕、標籤、下拉列表和文本字段之類的自定義交互式控件;也就是說,用戶可能需要提供輸入的元素。」
- Don’t Use Tabindex Greater than 0 – Adrian Roselli
- 亦討論了
tabindex
的議題,文內附有更多相關連結。
- 亦討論了