表單 Practice

Form Validation

這邊是我最近對於表單的一些作法,因為內化還不夠,每次都會漏掉一些,所以花了些時間整理整理,適合的情境不是 single page application 就是了,比較偏傳統形式網頁的表單,然後可能也包括不少大家早就知道(?)的細節就是了。

首先,我現在偏好不用 JavaScript 做表單檢查,而是先做好最基本的 server side 檢查,然後加上 HTML5 的表單檢查,會這樣決定的主因是:

  1. JavaScript 的表單檢查 library 用起來都不太順手,而且不想花時間處理串接,且能少點 library 總是好的;
  2. 幾個常用的 type,像是 email, url 比較不需要擔心檢查的 pattern 有不周全的地方,我想很多人都有上網搜尋過這些欄位的 regular expression pettern 的經驗;
  3. 支援度已經不是大問題了,事實上我的工作上還需要支援 IE 10, 9 之類的,其實這些非 modern browser 的量都已經非常少了,所以就靠個 server side 檢查對付他們就好,使用體驗稍微差一點也還可以接受,這也是種 graceful degradation(優雅降級);
  4. HTML5 的表單檢查可以說是 web developer 當年對抗網路標準發展遲緩一大勝利指標,當然應該要好好用一下。

而用 HTML5 表單檢查還有個意外的好處是基本的錯誤訊息自動有翻譯(看使用者瀏覽器的語言),另外如果有自製的輸入元件,也有 API 可以串接,當然訊息就要自己提供就是了。

用 HTML5 表單檢查當然也不是完全沒有問題,例如目前 email 欄位還沒有瀏覽器支援 IDN domain 的信箱;另外就是上傳檔案的 file input 的值不能從 server 端直接給,這限制是因為會有安全性問題,而這限制所衍生的問題是:表單送到 server side 檢查後發現有錯誤時(例如 captcha 錯),使用者就一定要重新選取上傳的檔案,對於使用者體驗算是個扣分(而且上傳檔案大的話很花時間,然後另外還有個上傳檔案大小限制、就又是另外一個議題了),要解決這問題一般來說就是靠 JavaScript 做些加強,例如針對 captcha 可以先用 ajax call 檢查 captcha ,正確的話就換個 session token 之類的回來,不過即使這樣,還是逃不了完整的 server side 表單檢查,所以也還要處理 ajax submit 後的表單錯誤訊息顯示。

不管是 server side 檢查後產生的錯誤訊息,還是 ajax call 之前檢查產生的錯誤訊息,理所當然都會放在欄位附近,不過還要讓訊息和欄位之間建立關聯,才好進一步做一些處理,例如使用者有更新欄位值之後會把錯誤訊息隱藏之類的,或許很多人會用父層 DOM 節點加上特殊的 class 包起來找,不過我比較偏好用 aria-describedby,大概會看起像是:

<input id="mail" name="mail" type="email" aria-describedby="mail-field-info" />
<span id="mail-field-info">Required field!</span>

這樣只要找的到#mail欄位,就可以透過他的aria-describedby屬性找到該欄位的相關訊息的 DOM 節點,另外值得注意的是,aria-describedby 值的格式是 IDRefList,不是單一個 ID,而是一個用空白切分的 ID 指標們,所以如果有這種情形,還可以在錯誤訊息的那個 DOM 節點加上 role="alert" 給它用來辨識,其實就算只有一個 ID 也還是可以加上 role 屬性啦。如果真的需要用透過父層 DOM 節點來找的話,之前研究的結論是可以在預期的父層標籤用role="section"來方便定位,用 jQuery 大概會像是:

$fieldSection = $field.closest('[role="section"]');

這個標籤下應該會包括欄位的標籤(label)、欄位的 input element 以及相關的資訊(說明、錯誤訊息)等。

另外還有一點,就是要用 ajax 上傳檔案的話,需要有支援 FormData 的瀏覽器,並且如果用 jQuery 送 FormData 的話記得要加點設定:

contentType: false,
processData: false

還有就是 ajax 送表單的目標 URL,我目前比較喜歡的作法是讀<form>action屬性,也就是和瀏覽器自己送的 URL 一樣,然後透過 HTTP content negotiation 機制來決定回傳的格式,比較正確的作法是看Accept,以 jQuery 來說,如果要 server 回 JSON 格式的話,可以加上:

dataType: 'json'

這樣送出的 request 就會帶上正確的Acceptheader,向 server 端要求application/json,不過Accept的值解析起來比較麻煩些,其實是可以送出說 client 端可以接受多種格式,然後還加上個優先度的,也因此也有很多人是看X-Requested-With,一般 library 如果是透過 XHR 發的 request 都會有這個 header;還有就是送出的資料格式(Content-Type),即使是 ajax call,我目前也都不用 JSON 了,還是用application/x-www-form-urlencoded為主,另外要上傳檔案的話當然一定要用multipart/form-data,主要是因為:

  1. 送 JSON 的話就不會是 simple request 了,有些時候會比較麻煩,例如 Cross Origin 時會需要發 preflight,然後就可能遇到 AWS 以前不支援 preflight request 的 bug;
  2. 用這幾個老的 Content-Type 支援度還是比較高,對於 server 端實做和 client 端實做其實都相對友善一點,例如 jQuery 預設就依然是 form-urlencoded,没特别需求還是用標準一點的格式,特殊需求是例如 GraphQL,不過一般表單發送應該不會走 GraphQL 吧。

