querySelector 抓不到新元素?在昨天的文章中,我們解決了 DOM 節點抓取(null 值存取錯誤)與 HTML 無障礙標籤(<label for="...">)的坑,成功讓「新增藥品卡片」的功能順利啟動。
有了新增,當然不能少了刪除!今天我們要完成 Tool 3 的最後一塊互動拼圖:
❌ 按鈕,刪除單一藥品並同步 LocalStorage。confirm() 二次確認機制。querySelector 抓不到新元素?在實作刪除按鈕時,前端新手最常碰到的問題是:
「明明在 JS 寫了
document.querySelectorAll('.btn-del').forEach(...),為什麼後續點按鈕『➕ 加入時間表』新增出來的藥品卡片,點擊❌卻完全沒有反應?」
核心原因:執行時間差
在網頁初始化執行 JavaScript 時,那些動態生成的藥品卡片根本還沒被建立!querySelectorAll 只會抓取「執行當下」已存在於 DOM 樹上的元素,對未來才產生的動態元素完全無能為力。
針對動態生成的 DOM 元素,前端主要有兩種經典解法:
事件代理 (Event Delegation)
將監聽器綁定在靜態存在 Parent 容器(例如 <table> 或卡片外層格)上,利用 JavaScript 的 事件冒泡 (Event Bubbling) 機制,當子元素被點擊時,由父元素統一捕捉並檢查 e.target。
動態生成時綁定 (Element-level Binding)
在 JavaScript 使用 document.createElement() 建立卡片的當下,同步使用 .addEventListener() 為該卡片內部的按鈕綁定專屬點擊事件。
單筆刪除:Array.prototype.filter() 陣列過濾JavaScript
刪除的思維不是直接去手動操作 DOM(.remove()),而是修改數據本身:
chip.querySelector(".btn-del").addEventListener("click", (e) => {
const idToDel = Number(e.target.getAttribute("data-id"));
medList = medList.filter(item => item.id !== idToDel);
saveAndRender();
});
💡 踩坑經驗提醒:HTML 中的
data-id取出來為String型態,而Date.now()產生的 ID 為Number型態。若使用嚴格相等的過濾條件!==,記得先透過Number()進行轉型!
原生 confirm() 防誤觸機制JavaScript
清空全天時間表屬於毀滅性操作(Destructive Action),透過原生的 confirm() 方法進行攔截:
if (confirm("確定要清空用藥時間表中的所有藥品嗎?此動作無法復原!")) {
medList = [];
saveAndRender();
}
confirm() 會暫停當前 JavaScript 執行緒並回傳布林值:點擊「確定」回傳 true,點擊「取消」回傳 false,簡單且有效率。
今天我們完成了 Tool 3 的完整 CRUD(新增、讀取、刪除)互動流程。
透過這次實作可以體會到,保持 「單一真實數據源 (Single Source of Truth)」 的概念極為重要:
medList 陣列,最後統一呼叫 saveAndRender() 重新渲染 UI。這樣不僅畫面與 LocalStorage 能達到 100% 同步,未來要加入「藥品排序」或「編輯備註」等進階功能時,也能輕鬆擴充。