效果图
代办事项.png
列表条目样式
<li>
<div>
<input class="chk" type="checkbox">
<span class="title">事项1</span>
</div>
<button>X</button>
</li>
根据数据,渲染列表页面
// 渲染函数,根据数据渲染页面
function render() {
// 数据转标签字符串数组
let newArr = todos.map(function (item, index) {
return `
<li>
<div>
<input data-index="${index}" class="chk" type="checkbox" ${item.finished ? "checked" : ""}>
<span class="title">${item.title}</span>
</div>
<button data-index="${index}">X</button>
</li>
`;
});
// 拼接每个标签
let html = newArr.join("");
// 渲染列表
ul.innerHTML = html;
// 统计未完成数量
let unfinishCountValue = 0;
// 统计完成数量
let finishCountValue = 0;
todos.forEach(function (item, index) {
if (item.finished) {
finishCountValue++;
} else {
unfinishCountValue++;
}
});
console.log(`${unfinishCountValue}个未完成`);
console.log(`${finishCountValue}个已完成`);
unfinishCount.innerText = unfinishCountValue;
finishCount.innerText = finishCountValue;
}
// 首次渲染
render();
处理键盘事件
// 监听输入框的键盘事件
wordInput.addEventListener('keyup', function (e) {
// 限定只处理回车
if (e.key === 'Enter') {
// 添加代办事项到数组中
let word = wordInput.value;
// 没有输入内容,不处理
if (word.trim() === '') {
return;
}
// 插入到数组的最前面
todos.unshift({
title: word,
finished: false
});
// 清空输入框
wordInput.value = '';
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
}
});
处理条目点击
// 事件委托,处理条目内的点击
ul.addEventListener('click', function (e) {
let targetEl = e.target;
// 通过自定义属性,取出这一行数据的索引
let index = targetEl.dataset.index;
// 勾选框
if (targetEl.classList.contains('chk')) {
let todo = todos[index];
// 切换选中和未选中
todo.finished = !todo.finished;
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
} else if (targetEl.tagName === 'BUTTON') {
// 删除数组中的数据
todos.splice(index, 1);
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
}
});
清理已完成的事项
// 清理已完成事项
cleanFinishLink.addEventListener('click', function (e) {
// 阻止默认行为
e.preventDefault();
// 是否有一个已完成的代办事项
let hasFinished = false;
// 删除所有已勾选的事项
for (let i = todos.length - 1; i >= 0; i--) {
const todo = todos[i];
if (todo.finished) {
hasFinished = true;
todos.splice(i, 1);
}
}
// 如果没有可清理的已完成代办事项,直接弹窗提示
if (!hasFinished) {
alert('没有一个已完成的代办事项可清理');
return;
}
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
});
完整代码
<!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是给checkbox加的类名,当checkbox勾选时,让兄弟元素span加上中划线 */
.chk:checked+span {
text-decoration: line-through;
}
h1 {
margin-bottom: 10px;
}
/* .chk+span {} */
.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>
<!--
<li>
<div>
<input class="chk" type="checkbox">
<span class="title">事项1</span>
</div>
<button>X</button>
</li>
-->
</ul>
<section>
<span><i>0</i> 未完成</span><a href="#">清理 <b>0</b> 已完成</a>
</section>
</div>
</div>
<script>
/*
整个页面分为4个功能,它整体的操作流程如下
1. 第一是输入框,用于给用户填写具体的待办事项,比如吃饭、睡觉等
2. 第二是切换待办状态,可以实现**已完成**和**未完成**的状态切换
3. 第三是删除功能,用户可以点击对应的一个待办选项,实现删除功能
4. 第四是统计功能,可以显示 **已完成**的待办数量,和 **未完成**的待办数量
5. 第五是清理已完成功能,可以实现 用户移除 **已完成** 的待办事项
*/
// 代办事项列表
const ul = document.querySelector('ul');
// 代办事项输入框
const wordInput = document.querySelector('#word');
// 未完成数量
const unfinishCount = document.querySelector('section span i');
// 已完成数量
const finishCount = document.querySelector('section a b');
// 清理已完成的a标签
const cleanFinishLink = document.querySelector('section a');
// 代办事项数据数组
/*
结构示例:
{
title: '吃饭',
finished: false
}
*/
const todos = JSON.parse(localStorage.getItem('todos')) || [];
// 同步数据到本地
function syncData2Local() {
localStorage.setItem('todos', JSON.stringify(todos));
}
// 渲染函数,根据数据渲染页面
function render() {
// 数据转标签字符串数组
let newArr = todos.map(function (item, index) {
return `
<li>
<div>
<input data-index="${index}" class="chk" type="checkbox" ${item.finished ? "checked" : ""}>
<span class="title">${item.title}</span>
</div>
<button data-index="${index}">X</button>
</li>
`;
});
// 拼接每个标签
let html = newArr.join("");
// 渲染列表
ul.innerHTML = html;
// 统计未完成数量
let unfinishCountValue = 0;
// 统计完成数量
let finishCountValue = 0;
todos.forEach(function (item, index) {
if (item.finished) {
finishCountValue++;
} else {
unfinishCountValue++;
}
});
console.log(`${unfinishCountValue}个未完成`);
console.log(`${finishCountValue}个已完成`);
unfinishCount.innerText = unfinishCountValue;
finishCount.innerText = finishCountValue;
}
// 监听输入框的键盘事件
wordInput.addEventListener('keyup', function (e) {
// 限定只处理回车
if (e.key === 'Enter') {
// 添加代办事项到数组中
let word = wordInput.value;
// 没有输入内容,不处理
if (word.trim() === '') {
return;
}
// 插入到数组的最前面
todos.unshift({
title: word,
finished: false
});
// 清空输入框
wordInput.value = '';
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
}
});
// 事件委托,处理条目内的点击
ul.addEventListener('click', function (e) {
let targetEl = e.target;
// 通过自定义属性,取出这一行数据的索引
let index = targetEl.dataset.index;
// 勾选框
if (targetEl.classList.contains('chk')) {
let todo = todos[index];
// 切换选中和未选中
todo.finished = !todo.finished;
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
} else if (targetEl.tagName === 'BUTTON') {
// 删除数组中的数据
todos.splice(index, 1);
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
}
});
// 清理已完成事项
cleanFinishLink.addEventListener('click', function (e) {
// 阻止默认行为
e.preventDefault();
// 是否有一个已完成的代办事项
let hasFinished = false;
// 删除所有已勾选的事项
for (let i = todos.length - 1; i >= 0; i--) {
const todo = todos[i];
if (todo.finished) {
hasFinished = true;
todos.splice(i, 1);
}
}
// 如果没有可清理的已完成代办事项,直接弹窗提示
if (!hasFinished) {
alert('没有一个已完成的代办事项可清理');
return;
}
// 同步数据到本地
syncData2Local();
// 重新渲染
render();
});
// 首次渲染
render();
</script>
</body>
</html>
网友评论