iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
自我挑戰組

打造一個糖尿病自我監測小工具:從0開始學前端系列 第 26

Day26打造一個糖尿病自我監測小工具:從0開始學前端

  • 分享至 

  • xImage
  •  

其實搬移到一半發現工程量有點大,而且再五天就要結束數鐵人賽了,所以決定繼續以:JavaScripts + HTML + CSS 去完成。前兩天的 React就當作初步了解,之後有機會再自己透過專案去練習。

今天打算來「忘記密碼」的功能,由於是個小專案,所以沒有打算要寫 Email 作為驗證身分的媒介,目前的想法是參考學校系統的方式:

  1. 在註冊時回答一個問題
    • 小學就讀的學校
    • 媽媽喜歡的東西
    • 爸爸的名字是什麼 等
  2. 當登入忘記密碼時,可以點擊忘記密碼,轉跳至頁面回答問題後重新設定密碼\
  3. 設定完成後,轉跳至登入頁面,輸入新密碼重新登入

開始實作

signup.html

加上選擇問題的部分,還有給使用者輸入答案的欄位

 <form id="signupForm">
    <label for="username">帳號名:</label>
    <input type="text" id="username" required><br><br>

    <label for="password">密碼:</label>
    <!-- 密碼建議用 type="password" 而不是 number -->
    <input type="password" id="password" required><br><br>

    <label for="securityQuestion">安全問題:</label>
    <select id="securityQuestion" required>
      <option value="" disabled selected>請選擇問題</option>
      <option value="pet">你的小學名字是?</option>
      <option value="food">你最喜歡的食物是?</option>
      <option value="movie">你最喜歡的電影是?</option>
    </select>
    <br><br>

    <label for="securityAnswer">答案:</label>
    <input type="text" id="securityAnswer" required>
    <br><br>

JS 邏輯

  // ================== 註冊表單邏輯 ==================
  const signupForm = document.getElementById("signupForm");
  if (signupForm) {
    signupForm.addEventListener("submit", function (e) {
      e.preventDefault();

      const username = document.getElementById("username").value.trim();
      const password = document.getElementById("password").value.trim();
      const securityQuestion = document.getElementById("securityQuestion").value;
      const securityAnswer = document.getElementById("securityAnswer").value.trim();
      const message = document.getElementById("message");

      let users = JSON.parse(localStorage.getItem("users")) || {};

      if (!username || !password) {
        message.textContent = "請輸入帳號與密碼!";
        message.style.color = "red";
        return;
      }

      if (users[username]) {
        message.textContent = "此帳號已被註冊,請換一個!";
        message.style.color = "red";
      } else {
        //這裡要存完整結構,不只是 password
        users[username] = {
          password,
          securityQuestion,
          securityAnswer,
        };

        localStorage.setItem("users", JSON.stringify(users));
        message.textContent = "註冊成功!將自動前往登入頁...";
        message.style.color = "green";

        setTimeout(() => {
          window.location.href = "login.html";
        }, 1000);
      }
    });
  }

新增了:

const securityQuestion = document.getElementById("securityQuestion").value;
const securityAnswer = document.getElementById("securityAnswer").value.trim();
  • 能從表單抓出使用者選擇的問題 & 回答的答案

新增 reset.html 頁面

這是點擊忘記密碼後,轉跳至的重設頁面

流程:確認帳號 → 回答問題 → 確認是本人 → 修改密碼 → 轉跳至登入頁面 ( 重新登入 )

<head>
  <link rel="stylesheet" href="style.css" />
  <title>忘記密碼</title>
  <script src="script.js" defer></script>
</head>

<body>
  <!-- 加上 id 讓 script.js 能找到 navbar -->
  <nav id="navbar">
    <a href="index.html">首頁</a>
    <a href="signup.html">註冊</a>
    <a href="login.html">登入</a>
  </nav>

  <h1>重設密碼</h1>

  <div id="resetContainer">
    <!-- 第一步:輸入帳號 -->
    <form id="resetForm">
      <label for="resetUsername">帳號名:</label>
      <input type="text" id="resetUsername" required>
      <button type="submit">確認帳號</button>
    </form>

    <!-- 第二步:顯示安全問題,輸入答案 -->
    <!-- 原本 style="display:none;" 改成 class="hidden" -->
    <div id="securitySection" class="hidden">
      <p id="securityQuestionText"></p>
      <label for="securityAnswerInput">答案:</label>
      <input type="text" id="securityAnswerInput" required>
      <button type="button" id="checkAnswerBtn">送出答案</button>
    </div>

    <!-- 第三步:輸入新密碼 -->
    <!-- 原本 style="display:none;" 改成 class="hidden" -->
    <div id="newPasswordSection" class="hidden">
      <label for="newPassword">新密碼:</label>
      <input type="password" id="newPassword" required>
      <button type="button" id="resetPasswordBtn">重設密碼</button>
    </div>
  </div>

  <p id="resetMessage"></p>
</body>

