本篇要解決的問題
網站上蠻常會有表單功能的,像是聯絡我們、購物車,就會需要前端工程師先將欄位驗證好,當資料、格式都正確後,再調用 API 把表單送到後端去做處理,這樣子的流程會是比較好的 UX。最近的案子剛好要驗證很多的表單欄位,就順便整理成這篇,之後要用就可以 複製貼上 回頭查找。
除了表單驗證,本篇的 Demo 也會使用一些套件,讓出來的資料會是正確的格式,就不會寫驗證了,因為格式已經是對的。
本篇除了自己手寫的原生 JS,也會使用套件,都會附上套件的連結。
本篇最後完成的 Demo:https://letswritetw.github.io/letswrite-form-validation/
Demo 部份為了讓頁面好看,有用 daisyUI 這套 Tailwind CSS 的 framework。
中文姓名
這算是比較特別的需求,要求說姓名欄位的部份只能填寫中文名字,不能填寫英文。
August 在寫的時候也增加了限制:不能填寫數字、不能填寫特殊符號。
但…特殊符號有點麻煩,因為中文還有些人會打全形,因此過濾的部份就是濾掉幾個常用的符號。
<!-- HTML --> <input id="js_name" type="text"> <!-- JS --> <script> function checkChName(el) { el.addEventListener('input', e => { setTimeout(() => { return e.target.value = e.target.value.replace(/[a-zA-Z0-9]|[ws]|[!#$€£%&'"`()*+-./::;;…,,。「」【】=<>?@{}^|[]]/g, '') }, 0) }, false); } const jsName = document.getElementById('js_name'); checkChName(jsName); </script>
簡單來說,就是 id
為 js_name
的 input
,在值有改變時,會把輸入的值濾掉英文、數字、指定的符號後,再回傳成值。
因為是寫原生的,所以寫的落落長,如果能用 Vue.js,直接寫進 watch
裡可以更簡短。
身分證字號、手機條碼
這部份就直接用套件了,上網查了一下身份證字號的規則,覺得自己寫好麻煩,既然有佛心工程師寫了套件,就直接使用囉~
使用的套件是:taiwan-id-validator
它總共可以驗證以下資料:
- 台灣身分證字號驗證
- 舊版臺灣地區無戶籍國民、外國人、大陸地區人民及香港或澳門居民之專屬代號
- 新版臺灣地區無戶籍國民、外國人、大陸地區人民及香港或澳門居民之專屬代號
- 公司統一編號驗證 (2021/10/28 新增新版統一編號檢查)
- 自然人憑證編號驗證
- 電子發票手機條碼驗證
- 電子發票捐贈碼驗證
是不是超多,本篇僅示範身分證字號、電子發票手機條碼這二個。
HTML
<!-- 身份證字號 --> <input id="js_id" class="uppercase" type="text"> <!-- 手機條碼 --> <input id="js_phoneId" class="uppercase" type="text">
JS
import { isNationalIdentificationNumberValid, // 身分證字號 isEInvoiceCellPhoneBarcodeValid, // 手機條碼 } from 'taiwan-id-validator'; // 驗證身分證字號 const jsId = document.getElementById('js_id'); let value = jsId.value; if(isNationalIdentificationNumberValid(value)) { // 格式正確時 } else { // 格式錯誤時 } // 驗證手機條碼 const jsPhoneId = document.getElementById('js_phoneId'); let value = jsPhoneId.value; if(isEInvoiceCellPhoneBarcodeValid(value)) { // 格式正確時 } else { // 格式錯誤時 }
身分證首字、手機條碼自動大寫
五歲能抬頭的朋友一定有發現到,上面 August 寫的 HTML 程式碼,在 input
上多加了 uppercase
這個 class。
身分證字號的第一個英文字母、手機條碼的英文字母,都是大寫,如果要使用者多按一個按鍵去切換大小寫,是不太好的 UX,因為每多一次點擊,就會多流失一點使用者的填單率。
所以更好的 UX 是不論使用者輸入大寫或小寫,我們都直接透過程式讓它自動轉為大寫。
方式很簡單。
- 在頁面 HTML 的部份,我們用
.uppercase
讓使用者看到的結果直接都是大寫 - 驗證資料時,用 JS 把使用者輸入的值轉成大寫後再去驗證
- 送資料時,也是透過 JS 把使用者輸入的值轉成大寫後再送出
所以上面的範例程式碼就可以改成:
HTML
<!-- 身份證字號 --> <input id="js_id" class="uppercase" type="text"> <!-- 手機條碼 --> <input id="js_phoneId" class="uppercase" type="text">
CSS
.uppercase { text-transform: uppercase; }
JS
import { isNationalIdentificationNumberValid, // 身分證字號 isEInvoiceCellPhoneBarcodeValid, // 手機條碼 } from 'taiwan-id-validator'; // 驗證身分證字號 const jsId = document.getElementById('js_id'); let value = jsId.value.toLocaleUpperCase(); if(isNationalIdentificationNumberValid(value)) { // 格式正確時 } else { // 格式錯誤時 } // 驗證手機條碼 const jsPhoneId = document.getElementById('js_phoneId'); let value = jsPhoneId.value.toLocaleUpperCase(); if(isEInvoiceCellPhoneBarcodeValid(value)) { // 格式正確時 } else { // 格式錯誤時 }
生日 / 出生年月日
選日期的部份,August 也是用套件,用過的有二套。
如果不在意頁面多引用 jQuery,推薦這套:fDatepicker。
他可以讓使用者先從「年」開始選,選完後再選「月」,之後再選「日」,操作上比較直覺。
如果不想用 jQuery,想用原生,那可以用本篇 Demo 使用的這套:Vanilla JS Datepicker。
August 覺得這套操作體驗上沒有 fDatepicker 來的好,但它的優點就是原生。
如果有用過更好的日期套件要推薦的話,歡迎留言~
關於出生年月日的欄位,為了要統一輸出的格式,因此是用套件來產生選單讓使用者選取。
但這樣還不夠,還需要一道工,就是生日的 input
不能讓使用者自行填寫。
HTML
<input id="js_birthday" type="text" readonly inputmode="none">
readonly
,就是讓這個欄位只能讀不能寫。
inputmode="none"
,就是在手機時不讓小鍵盤跳出來。
至於為什麼這邊 August 是推薦 readonly
而不是 disabled
?
在工程師永遠的好朋友 stackoverflow 上有提到,我們按 tab 鍵切換選定的元素時,readonly
可以被 focus,而 disabled
則無法,為了讓桌機時操作順暢,這邊選用的是 readonly
。
JS
import { Datepicker } from 'vanillajs-datepicker' // 引用中文 import zhTW from 'vanillajs-datepicker/locales/zh-TW' Object.assign(Datepicker.locales, zhTW); const birthday = document.getElementById('js_birthday'); const datepicker = new Datepicker(birthday, { format: 'yyyy-mm-dd', language: 'zh-TW' });
format
的部份可以改成後端需要的格式。
E-mail 電子信箱
驗證 email 比較單純,就是驗證輸入的格式正不正確。
HTML
<input id="js_email" type="email" inputmode="email">
inputmode="email"
,是讓手機時跳出的小鍵盤,是專門輸入 email 用的,iPhone 上看會像這樣:

JS
const jsEmail = document.getElementById('js_email'); const rex = /^w+((-w+)|(.w+))*@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+)*.[A-Za-z]+$/; if(jsEmail.value.search(rex) === -1) { // 錯誤 } else { // 正確 }
手機
驗證手機也很單純,就是看格式正不正確。
HTML
<input id="js_phone" type="tel" inputmode="tel">
inputmode="tel"
,是讓手機時跳出的小鍵盤,是專門輸入電話號碼用的,iPhone 上看會像這樣:

JS
const jsPhone = document.getElementById('js_phone'); const rex = /^(09)[0-9]{8}$/; if(jsPhone.value.search(rex) === -1) { // 錯誤 } else { j// 正確 }
手機限輸入 10 碼
手機號碼總共 10 碼,我們自己在網購時都知道,有時就是會手抖或手殘不小心多按一碼造成錯誤。
因此 August 在製作表單時,也會把手機這欄加入只能填寫 10 碼的 JS:
const jsPhone = document.getElementById('js_phone'); jsPhone.addEventListener('input', e => { if(e.target.value.length > 10) { e.target.value = e.target.value.slice(0, 10); } });
選擇縣市
通常,如果讓使用者填寫地址時,縣市的部份是自己打字,就會看到什麼叫世界末日,光一個台北市,就會看到「台北、台北市、臺北、臺北市、北市」。
如果還要使用者自己去找郵遞區號,就會更慘,不是憑記憶填不然就是乾脆放棄不填了。
幸好,工程師界裡總有希望世界和平的大神,選擇縣市的部份推薦套件:jQuery-TWzipcode。
雖然名稱上有可以再戰十年的「jQuery」,但不用擔心,它有出原生 JS 版的。
進到套件的 GitHub,會看見 JS 檔案有三個:

- jquery.twzipcode.js:jQuery + 未壓縮版
- jquery.twzipcode.min.js:jQuery + 壓縮版
- twzipcode.js:原生 JS + 未壓縮版
文件的部份,jQuery 版本的就看 GitHub 上 README.md 的說明。原生的就看上面提供的 Demo。
因為原生的部份不是寫成 export,所以引用時就在頁面上直接引用。
HTML
<div class="twzipcode"> <!-- 選擇縣市 --> <div data-role="county"> <!-- 選擇鄉鎮市區 --> <div data-role="district"> <!-- 郵遞區號 --> <div data-role="zipcode"> </div> <script src="/path/twzipcode.js"></script>
JS
const twzipcode = new TWzipcode(".twzipcode", { zipcode: { readonly: true } });
郵遞區號因為套件會自動產生,因此建議是加上 readonly: true
讓使用者無法自行輸入。
範例及原始碼
本篇的範例及原始碼有放在 GitHub 上,取用前麻煩分享本篇,或是按個星星,你的小小動作對本站都是大大的鼓勵。
Demo:https://letswritetw.github.io/letswrite-form-validation/
原始碼:https://github.com/letswritetw/letswrite-form-validation

