iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
JavaScript

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

【第七課】作品整合 1:個人風格的「待辦事項(To-Do)標籤牆」(DOM 動態新增與刪除)

  • 分享至 

  • xImage
  •  

觀念與函式深度解析

1. document.createElement('tagName') 與 appendChild()
• 為什麼選用這個函式?
當使用者在輸入框輸入文字並點擊新增時,我們需要動態產出一個新的 DOM 節點(例如 <li>),並將它掛載到頁面的清單中。

這個函式在幹嘛?
◦ document.createElement('li'):在記憶體中建立一個指定標籤名稱的全新空元素。
◦ parentElement.appendChild(newChild):將這個新建立的元素塞入指定父元素的最後面。

有沒有其他替代方案?
◦ 方案 A:innerHTML += '<li>...'
▪ 簡介:使用字串拼接的方式直接替換 HTML。
▪ 比較:innerHTML += 會強制瀏覽器重新解析並重繪整份父元素內的 HTML,這會導致原本舊節點上綁定的事件監聽器(EventListener)全部失效。因此動態新增元件時,強烈建議使用 createElement + appendChild。

2. 節點的自我刪除:element.remove()

為什麼選用這個函式?
當使用者點擊待辦事項旁邊的「刪除按鈕」時,我們需要把該筆資料從畫面中永久移除。

這個函式在幹嘛?
直接將呼叫該方法的 DOM 節點從 DOM 樹狀結構中刪除並釋放記憶體。

有沒有其他替代方案?
◦ 方案 A:parentElement.removeChild(child)
▪ 簡介:舊式的 DOM 刪除語法,需要先找到父節點才能刪除子節點。
▪ 比較:element.remove() 語法更簡短、直覺,可以直接讓目標元素自己刪除自己。

完整實作程式碼

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>第七課 - 個人風格待辦事項標籤牆</title>
  <style>
    body { font-family: sans-serif; padding: 40px; background-color: #f0f4f8; }
    .todo-card { width: 450px; background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); margin: 0 auto; }
    
    /* 輸入區域配置 */
    .input-group { display: flex; gap: 10px; margin-bottom: 20px; }
    .todo-input { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 15px; outline: none; }
    .add-btn { padding: 10px 18px; background-color: #2ecc71; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; }
    .add-btn:hover { background-color: #27ae60; }

    /* 標籤牆容器 */
    .tag-wall { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }

    /* 待辦事項標籤樣式 */
    .tag-item {
      display: flex;
      align-items: center;
      gap: 8px;
      background-color: #eef2f7;
      padding: 8px 14px;
      border-radius: 20px;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.2s ease;
      user-select: none;
    }
    .tag-item:hover { background-color: #e2e8f0; }

    /* 已完成狀態樣式 */
    .tag-item.completed {
      background-color: #d1d5db;
      color: #888;
      text-decoration: line-through; /* 刪除線 */
    }

    /* 刪除按鈕樣式 */
    .delete-btn {
      color: #999;
      font-weight: bold;
      border-radius: 50%;
      width: 18px;
      height: 18px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
    }
    .delete-btn:hover { color: #e74c3c; background-color: #fce8e6; }
  </style>
</head>
<body>

  <div class="todo-card">
    <h2>每日待辦標籤牆</h2>
    
    <!-- 輸入框與新增按鈕 -->
    <div class="input-group">
      <input type="text" id="todoInput" class="todo-input" placeholder="新增任務(例如:買咖啡)...">
      <button id="addBtn" class="add-btn">新增</button>
    </div>

    <!-- 動態生成標籤的牆面 -->
    <ul id="tagWall" class="tag-wall"></ul>
  </div>

  <script>
    // ----------------------------------------------------
    // JavaScript 邏輯 (DOM 操作與動態增刪)
    // ----------------------------------------------------

    // 第一步:取得必要的 DOM 元素
    const todoInput = document.getElementById("todoInput");
    const addBtn = document.getElementById("addBtn");
    const tagWall = document.getElementById("tagWall");

    // 核心功能:建立並新增一個待辦標籤
    function createTodoItem() {
      const todoText = todoInput.value.trim();

      // 防呆機制:如果輸入框是空的,不執行任何動作
      if (todoText === "") {
        alert("請輸入待辦事項內容!");
        return;
      }

      // 第二步:使用 createElement 動態建立新的 <li> 標籤
      const li = document.createElement("li");
      li.className = "tag-item";

      // 建立文字節點內容
      const textSpan = document.createElement("span");
      textSpan.innerText = todoText;

      // 建立刪除按鈕 (x)
      const deleteBtn = document.createElement("span");
      deleteBtn.className = "delete-btn";
      deleteBtn.innerText = "✕";

      // 第三步:綁定互動事件

      // 1. 點擊標籤本體:切換「完成/未完成」狀態
      li.addEventListener("click", function() {
        li.classList.toggle("completed");
      });

      // 2. 點擊刪除按鈕:刪除該標籤
      deleteBtn.addEventListener("click", function(event) {
        // 阻止事件冒泡 (避免觸發到外層 li 的完成切換)
        event.stopPropagation();
        // 將整個 li 節點從 DOM 樹中刪除
        li.remove();
      });

      // 第四步:將文字與刪除按鈕組裝進 li,再把 li 丟到 tagWall (ul)
      li.appendChild(textSpan);
      li.appendChild(deleteBtn);
      tagWall.appendChild(li);

      // 第五步:清空輸入框
      todoInput.value = "";
    }

    // 點擊「新增」按鈕觸發事件
    addBtn.addEventListener("click", createTodoItem);

    // 在輸入框按下 Enter 鍵也能觸發新增
    todoInput.addEventListener("keypress", function(event) {
      if (event.key === "Enter") {
        createTodoItem();
      }
    });
  </script>
</body>
</html>

下集預告

我們常常在新聞網站或長文章頂部看到隨著頁面往下捲動而變長的進度條,接下來就要做頁面滾動進度條的功能。

【第八課】視窗控制:頁面滾動進度條(window.scrollY 與百分比計算),我們將學習如何掌控瀏覽器視窗的滾動數據。


上一篇
第六課:【視覺反饋】字數計數器與上限提示
下一篇
【第八課】視窗控制:頁面滾動進度條(window.scrollY 與百分比計算)
系列文
解決學了JavaScript不知道能做什麼:用DOM操作打造實用網頁效果 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言