# 必學 Todo List JavaScript 必修篇 - 前端修練全攻略活動到了尾聲,開始實作一個 Todo List ,跟著艾草助教的步驟進行。 ## 艾草提醒思考步驟 1. 新增待辦事項 :::spoiler - 註冊監聽點擊按鈕事件: - 必須選取 button - 必須監聽 button 點擊事件 - 監聽函式內要先組物件,物件內包含: - input 欄位的值(記得先取出) - id (未來會需要透過 id 比對資料) - 紀錄完成狀態(ckeckbox 有沒有打勾勾) - 防呆,確認 input 欄位有值 - 有值的情況下,將剛組好的物件推到全域變數(記得宣告一個全域變數並賦予值為空陣列) - 確認 HTML 結構,看要渲染的資料要擺在哪 - 宣告一個函式 render 用來渲染資料 - 將剛組好的資料拿來跑 forEach - 將全域變數陣列內的 input 欄位值依需求擺放進要渲染的字串內 - id 、 紀錄完成狀態等資訊可以等後續用到時再來補埋 ::: 2. 刪除單筆/切換打勾 :::spoiler * 監聽外層 ul 的點擊事件 * 取出點擊時該筆 li 的 id(此 id 需提前透過 `data-"自定義名稱"` 埋進渲染的字串內) * 刪除功能 * 確認點擊到的是打叉按鈕 * 找出點擊到的該筆資料的索引值 * 刪除該筆資料 * 切換打勾功能 * 確認點擊到哪筆資料(可透過 id 比對) * 點擊時新增 "checked" 屬性至 input checkbox 內(需提前埋進渲染的字串內) * 補充:input checkbox 的屬性 checked ,可以使 checkbox 呈現打勾 * 重新渲染 ::: 3. 切換 tab &修改完成狀態 ::: spoiler * 切換 tab:監聽是否點擊到全部/待完成/已完成區塊 * 取出埋藏的 tab 狀態(在 HTML 透過 `data-"自定義屬性"` 埋藏 tab 的狀態) * 透過 querySelectorAll 選取所有 tab * 先取消全部 tab 的 class 類別 active * 給點擊到的 tab 新增 class 類別為 active * 修改完成狀態 * 建立一個新函式處理修改完成狀態 * 透過 tab 狀態去判斷是否完成 * 透過判斷是否打勾來顯示對應資料(可使用 filter) * 最後呼叫 render 函式渲染 filter 後的資料 * 將原先其他功能的 render 渲染函式呼叫取消,替換成呼叫此新函式 * 計算待完成項目 * 透過計算陣列中未打勾狀態的長度,計算待完成筆數 * 將筆數賦予到 DOM 節點上 ::: 4. 刪除全部&優化 :::spoiler * 監聽點擊清除已完成項目按鈕 * 點擊後刪除已完成項目(可透過 filter 篩選出未完成項目後重新渲染) * 優化:鍵盤事件 Enter 也能新增待辦事項 * 可透過監聽鍵盤事件 * 鍵盤按下 Enter 後呼叫新增待辦事項函式 ::: 5. 刪除全部&優化 :::spoiler - 監聽點擊清除已完成項目按鈕 - 點擊後刪除已完成項目(可透過 filter 篩選出未完成項目後重新渲染) - 優化:鍵盤事件 Enter 也能新增待辦事項 - 可透過監聽鍵盤事件 - 鍵盤按下 Enter 後呼叫新增待辦事項函式 ::: ## Todo List Start ! <iframe height="300" style="width: 100%;" scrolling="no" title="TodoList" src="https://codepen.io/unayo/embed/YzQWNGP?default-tab=result&theme-id=dark" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/unayo/pen/YzQWNGP"> TodoList</a> by unayo (<a href="https://codepen.io/unayo">@unayo</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> ## 補充 艾草: 如果真的想不出來卡關了,也不要灰心,這裡有可愛的 Vtuber 艾拉的 👉 [todoList 教學](https://courses.hexschool.com/courses/202011122/lectures/34213327) 一天實作一點小功能,很快就完成囉 ### 其他寫法 刪除 : ```javascript= todoList.addEventListener('click', deleteAndChecked); function deleteAndChecked(e) { let id = e.target.closest('li').dataset.id; if (e.target.classList.value == 'delete') { e.preventDefault(); // 篩選:我們需要的是 『點到的 ID 要刪除』 // i.id != id ,篩選『點到的ID 不是那個ID』 // 就會變成留下沒有點到的 ID // 也就是刪掉點到的 ID todoData = todoData.filter((i) => i.id != id); } else { // 切換 checked 狀態功能 todoData.forEach((i, index) => { if (i.id == id) { if (todoData[index].checked == 'checked') { todoData[index].checked = ''; } else { todoData[index].checked = 'checked'; } } }); } updateList(); } ``` ###### tags: `JS` {%hackmd @unayojanni/H1Qq0uKkK %}