其實 JSON 雖然已經有 RFC 規範了,不過在 Web 標準的世界還沒相當深入內化,不知道以後有沒有機會更加的內化整合進去。

前面有提到 ajax call 送出的目標 URL 我會偏好從,<form>裡面讀,不過或許有的情境會讓 ajax call 必須要自己用不一樣的 API URL,這時候我建議還是把 API URL 寫在<form>的屬性裡面,這樣可以讓 JavaScript 的邏輯比較乾淨,也不用作什麼 mapping 或是常數來儲存 API 的 URL,維護修改時也不用兩邊檢查,屬性名稱可以用例如:data-action之類的屬性,data-*屬性正好適合來做這些事情,不但有 DOM API 支援,jQuery 也可以用.data()method 來讀取,命名上,如果覺得有個標準參考比較好,可以看看 jQuery-ujs 的設計,雖然比較長一點,它用的是:data-ujs:submit-button-formaction,我是覺得有些不正確啦,畢竟要送出表單不一定是點擊 submit button。

其實假設送出表單的動作都是滑鼠點擊 submit button 這是個親和力問題,如果只把 ajax call 送的函式 bind 在 submit button 的 click 事件上,這其實是不太好的,因為其實瀏覽器預設的行為是可以在很多地方用鍵盤送出表單,例如在 text input 上按下 Enter 鍵,或是在 submit button 上按下空白鍵之類的,所以針對表單還是要去 bind form submit 事件才是正解,至於 jQuery-ujs,其實也是這樣做的,它是用 delegate event 的形式去監聽傳遞到 document 上的 submit 事件,然後才去做後續的處理,只是命名上讓人覺得不太正確。

最後一項,前面說不用 JavaScript 做表單檢查(不看自訂輸入元件的話),其實有一個例外,就是上傳檔案的大小檢查,因為沒做對使用體驗的影響比較大,然後就是要還要記得針對 ajax call 送表單加上 HTTP 413 Status Code 的錯誤訊息處理。


a11y

Pa11y Dashboard,

標題的 a11y 其實是 accessibility (親和力)的縮寫,現在英文世界似乎很大量的使用這個簡稱,今年的 JSConf EU 前陣子放出演講錄影,其中有一場是在講網路親和力的議題「YES, your site too can (and should) be accessible.」:

講者是 Laura Carvajal,在 Financial Times 工作,而這場演講就是他們改進 ft.com 網站親和力的過程和一些想法,我覺得精華在後半,前面是介紹自動化工具 Pa11y,a11y 是 accessibility,至於首字母的 P,看 README 應該是 pal 的意思;他們把這個自動化工具整合進他們的開發流程,然後慢慢的修改,直到把回報的問題都修完,其實現在自動化工具已經很強了,連顏色對比度夠不夠都能算出來(瀏覽器的開發工具以後也會有相關資訊),不過要驗證親和力做的如何,還是有很大量的驗證其實是需要手動測試。

手動測試的部分他們是請了 DAC (Digital Accessibility Centre) 來做,演講中還有一些測試者的測試影片,每位測試者都會先說他身體有怎樣的障礙,然後他邊測試會邊口述他在做什麼,遇到怎樣的問題,建議可以怎麼處理,感覺就是很專業的測試員。總之,在他們處理完所有 Pa11y 檢測到的問題後,才請 DAC 做親和力評估驗證,結果還是收到了一份兩百多頁的測試報告,回報了各種 ft.com 網站上的親和力問題,之後又花了幾個月的時間來處理這些問題,最後終於得到 DAC 的認證,這份認證資訊還蠻完整的,還說明了他們認證時網站的狀況,還有哪些問題待解決,甚至連可能會使用到但是還沒處理過親和力問題的同組織的網站(服務)都有列出來,另外在 ft.com 的親和力聲明也可以看到 DAC 的認證。

接著 Laura Carvajal 介紹到如何實際體會(參與)這些親和力問題,其中一個很經典的狀態就是只用鍵盤做所有的控制,他提供了一些強迫自己只能用鍵盤操控的方法,並且在這種狀態下工作,其它還有像是使用 Mac 的 VoiceOver 做為 ScreenReader、使用 Windows 的高對比模式等等,他建議可以實際自己去體驗看看的,甚至強迫自己使用一陣子,會對這些問題更有體會,除此之外,他還透過一些活動來讓其他公司內的人也來參與,像是模擬一個障礙者會面臨的環境,以他的例子來說是把網頁模糊化,模擬視力障礙的使用者狀態,然後在這個狀態下請人去完成一些任務,像是填一個表單,並且有提供些獎勵增加參與人數,記得他們是提供 Amazon Credit,這樣可以讓更多人體會到需要依靠輔具來上網的不便,長久下來也可以讓這些工作的推動更加順利。

這幾天我也試著裝起了 Pa11y Dashboard 開始做些檢測,看到的 Error 加上 Warning 數量真是有點驚人,再來慢慢處理吧...


更之前的文章