共计 11903 个字符,预计需要花费 30 分钟才能阅读完成。
🌏个人博客主页:
前言:前段时间学习了 JavaScript,然后写了一个 todoList 小项目,现在和大家分享一下我的清单以及如何实现的,希望对大家有所帮助
🔥🔥🔥文章专题:todoList 清单
😽感谢大家的点赞👍收藏⭐️评论✍您的一键三连是我更新的动力 💓
清单效果:
页面展示:
tab- 工作 -tab- 生活 -tab- 学习 -tab- 健康:
功能介绍和代码详细解释:
我一共写了 4 个页面,但是每个页面都是基本一样的,所以我通过第一个页面向大家展示我的功能都有哪些并且是如何实现的
大体思路:
现在我来介绍一下我大体是如何实现的,首先我创造两个 二维数组 (一个 arrList1 数组储存我的未完成内容,一个 arrList2 数组储存我的已完成内容),还有一个 全局变量 dataId,dataId 在这里的作用非常大,dataId通过 tab 点击可以改变我的 dataId 的内容 ,然后显示我的第几个页面和改变我的二维数组的第一个下标的值( 四个页面我都设置了相 class=“note” 属性,所以可以通过 tab 关联的 dataId 显示我的第几个 note 和转换我的第几个数组)
提示:(第一个页面是我的 arrList1[0]和 arrList2[0])
每一个页面的内容的存储都相当于两个一维数组
1 全局 dataId 和二维数组显示:
// 判断到第几个 note
let dataId = 1
// 定义数组
if (!this.localStorage.getItem('arr')) {localStorage.setItem('arr', "[[], [], [], []]")
} else if (!this.localStorage.getItem('arr1')) {localStorage.setItem('arr1', "[[], [], [], []]")
}
// 我的未完成数组
const arrList1 = JSON.parse(localStorage.getItem('arr'))
// 我的已完成数组
const arrList2 = JSON.parse(localStorage.getItem('arr1'))
2 更新未完成和已完成:
因为更新已完成和未完成相似,所以下面我就通过更新未完成来和大家讲解,关于更新添加,我是通过将我的本地存储解析成数组然后遍历将这些内容通过 join 返回成一个字符串,添加到我的 un_ul 里面,最终显示在页面当中
// 更新未完成
function renderUnfinished() {let un_ul = document.querySelector(`.note:nth-child(${dataId}) .uul-unfinished`);
const listItems = JSON.parse(localStorage.getItem('arr'))[dataId - 1].map((taskText, index) => {
return `
${taskText}
定时
`
}).join('')
console.log(JSON.parse(localStorage.getItem('arr'))[dataId - 1])
un_ul.innerHTML = listItems
}
// 更新已完成
function renderFinished() {let ul = document.querySelector(`.note:nth-child(${dataId}) .uul-finished`)
const listItems = JSON.parse(localStorage.getItem('arr1'))[dataId - 1].map((taskText, index) => {
return `
${taskText}
定时
`
}).join('')
ul.innerHTML = listItems
}
3 input 输入框回车输入内容:
第一步获得我的 input 标签,然后我给我的 input 标签添加键盘事件监听,如果监听到 点击键盘的回车 Enter 键 ,并且我的内容不为空, 然后就添加给我的数组和我的本地存储,并且通过我的 renderUnfinished 方法进行显示,最后将我的 input 标签内容变为空
// 如果按下了回车事件
let inputTask = document.querySelector('#task')
inputTask.addEventListener('keydown', function (e) {if (e.key === 'Enter') {let newTaskText = inputTask.value.trim()
if (newTaskText !== '') {arrList1[dataId - 1].push(newTaskText)
localStorage.setItem('arr', JSON.stringify(arrList1))
// 更新未完成事件界面
renderUnfinished()
// 将 input 设为空
inputTask.value = ''
}
}
})
4 点击删除未完成:
首先给父级元素添加点击事件,通过 dataID 来获得是第几个 li 标签然后删除该位置的数组内容 (dataID 和一维数组是关联的)通过 dataID 可以当中数组下标得到内容, 所以可以直接使用 dataID 进行数组操作,定位到第几个内容然后直接进行删除操作,然后改变本地存储,最后进行遍历显示
// 删除未完成
//un_ul 是存储内容 li 的父级 ul 标签,这里使用冒泡来获得 li
un_ul.addEventListener('click', function (e) {
// 判断点击的是否是 delete 小图标
if (e.target.classList.contains('delete')) {let dataID = parseInt(e.target.dataset.id)
arrList1[dataId - 1].splice(dataID, 1)
console.log(arrList1)
// 往本地存储添加相同密匙的内容只会覆盖
localStorage.setItem('arr', JSON.stringify(arrList1))
renderUnfinished()}
})
5 点击删除已完成:
这里的操作和上面的删除未完成操作一样,只是获取的父级元素不一样
// 删除已完成
ul.addEventListener('click', function (e) {if (e.target.classList.contains('delete')) {let dataID = parseInt(e.target.dataset.id)
// console.log(dataID)
arrList2[dataId - 1].splice(dataID, 1)
localStorage.setItem('arr1', JSON.stringify(arrList2))
renderFinished()}
})
6 删除全部未完成:
这个就是点击全部已完成的按钮,然后将数组和本地存储清空,最后进行更新
// 删除全部已完成
text1.addEventListener('click', function (e) {if (e.target.tagName === 'SPAN') {arrList2[dataId - 1] = []
localStorage.setItem('arr1', JSON.stringify(arrList2))
renderFinished()}
})
7 点击圆形图标未完成变成已完成:
这个就是点击 li 标签的第一个小图标,然后使得该内容从未完成变成已完成,内部操作就是点击该图标,通过 dataName 为下标来获得该处的内容,然后将该内容添加到已完成的数组里面,最后将该内容在未完成里面删除,最后修改本地存储,然后进行更新
// 点击未完成按钮任务变成完成
un_ul.addEventListener('click', function (e) {if (e.target.classList.contains('finished')) {let dataName = parseInt(e.target.dataset.name);
arrList2[dataId - 1].push(arrList1[dataId - 1][dataName]);
arrList1[dataId - 1].splice(dataName, 1);
localStorage.setItem('arr1', JSON.stringify(arrList2));
localStorage.setItem('arr', JSON.stringify(arrList1));
renderFinished();
renderUnfinished();}
})
8 时间提醒:
这个是我的 select 标签里面的 option 进行的 change 才会触发事件监听,虽然还是冒泡,但是在 if 语句里面进行了判断,开始获得事件,然后添加了计时器将时间每秒减一次,当时间为 0 时重复上面的未完成功能到已完成的操作
// 时间提醒
un_ul.addEventListener('change', function (e) {if (e.target && e.target.classList.contains('timeNum')) {
let timer = +e.target.value;
let time = setInterval(function () {
timer--;
let dataName = parseInt(e.target.dataset.name);
if (timer === 0) {arrList2[dataId - 1].push(arrList1[dataId - 1][dataName]);
arrList1[dataId - 1].splice(dataName, 1);
localStorage.setItem('arr', JSON.stringify(arrList1));
localStorage.setItem('arr1', JSON.stringify(arrList2));
renderFinished();
renderUnfinished();
alert('滴滴,时间到');
clearInterval(time);
}
}, 1000)
}
})
9 计时器:
获得当前时间进行显示,然后通过计时器每秒更改一次时间
// 显示当前时间
function showTime() {let date = new Date()
// 年月日
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
// 时分秒
let hour = date.getHours()
hour = hour '+ year +'-'+ month +'-'+ day +' '+ hour +':'+ minute +':'+ second +''
}
let ShowTime = window.setInterval(showTime, 1000)
全部代码展示:
HTML:
工作
生活
学习
健康
未完成
已完成
删除全部
未完成
已完成
删除全部
未完成
已完成
删除全部
未完成
已完成
删除全部
CSS:
* {
margin: 0;
margin: 0;
box-sizing: border-box;
}
li {list-style: none;}
a {text-decoration: none;}
body {
background-color: #fffbfb;
background-image: url(./image/bigbgc.png);
height: 1300px;
background-repeat: no-repeat;
background-size: cover;
user-select: none;
}
#date {
text-align: center;
height: 80px;
font-size: 50px;
color: #fff;
}
.entire {
margin: auto;
width: 600px;
height: 1100px;
border-radius: 45px;
border: 1px solid rgb(228, 228, 255);
box-shadow: 5px 5px 15px rgb(201, 198, 198), -5px -5px 10px #fff;
background-color: #ffefef;
/* background-image: url(./image/bgc.png);
background-size: contain; */
}
.task-icon {
cursor: pointer;
font-family: 'icomoon';
display: inline-block;
width: 40px;
height: 40px;
font-size: 40px;
text-align: center;
border-radius: 20px;
/* box-shadow: 5px 5px 5px rgb(255, 255, 255), 5px 5px 5px #fffdfd; */
color: #ffffff;
margin-right: 10px;
margin-bottom: 9px;
}
.life-finished .task-icon,
.life-unfinished .task-icon {color: #ffd1d1;}
.health-finished .task-icon,
.health-unfinished .task-icon {color: #dde6ff;}
.title {
margin: auto;
width: 400px;
height: 100px;
border-radius: 40px;
text-align: center;
box-shadow: 5px 5px 15px rgb(201, 198, 198), -5px -5px 10px #fff;
margin-bottom: 20px;
}
/* .title span {
color: #d27171;
font-size: 30px;
margin-top: 5px;
text-align: center;
vertical-align: middle;
} */
.title .pic {
width: 80px;
height: 80px;
vertical-align: middle;
margin-top: 10px;
margin-right: 10px;
}
.title .pic1 {
border-radius: 12px;
width: 160px;
vertical-align: middle;
margin-top: 10px;
margin-right: 10px;
}
.header input {
margin-left: 105px;
padding-left: 15px;
width: 390px;
height: 55px;
border-radius: 25px;
border: none;
outline: none;
font-size: 22px;
color: #e09191;
background-color: #ffefef;
box-shadow: 5px 5px 15px rgb(219, 218, 218) inset, -5px -5px 10px #fff inset;
}
input::placeholder {
color: #f2b1b1;
font-size: 21px;
}
.tabs {
margin-top: 35px;
display: flex;
gap: 10px;
border-bottom: 2px solid #f0b2b2;
}
.tabs div {
padding: 10px 20px;
background-color: #ffefef;
border: 2px solid #e7b3b3;
border-top-left-radius: 36px;
border-top-right-radius: 36px;
border-bottom: none;
cursor: pointer;
color: #e59898;
box-shadow: 5px 5px 15px rgb(250, 241, 241) inset, -5px -5px 10px #fff inset;
}
.tabs .active {background-color: #ffd5d5;}
.timing {
width: 140px;
text-align: center;
}
#timing-list {
width: 60px;
color: rgb(206, 168, 168);
outline: none;
border-color: rgb(228, 128, 128);
border-radius: 10px;
margin-top: 13px;
}
.content {padding-top: 15px;}
.note {
padding-top: 20px;
margin: auto;
width: 560px;
height: 790px;
border: 3px solid #f7dfdf;
border-radius: 40px;
}
.note:nth-child(1) {background-color: #ffdddd;}
.note:nth-child(2) {background-color: #fffefe;}
.note:nth-child(2) .center-item {color: #da5d5d;}
.note:nth-child(3) {background-color: #ffdddd;}
.note:nth-child(3) .center-item {color: #ffffff;}
.note:nth-child(4) {background-color: #f8f9ff;}
.note:nth-child(4) .center-item {color: #7d9fb8;}
/* 设置背景图片 */
.work-unfinished,
.work-finished {background-image: url(./image/bgc.png);
}
.life-finished,
.life-unfinished {background-image: url(./image/bgc1.png);
background-size: cover;
}
.study-unfinished,
.study-finished {background-image: url(./image/bgc2.png);
}
.health-unfinished,
.health-finished {background-image: url(./image/bgc3.png);
}
/* 设置完成和未完成的框的大小和样式 */
.work-unfinished,
.work-finished,
.life-unfinished,
.life-finished,
.study-unfinished,
.study-finished,
.health-unfinished,
.health-finished {
padding-top: 10px;
margin: auto;
height: 360px;
width: 510px;
border-radius: 25px;
border: #eadddd solid 2px;
box-shadow: 5px 5px 5px rgb(223, 223, 223) inset, -5px -5px 10px #f2e8e8 inset;
}
/* 设置完成和未完成之间的间隔为 20px */
.work-unfinished,
.life-unfinished,
.study-unfinished,
.health-unfinished {margin-bottom: 20px;}
/* 设置完成和未完成的字体样式 */
.note-text {
margin: auto;
width: 140px;
height: 50px;
border-radius: 22px;
padding-top: 5px;
color: #d58585;
font-size: 26px;
text-align: center;
}
/* 更换第三个标签主题字体颜色 */
.study-unfinished .note-text,
.study-finished .note-text {color: #ffffff;}
/* 更换第一个标签主题颜色 */
.work-finished .note-text,
.work-unfinished .note-text {box-shadow: 5px 5px 5px rgb(248, 157, 157) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #ffdfdf;
}
.life-finished .note-text,
.life-unfinished .note-text {box-shadow: 5px 5px 5px rgb(248, 157, 157) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #fff8f8;
}
.study-unfinished .note-text,
.study-finished .note-text {box-shadow: 5px 5px 5px rgb(207, 161, 161) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #ffc5c5;
}
.health-finished .note-text,
.health-unfinished .note-text {
box-shadow: 5px 5px 5px #e3f1ff inset, -5px -5px 10px #dbdbdb inset;
color: #d6e3ff;
background-color: #ffffff;
}
.work-finished .note-text,
.life-finished .note-text,
.study-finished .note-text,
.health-finished .note-text {margin-left: 185px;}
.uul-unfinished,
.uul-finished {
margin-top: 5px;
display: block;
width: 510px;
height: 295px;
overflow-y: scroll;
overflow-x: hidden;
}
/* 隐藏滚动条 */
.uul-unfinished::-webkit-scrollbar,
.uul-finished::-webkit-scrollbar {width: 0;}
.uul-unfinished li,
.uul-finished li {
padding: 11px;
margin-top: 12px;
margin-left: -10px;
border-radius: 30px;
display: flex;
height: 60px;
width: 450px;
text-align: center;
font-size: 17px;
color: #c06666;
}
.work-unfinished .uul-unfinished li,
.work-finished .uul-finished li {box-shadow: 5px 5px 15px rgb(255, 255, 255) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #ffc3c3;
}
.life-unfinished .uul-unfinished li,
.life-finished .uul-finished li {box-shadow: 5px 5px 5px rgb(255, 211, 211) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #ffffff;
}
.study-unfinished .uul-unfinished li,
.study-finished .uul-finished li {box-shadow: 5px 5px 15px rgb(255, 255, 255) inset, -5px -5px 10px #f2e8e8 inset;
background-color: #ffa3a3;
}
.health-unfinished .uul-unfinished li,
.health-finished .uul-finished li {box-shadow: 5px 5px 15px rgb(255, 255, 255) inset, -5px -5px 10px #e6f8ff inset;
background-color: #ffffff;
}
.center-item {
width: 250px;
height: 20px;
overflow: hidden;
margin-top: 10px;
text-align: left;
}
.uul-finished .center-item {text-decoration: line-through;}
/* .delete {
cursor: pointer;
color: #ffffff;
height: 36px;
width: 36px;
} */
/* 设置变签内容不可看 */
.note {display: none;}
/* 设置变迁内容可看 */
.content .active {display: block;}
/* 设置完成文字图片在一排 */
.text {display: flex;}
/*
.deleteAll {
cursor: pointer;
background-image: url(./image/unfinished.png);
width: 50px;
height: 50px;
} */
.text img {
width: 30px;
height: 30px;
border-radius: 15px;
margin-right: 10px;
margin-left: 10px;
margin-top: 4px;
}
.text span {
width: 100px;
height: 40px;
/* cursor: pointer;/ */
text-align: center;
vertical-align: middle;
color: #8d2222;
font-size: 20px;
padding: 4px;
border-radius: 15px;
border: 1px solid rgb(255, 217, 142);
}
JS:
window.addEventListener('load', function () {
// 判断到第几个 note
let dataId = 1
// 显示时间 1
showTime()
// 定义数组
if (!this.localStorage.getItem('arr')) {localStorage.setItem('arr', "[[], [], [], []]")
} else if (!this.localStorage.getItem('arr1')) {localStorage.setItem('arr1', "[[], [], [], []]")
}
const arrList1 = JSON.parse(localStorage.getItem('arr'))
const arrList2 = JSON.parse(localStorage.getItem('arr1'))
// 初始化所有标签页的任务列表
for (let i = 1; i {
return `
${taskText}
定时
`
}).join('')
console.log(JSON.parse(localStorage.getItem('arr'))[dataId - 1])
un_ul.innerHTML = listItems
}
// 更新已完成
function renderFinished() {let ul = document.querySelector(`.note:nth-child(${dataId}) .uul-finished`)
// if (!ul) {// console.error('Element .note:nth-child(${dataId}) .uul-finished is not found.')
// return
// }
const listItems = JSON.parse(localStorage.getItem('arr1'))[dataId - 1].map((taskText, index) => {
return `
${taskText}
定时
`
}).join('')
ul.innerHTML = listItems
}
// 显示当前时间
function showTime() {let date = new Date()
// 年月日
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
// 时分秒
let hour = date.getHours()
hour = hour '+ year +'-'+ month +'-'+ day +' '+ hour +':'+ minute +':'+ second +''
}
let ShowTime = window.setInterval(showTime, 1000)
// 初始化事件监听器
bindEventListeners()})
到这里就讲完了,感谢大家的观看,希望大家有所收获
原文地址: todoList 清单(HTML CSS JavaScript)