相关技术
- html
- css
- JavaScript
- 数据驱动视图
数据驱动视图是指,页面随着数据的变化而变化
需求
- 有一个输入框,用于给用户填写具体任务
- 任务的状态可以切换(已完成/未完成)
- 可以删除已经输入的任务
- 分别统计已完成的任务和未完成的任务
- 可以把已完成的任务清理掉
- 页面打开后保持和页面关闭前前状态一致
实现
搭建静态结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no"
/>
<title>04-待办列表.html</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #ccc;
}
ul {
list-style: none;
}
li {
padding: 20px;
text-align: left;
font-size: 30px;
border-bottom: 1px dashed #ccc;
display: flex;
justify-content: space-between;
align-items: center;
}
li input {
margin-right: 10px;
}
li button {
display: none;
padding: 5px;
}
li:hover button {
display: inline-block;
cursor: pointer;
}
.chk:checked + span {
text-decoration: line-through;
}
h1 {
margin-bottom: 10px;
}
.chk + span {
width: 100%;
}
.box {
background-color: #fff;
width: 60vw;
padding: 20px 20px 0;
margin: 50px auto;
}
.box .tool input {
width: 100%;
height: 50px;
text-indent: 20px;
font-size: 20px;
font-style: italic;
color: #666;
font-weight: 700;
}
section {
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
}
a {
text-decoration-color: #666;
color: inherit;
}
</style>
</head>
<body>
<div id="app" data-v-app="">
<div class="box">
<h1>任务列表</h1>
<div class="tool">
<input
id="word"
autofocus=""
type="text"
placeholder="请输入代办事项"
/>
</div>
<ul></ul>
<section>
<span><i>1</i> 未完成</span><a href="#">清理 <b>0</b> 已完成</a>
</section>
</div>
</div>
</body>
</html>
封装函数
统计数量的函数
function total() {
const inputs = document.querySelectorAll(".item");
const i = document.querySelector("i"); // 未完成
const b = document.querySelector("b"); // 已完成
const unfinished = [];
const finished = [];
for (let i = 0; i < inputs.length; i++) {
inputs[i].checked
? finished.push(inputs[i])
: unfinished.push(inputs[i]);
}
i.innerText = unfinished.length;
b.innerText = finished.length;
}
渲染函数,把数据渲染到页面上
const ul = document.querySelector("ul"); // 任务列表
function render() {
const lis = item.map((ele, i) => {
return `
<li data-id=${i}>
<input type="checkbox" class="item chk"
${ele.isChecked ? "checked" : ""}
/>
<span>${ele.msg}</span>
<button>❌</button>
</li>
`;
});
ul.innerHTML = lis.join("");
total();
}
实现功能
声明一个数组用来存放数据
const item = JSON.parse(localStorage.getItem("item")) || [];
由于要使页面打开后和关闭前状态一致,这里我们使用到了本地存储
实现添加任务功能
const word = document.querySelector("#word"); // 输入框
word.addEventListener("keyup", function (e) {
if (e.key === "Enter") {
item.unshift({
msg: this.value,
});
localStorage.setItem("item", JSON.stringify(item));
render();
this.value = "";
}
});
用户按下回车键进行添加任务
实现删除任务功能
const ul = document.querySelector("ul"); // 任务列表
ul.addEventListener("click", function (e) {
if (e.target.tagName === "BUTTON") {
if (confirm("是否确认删除该事项?")) {
item.splice(e.target.dataset.id, 1);
localStorage.setItem("item", JSON.stringify(item));
render();
}
}
});
使用了事件委托
实现清理已完成功能
const a = document.querySelector("a"); // 清理按钮
a.addEventListener("click", function (e) {
item.forEach((ele, i) => {
if (ele.isChecked) {
item.splice(i, 1);
localStorage.setItem("item", JSON.stringify(item));
render();
}
});
});
实时统计已完成数量和未完成数量
this.addEventListener("change", function (e) {
const inputs = document.querySelectorAll(".item");
total();
item.forEach((ele, i) => {
ele.isChecked = inputs[i].checked;
});
localStorage.setItem("item", JSON.stringify(item));
});
当页面发生变化时重新统计数量
完整JavaScript代码
<script>
// 获取元素
const word = document.querySelector("#word"); // 输入框
const ul = document.querySelector("ul"); // 任务列表
const a = document.querySelector("a"); // 清理按钮
const item = JSON.parse(localStorage.getItem("item")) || []; // 存放事项
render();
// 添加
word.addEventListener("keyup", function (e) {
if (e.key === "Enter") {
item.unshift({
msg: this.value,
});
localStorage.setItem("item", JSON.stringify(item));
render();
this.value = "";
}
});
// 删除
ul.addEventListener("click", function (e) {
if (e.target.tagName === "BUTTON") {
if (confirm("是否确认删除该事项?")) {
item.splice(e.target.dataset.id, 1);
localStorage.setItem("item", JSON.stringify(item));
render();
}
}
});
// 统计数据变换
this.addEventListener("change", function (e) {
const inputs = document.querySelectorAll(".item");
total();
item.forEach((ele, i) => {
ele.isChecked = inputs[i].checked;
});
localStorage.setItem("item", JSON.stringify(item));
});
// 清理已完成功能
a.addEventListener("click", function (e) {
item.forEach((ele, i) => {
if (ele.isChecked) {
item.splice(i, 1);
localStorage.setItem("item", JSON.stringify(item));
render();
}
});
});
// 渲染函数
function render() {
const lis = item.map((ele, i) => {
return `
<li data-id=${i}>
<input type="checkbox" class="item chk"
${ele.isChecked ? "checked" : ""}
/>
<span>${ele.msg}</span>
<button>❌</button>
</li>
`;
});
ul.innerHTML = lis.join("");
total();
}
// 统计函数
function total() {
const inputs = document.querySelectorAll(".item");
const i = document.querySelector("i"); // 未完成
const b = document.querySelector("b"); // 已完成
const unfinished = [];
const finished = [];
for (let i = 0; i < inputs.length; i++) {
inputs[i].checked
? finished.push(inputs[i])
: unfinished.push(inputs[i]);
}
i.innerText = unfinished.length;
b.innerText = finished.length;
}
</script>
总结
- 使用数据操控页面变化比直接用DOM元素反复调整页面变化方便
- 需要反复使用的一段代码可以封装到一个函数里
- 要实现每次打开页面保持和关闭前一致的话,每当数据发生变化时就要存一次数据到本地存储里