表單驗證:中文姓名、身分證、手機條碼、生日、信箱、手機、選擇縣市

letswrite

Let's Write

Posted on June 27, 2022

表單驗證:中文姓名、身分證、手機條碼、生日、信箱、手機、選擇縣市

本篇大綱


本篇要解決的問題

網站上蠻常會有表單功能的,像是聯絡我們、購物車,就會需要前端工程師先將欄位驗證好,當資料、格式都正確後,再調用 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]|[\w\s]|[!#$€£%&'"`()*+\-.\/::;;…,,。「」【】=<>?@{}\\\^|\[\]]/g, '')
      }, 0)
    }, false);
  }
  const jsName = document.getElementById('js_name');
  checkChName(jsName);
</script>
Enter fullscreen mode Exit fullscreen mode

簡單來說,就是 idjs_nameinput,在值有改變時,會把輸入的值濾掉英文、數字、指定的符號後,再回傳成值。

因為是寫原生的,所以寫的落落長,如果能用 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">
Enter fullscreen mode Exit fullscreen mode

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 {
  // 格式錯誤時
}
Enter fullscreen mode Exit fullscreen mode

身分證首字、手機條碼自動大寫

五歲能抬頭的朋友一定有發現到,上面 August 寫的 HTML 程式碼,在 input 上多加了 uppercase 這個 class。

身分證字號的第一個英文字母、手機條碼的英文字母,都是大寫,如果要使用者多按一個按鍵去切換大小寫,是不太好的 UX,因為每多一次點擊,就會多流失一點使用者的填單率。

所以更好的 UX 是不論使用者輸入大寫或小寫,我們都直接透過程式讓它自動轉為大寫。

方式很簡單。

  1. 在頁面 HTML 的部份,我們用 .uppercase 讓使用者看到的結果直接都是大寫
  2. 驗證資料時,用 JS 把使用者輸入的值轉成大寫後再去驗證
  3. 送資料時,也是透過 JS 把使用者輸入的值轉成大寫後再送出

所以上面的範例程式碼就可以改成:

HTML

<!-- 身份證字號 -->
<input id="js_id" class="uppercase" type="text">

<!-- 手機條碼 -->
<input id="js_phoneId" class="uppercase" type="text">
Enter fullscreen mode Exit fullscreen mode

CSS

.uppercase {
    text-transform: uppercase;
}
Enter fullscreen mode Exit fullscreen mode

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 {
  // 格式錯誤時
}
Enter fullscreen mode Exit fullscreen mode

生日 / 出生年月日

選日期的部份,August 也是用套件,用過的有二套。

如果不在意頁面多引用 jQuery,推薦這套:fDatepicker

他可以讓使用者先從「年」開始選,選完後再選「月」,之後再選「日」,操作上比較直覺。

如果不想用 jQuery,想用原生,那可以用本篇 Demo 使用的這套:Vanilla JS Datepicker

August 覺得這套操作體驗上沒有 fDatepicker 來的好,但它的優點就是原生。

如果有用過更好的日期套件要推薦的話,歡迎留言~

關於出生年月日的欄位,為了要統一輸出的格式,因此是用套件來產生選單讓使用者選取。

但這樣還不夠,還需要一道工,就是生日的 input 不能讓使用者自行填寫。

HTML

<input id="js_birthday" type="text" readonly inputmode="none">
Enter fullscreen mode Exit fullscreen mode

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'
});
Enter fullscreen mode Exit fullscreen mode

format 的部份可以改成後端需要的格式。


E-mail 電子信箱

驗證 email 比較單純,就是驗證輸入的格式正不正確。

HTML

<input id="js_email" type="email" inputmode="email">
Enter fullscreen mode Exit fullscreen mode

inputmode="email",是讓手機時跳出的小鍵盤,是專門輸入 email 用的,iPhone 上看會像這樣:

手機鍵盤輸入 email 形式

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 {
  // 正確
}
Enter fullscreen mode Exit fullscreen mode

手機

驗證手機也很單純,就是看格式正不正確。

HTML

<input id="js_phone" type="tel" inputmode="tel">
Enter fullscreen mode Exit fullscreen mode

inputmode="tel",是讓手機時跳出的小鍵盤,是專門輸入電話號碼用的,iPhone 上看會像這樣:

手機鍵盤輸入電話號碼形式

JS

const jsPhone = document.getElementById('js_phone');

const rex = /^(09)[0-9]{8}$/;
if(jsPhone.value.search(rex) === -1) {
  // 錯誤
}
else {
  j// 正確
}
Enter fullscreen mode Exit fullscreen mode

手機限輸入 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);
  }
});
Enter fullscreen mode Exit fullscreen mode

選擇縣市

通常,如果讓使用者填寫地址時,縣市的部份是自己打字,就會看到什麼叫世界末日,光一個台北市,就會看到「台北、台北市、臺北、臺北市、北市」。

如果還要使用者自己去找郵遞區號,就會更慘,不是憑記憶填不然就是乾脆放棄不填了。

幸好,工程師界裡總有希望世界和平的大神,選擇縣市的部份推薦套件:jQuery-TWzipcode**

雖然名稱上有可以再戰十年的「jQuery」,但不用擔心,它有出原生 JS 版的。

進到套件的 GitHub,會看見 JS 檔案有三個:

jQuery-TWzipcode 的 GitHub

文件的部份,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>
Enter fullscreen mode Exit fullscreen mode

JS

const twzipcode = new TWzipcode(".twzipcode", {
  zipcode: {
    readonly: true
  }
});
Enter fullscreen mode Exit fullscreen mode

郵遞區號因為套件會自動產生,因此建議是加上 readonly: true 讓使用者無法自行輸入。


範例及原始碼

本篇的範例及原始碼有放在 GitHub 上,取用前麻煩分享本篇,或是按個星星,你的小小動作對本站都是大大的鼓勵。

Demo:https://letswritetw.github.io/letswrite-form-validation/

原始碼:https://github.com/letswritetw/letswrite-form-validation

💖 💪 🙅 🚩
letswrite
Let's Write

Posted on June 27, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related