艾草:「もうだめだ。我已经没有梗了,不行了...」
「艾草,醒醒!我们不是说好要一起征服这个世界吗?」
艾草:「我..的魔法作业都做不完啦,呜哇啊啊啊,救我~~」
「...上次的古书好像有提到让人充满动力的魔法 - todo 实作魔法!魔法咒语 ㄉㄡ ㄌ ㄟ ㄇ一ㄙ ㄡ ~~」
艾草:「等一下,先不要 todo 只是拿来压时程的东西啊啊, No ~ 身体自己动起来了!」
(艾草坏掉中)
此次使用六角学院提供於「Vtuber x Coding 蹦出新滋味 ⚙️」影片下方的 todoList 版型,并参考影片内容实作 todolist !
功能切分如下:
input
细节优化首先附上 HTML 程序码:
<div class="container">
<h1>TODO LIST</h1>
<div class="card input">
<input type="text" placeholder="请输入待办事项" id="inputVal" />
<a href="#" class="btn_add" id="addTodoBtn">+</a>
</div>
<div class="card card_list">
<ul class="tab" id="tab">
<li class="active" data-tab="all">全部</li>
<li data-tab="work">待完成</li>
<li data-tab="done">已完成</li>
</ul>
<div class="cart_content">
<ul class="list" id="todoList">
</ul>
<div class="list_footer">
<p><span id="workNum"></span> 个待完成项目</p>
<a href="#" id="deleteBTN">清除已完成项目</a>
</div>
</div>
</div>
</div>
最先开始实作的功能为新增待办事项,实作过程基本如下:
input
栏位输入的值new Date().getTime()
)input
栏位是否有值,有值的情况下,将内容推到全域变数的阵列内forEach
组字串,字串须留意要埋藏checked
属性至 input
栏位(判断完成状态)input
文字内容innerHTML
渲染至网页上,并执行该函式//透过 querySelector 选取 input 栏位
const inputVal = document.querySelector("#inputVal");
//透过 querySelector 选取 button 栏位(新增按钮)
const addTodoBtn = document.querySelector("#addTodoBtn");
//宣告全域变数 todoData 来接组出的物件资料
let todoData = [];
//监听是否点击新增按钮
addTodoBtn.addEventListener("click", addTodo);
//一点击就执行 addTodo()
function addTodo() {
// 组出未来要用到的物件
let todo = {
// input 的值
txt: inputVal.value,
// id 用 getTime() 取毫秒
id: new Date().getTime(),
//纪录待办事项完成状态
complete: false
};
//防呆 确保有填入文字
if (todo.txt.trim() !== "") {
//要塞在第一笔资料,所以用 unshift 把组好的 todo 物件赋予到外层的 todoData
todoData.unshift(todo);
// 把 input 栏位清空
inputVal.value = ""; //清空
}
//跑 render 函式,把外层的 todoData 放进去
render(todoData);
}
//透过 querySelector 选取要放入资料的 ul
const todoList = document.querySelector("#todoList");
//渲染的函式
function render(todo) {
let str = "";
//透过 todoData 跑回圈
todo.forEach((item) => {
//将 todo 的 id 透过 data-id 埋进去
//将是否打勾埋在 input 标签内
//将字放进去
str += `<li data-id="${item.id}">
<label class="checkbox" for="">
<input type="checkbox" ${item.complete ? "checked" : ""}/>
<span>${item.txt}</span>
</label>
<a href="#" class="delete"></a>
</li>`;
});
//最後 innerHTML 把组好的字串赋予给 todoList
todoList.innerHTML = str;
}
补充:
Date
物件:基於世界标准时间(UTC) 1970 年 1 月 1 日开始的毫秒数值来储存时间,可以透过 new Date().getTime()
的方式来当成 id 使用。data-"自定义名称"
:可以拿来埋各种资料进 HTML 结构内。checked
属性:input
checkbox
的属性 checked
,可以使 checkbox
维持打勾执行监听 ul
区域内的删除功能与打勾时完成状态能切换。
实作流程:
ul
区块的点击事件li
的 id 值input
栏位,要透过 closest
才能点击到 li
li
取出的 id 值字串型别转型为数字型别findIndex
比对符合的 id 後使用阵列方法删除该笔资料forEach
比对点击 id 是否符合 Data
内的 id 值true/false
//监听注册 ul todoList 的点击事件
todoList.addEventListener("click", (e) => {
//透过 closest 的方式能找出点击到的 li 标签
//透过 dataset.id 取出埋在该 li 内的 id
//取出来的 id 会是字串型别记得帮它转型成数字型别
let id = parseInt(e.target.closest("li").dataset.id);
//删除功能
//透过 nodeName 确认是否为 A 连结
if (e.target.nodeName === "A") {
e.preventDefault(); //取消 a 标签预设行为
//透过阵法方法 findIndex 比对 todoData 内的 id 是否等於点击到的 id
let index = todoData.findIndex((item) => item.id === id);
//如果是的话删除该笔资料
todoData.splice(index, 1);
} else {
//切换打勾功能
//透过 todoData 去跑 forEach
todoData.forEach((item) => {
//如果 todoData 内的 id 是否等於点击到的 id
if (item.id === id) {
//更改资料是否状态
item.complete ? (item.complete = false) : (item.complete = true);
}
});
}
//重新渲染
render(todoData);
});
补充:
closest
:当在复杂 HTML 结构中想透过 e.target
选取某个 Element
,却都只能选到它的子层时,可以透过 closest
去取到自己想要的父层 Element
。接下来实作点击此区需有状态样式切换,并能筛选出对应资料。
实作流程:
tab
内新增 class
名称为 active
实现
tab
区块status
记录 tab
点击状态并取出该 HTML 结构内埋藏的值,预设为 all
querySelectorAll
选取所有 tabs 状态forEach
先移除所有 tabs
active
样式tab
新增 active
样式透过 status
状态判断点击到的状态为何,并将资料赋予给全域储存待办事项阵列的变数
a. 为 all
全部时显示全域储存变数
b. 为 work
待完成时筛选出未完成状态
c. 都不是时,筛选出已完成状态
透过未完成状态长度筛选出左下角待完成项目,并渲染至网页上
将更新後的资料透过渲染函式执行
重要:透过 updateList()
函式取代掉其它地方之 render(todo)
渲染函式呼叫
//切换 tab
//透过 querySelector 选取 id tab
const tab = document.querySelector("#tab");
//预设显示状态为全部
let status = "all";
//注册监听是否点击到 tab
tab.addEventListener("click", changeTab);
//点击到 tab 就执行 changeTab(e)
function changeTab(e) {
//透过 e.target 将 dataset 埋入的 tab 取出
status = e.target.dataset.tab;
//透过 querySelectorAll 选取 tab 标签底下的 li
let tabs = document.querySelectorAll("#tab li"); //类阵列
//点击时 tab 先清掉全部 class 样式
tabs.forEach((item) => {
//先移除全部的 class active 样式
item.setAttribute("class", "");
});
//有被点击到的才加 class 样式
e.target.setAttribute("class", "active");
//切换页面重新渲染
updateList();
}
//修改完成状态
function updateList() {
//切换不同页面显示资料
let showData = [];
//跟切换 tab 的 status 整合
if (status === "all") {
//状态为全部 "all" 时就全部显示
showData = todoData;
//状态为待完成 "work" 时
} else if (status === "work") {
//筛选出未完成
showData = todoData.filter((item) => !item.complete);
} else {
//筛选出已完成
showData = todoData.filter((item) => item.complete);
}
//计算几个待完成项目 (左下角)
const workNum = document.querySelector("#workNum");
//筛选出未完成的长度
let todoLength = todoData.filter((item) => !item.complete);
//并将长度赋予到该 DOM 节点上
workNum.textContent = todoLength.length;
//渲染 showData
render(showData);
}
updateList(); //初始化页面
此阶段执行清除所有已完成项目,并优化新增功能,使用 enter 按键也能新增待办事项。
实作流程:
input
栏位的键盘事件程序码:
// 清除已完成项目
// 透过 querySelector 选取 id 为 deleteBTN 的 DOM
const deleteBTN = document.querySelector("#deleteBTN");
// 注册监听 deleteBTN 的点击事件
deleteBTN.addEventListener("click", function (e) {
//取消预设效果
e.preventDefault();
//重新将 todoData 赋予未完成的资料
todoData = todoData.filter((item) => !item.complete);
//重新渲染 updateList()
updateList();
});
//点击 Enter 也可以新增资料
//注册监听 inputVal 的键盘 "keyup" 事件
inputVal.addEventListener("keyup", function (e) {
//如果点击到 "Enter"
if (e.key === "Enter") {
//执行新增该笔资料
addTodo();
}
});
补充:
keyup
:键盘事件 keyup
可以拿来侦测是否按下特定键盘,而 keyup
的触发时机为当你按下特定键盘又放开的那刻。todoList 可以练习新增、切换状态、删除功能,後续还可以持续优化新增编辑功能,透过练习 todoList 更了解 JavaScript 魔法!
题目来源 #include <stdio.h> #include <stdlib....
小弟近日工作刚好会用到Azure,公司希望我们可以以考取AZ204为目标,所以接下来会照着这个目标发...
由於我们需要有指标来衡量一个模型的好坏,而问题可以粗略分成「分类」和「回归」问题。而根据不同的问题,...
本文同步更新於blog 情境:玉皇大帝要举办渡河比赛,动物选手各显神通。 <?php na...
画出一张属於自己的学习地图,只有知道自己身在何处,才不会迷路。你必须知道这项学习的终点,该拥有什麽样...