JS 邏輯

 // ================== 忘記密碼邏輯 ==================
  const resetForm = document.getElementById("resetForm");
  if (resetForm) {
    const securitySection = document.getElementById("securitySection");
    const securityQuestionText = document.getElementById("securityQuestionText");
    const securityAnswerInput = document.getElementById("securityAnswerInput");
    const checkAnswerBtn = document.getElementById("checkAnswerBtn");

    const newPasswordSection = document.getElementById("newPasswordSection");
    const newPasswordInput = document.getElementById("newPassword");
    const resetPasswordBtn = document.getElementById("resetPasswordBtn");

    const resetMessage = document.getElementById("resetMessage");

    let currentUser = null; // 記錄正在重設密碼的帳號

    const QUESTION_TEXT = {
      pet: "你的小學名字是?",
      food: "你最喜歡的食物是?",
      movie: "你最喜歡的電影是?",
    };

    // 第一步:輸入帳號,顯示安全問題
    resetForm.addEventListener("submit", function (e) {
      e.preventDefault();
      resetMessage.textContent = "";

      const username = document.getElementById("resetUsername").value.trim();
      const users = JSON.parse(localStorage.getItem("users") || "{}");
      const user = users[username];

      if (!user) {
        resetMessage.textContent = "查無此帳號!";
        resetMessage.style.color = "red";
        securitySection.classList.add("hidden");
        newPasswordSection.classList.add("hidden");
        return;
      }

      currentUser = username;
      securityQuestionText.textContent =
        QUESTION_TEXT[user.securityQuestion] || "請回答你的安全問題";

      // 顯示安全問題、隱藏新密碼區
      securitySection.classList.remove("hidden");
      newPasswordSection.classList.add("hidden");
      securityAnswerInput.value = "";
      securityAnswerInput.focus();
    });

    // 第二步:檢查答案是否正確
    checkAnswerBtn.addEventListener("click", function () {
      const users = JSON.parse(localStorage.getItem("users") || "{}");
      if (!currentUser || !users[currentUser]) return;

      const expected = (users[currentUser].securityAnswer || "")
        .trim()
        .toLowerCase();
      const got = (securityAnswerInput.value || "").trim().toLowerCase();

      if (got && got === expected) {
        resetMessage.textContent = "驗證成功,請輸入新密碼";
        resetMessage.style.color = "green";
        newPasswordSection.classList.remove("hidden");
        newPasswordInput.value = "";
        newPasswordInput.focus();
      } else {
        resetMessage.textContent = "答案錯誤!";
        resetMessage.style.color = "red";
        newPasswordSection.classList.add("hidden");
      }
    });

    // 第三步:設定新密碼
    resetPasswordBtn.addEventListener("click", function () {
      const users = JSON.parse(localStorage.getItem("users") || "{}");
      if (!currentUser || !users[currentUser]) return;

      const newPassword = (newPasswordInput.value || "").trim();
      if (!newPassword) {
        resetMessage.textContent = "新密碼不可為空!";
        resetMessage.style.color = "red";
        return;
      }

      users[currentUser].password = newPassword;
      localStorage.setItem("users", JSON.stringify(users));

      resetMessage.textContent = "密碼已成功重設!請回到登入頁";
      resetMessage.style.color = "green";

      // 收尾
      newPasswordSection.classList.add("hidden");
      securitySection.classList.add("hidden");
      resetForm.reset();
    });
  }

流程解釋

  1. 輸入帳號 → 顯示問題

    resetForm.addEventListener("submit", ...)
    
    • 檢查帳號是否存在
    • 存到 currentUser
    • 顯示該帳號的安全問題
  2. 輸入答案 → 驗證

    checkAnswerBtn.addEventListener("click", ...)
    
    • localStorage 裡的答案比對
    • 正確 → 顯示「新密碼」輸入框
  3. 輸入新密碼 → 更新

    resetPasswordBtn.addEventListener("click", ...)
    
    • 更新 localStorage 裡的密碼
    • 顯示成功訊息

實作流程

  • 註冊一個新帳號 ( 原本密碼是:11 )
    https://ithelp.ithome.com.tw/upload/images/20250926/20169698UdwMlDsmdF.png
  • 登入時 發現發現忘記密碼了
    https://ithelp.ithome.com.tw/upload/images/20250926/20169698r17UD30Sbf.png
  • 輸入帳號名
    https://ithelp.ithome.com.tw/upload/images/20250926/20169698xa29LI4yGo.png
  • 回答問題
    • 答對
      https://ithelp.ithome.com.tw/upload/images/20250926/20169698BOGJwui4qN.png
    • 答錯
      https://ithelp.ithome.com.tw/upload/images/20250926/20169698jbT3leBMXu.png
  • 成功修改 ( 改成密碼為:22 )
    https://ithelp.ithome.com.tw/upload/images/20250926/20169698ctqueWd9Yx.png
    https://ithelp.ithome.com.tw/upload/images/20250926/20169698zP7auo4JWm.png

上一篇
Day25打造一個糖尿病自我監測小工具:從0開始學前端
系列文
打造一個糖尿病自我監測小工具:從0開始學前端26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言