iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
JavaScript

解決學了JavaScript不知道能做什麼:用DOM操作打造實用網頁效果 系列 第 16 篇

【第十六課】表單與驗證:即時表單驗證與錯誤提示(RegExp 正則表達式與 submit 事件)

  • 分享至 

  • xImage
  •  

前言
填寫表單時,如果等到按下送出後頁面刷空才告訴你「Email 格式錯誤」,使用者一定會相當崩潰。優良的前端驗證可以在送出時擋下無效請求,並在輸入時即時復原錯誤狀態,大幅節省使用者時間與伺服器資源。
​觀念與函式深度解析
​1. event.preventDefault() 攔截表單預設送出
​為什麼選用這個函式?
原生 在按下送出按鈕或 Enter 時,預設會刷新頁面並將資料發送到 action 網址。在現代單頁應用(SPA)或前端驗證流程中,我們必須呼叫 event.preventDefault() 擋下瀏覽器的預設刷新,改由 JavaScript 來處理驗證與資料發送。
​這個函式在幹嘛?
取消瀏覽器對該事件執行的預設動作。
​2. 正則表達式(RegExp)格式檢查 (pattern.test(string))
​為什麼選用正則表達式?
驗證 Email 格式(例如是否包含 @ 與 .) 或密碼強度(例如最少 8 位數),最嚴謹且有效率的方式就是使用正則表達式。RegExp.test() 會傳回布林值(true/false),非常適合用在條件判斷式中。

​核心觀念與語法拆解
​event.preventDefault():阻止 表單送出時瀏覽器預設的頁面刷新行為,將主控權交還給 JavaScript 邏輯。
​RegExp.test() 格式過濾:使用正則表達式對格式要求嚴謹的欄位(如 Email、手機號碼)進行檢測,搭配 DOM 的 .classList.add('error') 動態高亮問題欄位。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>第十六課 - 即時表單驗證與錯誤提示</title>
  <style>
    body { font-family: sans-serif; padding: 40px; background-color: #f8f9fa; }
    .form-card { max-width: 400px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); }

    .form-group { margin-bottom: 20px; }
    label { font-weight: bold; color: #333; display: block; margin-bottom: 8px; }

    /* 輸入框基本樣式 */
    input {
      width: 100%;
      padding: 10px;
      font-size: 15px;
      border: 2px solid #ced4da;
      border-radius: 6px;
      box-sizing: border-box;
      outline: none;
      transition: border-color 0.2s ease;
    }
    input:focus { border-color: #007bff; }

    /* 驗證失敗時的輸入框與錯誤訊息樣式 */
    input.error { border-color: #dc3545; background-color: #fff8f8; }
    .error-msg { color: #dc3545; font-size: 13px; margin-top: 5px; display: none; }
    .error-msg.show { display: block; }

    /* 送出按鈕 */
    .submit-btn {
      width: 100%;
      padding: 12px;
      background-color: #007bff;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 16px;
      font-weight: bold;
      cursor: pointer;
      transition: background-color 0.2s ease;
    }
    .submit-btn:hover { background-color: #0056b3; }
  </style>
</head>
<body>

  <div class="form-card">
    <h2>會員註冊</h2>
    <form id="signupForm" novalidate>
      <!-- 電子郵件區塊 -->
      <div class="form-group">
        <label for="emailInput">電子郵件</label>
        <input type="email" id="emailInput" placeholder="example@email.com">
        <div id="emailError" class="error-msg">請輸入有效的電子郵件格式!</div>
      </div>

      <!-- 密碼區塊 -->
      <div class="form-group">
        <label for="passwordInput">密碼 (最少 8 位數)</label>
        <input type="password" id="passwordInput" placeholder="請輸入密碼...">
        <div id="passwordError" class="error-msg">密碼長度至少需要 8 個字元!</div>
      </div>

      <button type="submit" class="submit-btn">註冊帳號</button>
    </form>
  </div>

  <script>
    // ----------------------------------------------------
    // JavaScript 邏輯 (submit 事件攔截與正則表達式驗證)
    // ----------------------------------------------------

    // 第一步:取得 DOM 元素
    const signupForm = document.getElementById("signupForm");
    const emailInput = document.getElementById("emailInput");
    const passwordInput = document.getElementById("passwordInput");
    const emailError = document.getElementById("emailError");
    const passwordError = document.getElementById("passwordError");

    // Email 的正則表達式 (Basic Email Pattern)
    const emailRule = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

    // 驗證輔助函式:根據驗證結果切換 UI 錯誤狀態
    function validateField(inputEl, errorEl, isValid) {
      if (!isValid) {
        inputEl.classList.add("error");
        errorEl.classList.add("show");
        return false;
      } else {
        inputEl.classList.remove("error");
        errorEl.classList.remove("show");
        return true;
      }
    }

    // 第二步:監聽表單 submit 事件
    signupForm.addEventListener("submit", function(event) {
      // 1. 攔截表單預設刷新頁面的行為
      event.preventDefault();

      // 2. 進行個別欄位驗證
      const isEmailValid = validateField(
        emailInput, 
        emailError, 
        emailRule.test(emailInput.value.trim())
      );

      const isPasswordValid = validateField(
        passwordInput, 
        passwordError, 
        passwordInput.value.length >= 8
      );

      // 3. 通過所有驗證才執行成功邏輯
      if (isEmailValid && isPasswordValid) {
        alert("驗證成功!表單已準備送出。");
        // 這裡後續可以接入 fetch() 將資料傳送至 API
      }
    });

    // 第三步:當使用者重新輸入時,動態清除錯誤狀態 (提升 UX)
    emailInput.addEventListener("input", function() {
      if (emailRule.test(emailInput.value.trim())) {
        validateField(emailInput, emailError, true);
      }
    });

    passwordInput.addEventListener("input", function() {
      if (passwordInput.value.length >= 8) {
        validateField(passwordInput, passwordError, true);
      }
    });
  </script>
</body>
</html>

總結與實用情境
今天我們建構了完整的表單驗證邏輯流程。掌握「攔截預設行為 ➔ 欄位規則檢測 ➔ 錯誤視覺提示 ➔ 即時復原修正」這套流程,任何登入、註冊、問卷或購物結帳表單都能輕鬆搞定!
​ 下集預告
填寫地址或選取分類時,常常遇到「選了台北市,下一個選區區選單就會自動更新成台北市的行政區」對吧?
【第十七課】表單與輸入:多層級聯動下拉選單(change 事件與動態選單渲染),我們將學習如何處理選單資料間的層級關聯。


上一篇
【第十五課】表單與輸入:密碼顯示/隱藏切換(type 屬性控制與圖示狀態)
下一篇
【第十七課】表單與輸入:多層級聯動下拉選單(change 事件與動態選單渲染)
系列文
解決學了JavaScript不知道能做什麼:用DOM操作打造實用網頁效果 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言