前端练手小项目 todoList 待做事项清单【jQuery+JS+HTML+CSS实现】
💹 完整代码在文章末尾,完整文件在博客主页自取!!!
文章目录:
一:页面展示:
二:功能实现分析
2.1 数据存放形式
2.2 添加数据过程分析
2.3 添加数据
2.4 获取数据 getData
2.5 保存数据 saveData
2.6 渲染页面函数 show
2.7 删除按钮实现
2.8 完成与未完成事件的转化
2.9 其他函数
三:完整代码获取
前言:
近期完成了一个仿 todolist 的一个任务事项管理页面,简洁的布局,斜对称的设计更加美观,仅仅用到了本地存储,纯前端,这篇文章给大家分享源码并分析一下其 jQuery 实现过程
一:页面展示:
- 未添加事项页面:
- 添加事项(回车,点击添加均可触发):
- 做完事项后勾选左侧选项
- 点击右侧橙色按钮删除事项
主题切换(目前写入了两个主题):
- 左侧工具栏操作说明
二:功能实现分析
此版块带大家简单分析一下各个功能的实现原理,其实本案例不是单纯的创建元素添加元素,而是每次操作都从本地存储中拿到数据,然后将数据重新分配加载到页面中,对于要多次获取数据,更新存放数据,加载渲染页面,我们将上述功能封装为函数以便多次调用
2.1 数据存放形式
本案例中数据是存放在 localStorage 本地存储中,格式是数组对象的形式,每一个要添加的数据都包含两个属性放在对象内,再把该对象作为数组元素放入数组内
数据存放形式:
todolist=[{ title:' 要添加的任务 ', done:' false '}]
属性 title done 含义 输入框内输入的要添加的信息 事情是否已完成,完成为 true,未完成为 false
补充:本地存储只能存放字符串类型,而数据更新我们需要在数组的形式上进行,对此我们就需要不断地在数组和字符串之间切换,这就需要两种本地存储的格式转化:
JSON.parse():将字符串转换为原先的格式(作用为从本地存储取出数据后进行数组操作)
JSON.stringify():将其他格式的数据转换为字符串格式(作用为存进本地存储)
2.2 添加数据过程分析
数据添加大致分为以下过程:
//第一步:得到本地存储已存放的数据 getData()//第二步:更新数据,即将输入的内容添加进数据中 数组的push方法添加数据//第三步:保存数据到本地存储 saveData()//第四步:重新渲染加载页面 show()
2.3 添加数据
是使用按下回车添加数据和点击添加建添加数据,两个的实现代码大同小异,只讲解回车添加数据,此内容和上述添加步骤一致,此处只为功能函数的调用,重点在于各个函数的封装
//按回车处理数据 $('.text').on('keydown',function(event){ if(event.keyCode===13){ if($('.text').val()!=''){ //得到原先存储的数据 var local=getData(); //更新本地存储数据 local.push({title:$('.text').val(), done:false}) saveData(local); show() notodo_num(); hasdo_num() isnull() $('.text').val('') }else{ alert('您需要输入内容才能添加!') } } })
2.4 获取数据 getData
获取数据的分析如下:
- 首先我们使用 localstorage 的方法得到存放过数据的数组 ----- todolist
- 如果得到的不为空,说明之前便有数据,只需要将数据从字符串形式转为数组形式即可
- 如果为空,则返回一个空数组
function getData(){ var data=window.localStorage.getItem('todolist') if(data!=null){ data = JSON.parse(data); return data; }else{ return []; } }
2.5 保存数据 saveData
保存数据到本地存储分析如下:
- 首先这个函数是有传参的,调用该函数时要把处理好的数组数据传入
- 然后执行一步转化操作将数组形式转为字符串形式以便传入本地存储
- 最后使用 localstorage 的方法传入本地存储即可
function saveData(arr){ arr=JSON.stringify(arr); window.localStorage.setItem('todolist',arr); }
2.6 渲染页面函数 show
渲染页面过程如下:
- 首先在函数内调用 getData 函数获取到已存储的数据
- 将已完成列表和未完成列表的内容全部清空重新渲染,防止出现重复添加的bug
- 使用 each 方法遍历所有数组对象并一一添加,添加时创建的元素顺便给右侧的删除按钮添加了自定义属性 id,方便后续删除操作使用
- 添加时要判断 数组对象的第二个属性 done,是 false 就添加到未完成队列,是 true 就添加到完成队列
function show(){ var data=getData(); $('.notodo-ul').empty(); $('.hasdo-ul').empty(); $.each(data,function( ndex,item){ if(item.done==true){ var li=$('
') $('.hasdo-ul').prepend(li) }else if(item.done==false){ var li=$(' '+item.title+'-') $('.notodo-ul').prepend(li) } }) } '+item.title+'-
2.7 删除按钮实现
点击按钮删除对应事项的分析:
- 点击删除后弹出确认弹窗,点击确认后执行以下过程
- 先调用了 getData 获取到本地存储内的数据
- 对获取到的数据进行操作,show 函数中我们已经添加了给创建的每一个元素添加了一个自定义属性 id,其中 id 的值就是 each 方法遍历时自动编排的索引值,我们得到点击事项的索引值
- 然后对该索引处的对象进行数组的截取操作 splice 即可去除对应点击的数据
- 然后再 saveData 保存数据,show 渲染数据即可
$('.notodo-ul, .hasdo-ul').on('click','.right-btn',function(){ $('.yn-box').fadeIn(200) $('.mask').show() $('.yes').on('click',function(){ //修改数据 var data=getData(); var index=$(this).attr('id') data.splice(index,1) //保存到本地存储数据 saveData(data) //重渲染页面 show() notodo_num() hasdo_num() isnull()$('.yn-box').fadeOut(200) $('.mask').hide() }) $('.no').on('click',function(){ $('.yn-box').fadeOut(200) $('.mask').hide() }) })
2.8 完成与未完成事件的转化
这一步是容易引起误解的地方,很多人认为该步骤是单单将某个要转换列表的元素转换过去即可,其实不然,我们还是更改过 done 属性后,重新保存数据,渲染页面
完成与未完成转换分析:
- 首先还是获取数据,然后对其进行操作
- 仍然是得到点击的 自定义属性 id 的值,但此处需要通过其兄弟元素删除按钮来间接得到
然后更改其 done 的值为当前 单选框表单的选中状态,正好对应选中的 true 与 false
再接着保存数据,渲染页面即可
//完成未完成转换按钮 $('.notodo-ul, .hasdo-ul').on('click','input',function(){ //获取更改数据 var data=getData() var index=$(this).siblings('.right-btn').attr('id') data[index].done=$(this).prop('checked') //保存数据 saveData(data) //重新渲染页面 show(); notodo_num() hasdo_num() isnull(); })
2.9 其他函数
剩下的函数都通俗易懂不需要过多解释了
判断是否没有数据:
没有数据就要让背景的空提示图显示出来
function isnull(){ if($('.notodo-ul').children().length==0){$('.notodo-null').fadeIn(500) } else if($('.notodo-ul').children().length!=0){ $('.notodo-null').fadeOut(200)}if($('.hasdo-ul').children().length==0){ $('.hasdo-null').fadeIn(500) } else if($('.hasdo-ul').children().length!=0){ $('.hasdo-null').fadeOut(200) } }
更新完成与未完成的个数显示:
function notodo_num(){ var notodonum=$('.notodo-ul').children().length $('.notodo-number').text(notodonum) }//完成个数 function hasdo_num(){ var hasdonum=$('.hasdo-ul').children().length $('.hasdo-number').text(hasdonum) }
更新主题:
$('.pink').on('click',function(){ $('.head-map, .tool-box, .tdlist-brand, .op').css({ 'backgroundColor':'rgb(255, 117, 117)' }) $('.select-text').css({ 'backgroundColor':'rgb(255, 165, 165)' }) $('.value').css({ 'backgroundColor':'#ffe2e2' }) $('.notodo-banner').css({ 'backgroundColor':'#b055e8' }) $('.hasdo-banner').css({ 'backgroundColor':'#b055e8' }) $('.mask').css({ 'backgroundColor':'rgba(0, 0, 0, 0.324)'})})
三:完整代码获取
jQuery + JS文件:
document.addEventListener('DOMContentLoaded',function(){ document.addEventListener('selectstart',function(event){ event.preventDefault(); }) document.addEventListener('contextmenu',function(event){ event.preventDefault(); })})$(function(){ show(); isnull(); notodo_num(); hasdo_num() $('.add-img').on('mouseover',function(){ $(this).prop('src','./img/添加2.png') }) $('.add-img').on('mouseout',function(){ $(this).prop('src','./img/添加1.png') }) // 主题下拉 var flag1=true; $('.select-text').on('click',function(){ if(flag1==true){$('.select').stop().slideDown(600)$('.select-text').text('▲ 切换主题'); flag1=false; }else if(flag1==false){$('.select').stop().slideUp(600)$('.select-text').text('▼ 切换主题');flag1=true; } })// ---------------------------主功能区--------------------------- //数组对象形式存储数据 //按回车处理数据 $('.text').on('keydown',function(event){ if(event.keyCode===13){ if($('.text').val()!=''){ //得到原先存储的数据 var local=getData(); //更新本地存储数据 local.push({title:$('.text').val(), done:false}) saveData(local); show() notodo_num(); hasdo_num() isnull() $('.text').val('') }else{ alert('您需要输入内容才能添加!') } } }) //按下添加建添加数据 $('.add-img').on('click',function(event){ if($('.text').val()!=''){ //得到原先存储的数据 var local=getData(); //更新本地存储数据 local.push({title:$('.text').val(), done:false}) saveData(local); show() notodo_num(); hasdo_num() isnull() $('.text').val('') }else{ alert('您需要输入内容才能添加!') } }) function getData(){ var data=window.localStorage.getItem('todolist') if(data!=null){ data = JSON.parse(data); return data; }else{ return []; } } function saveData(arr){ arr=JSON.stringify(arr); window.localStorage.setItem('todolist',arr); } //更新渲染函数 show data-load() function show(){ var data=getData(); $('.notodo-ul').empty(); $('.hasdo-ul').empty(); $.each(data,function( ndex,item){ if(item.done==true){ var li=$(''+item.title+'- ') $('.hasdo-ul').prepend(li) }else if(item.done==false){ var li=$(''+item.title+'- ') $('.notodo-ul').prepend(li) } }) } //del-right $('.notodo-ul, .hasdo-ul').on('click','.right-btn',function(){ $('.yn-box').fadeIn(200) $('.mask').show() $('.yes').on('click',function(){ //修改数据 var data=getData(); var index=$(this).attr('id') data.splice(index,1) //保存到本地存储数据 saveData(data) //重渲染页面 show() notodo_num() hasdo_num() isnull()$('.yn-box').fadeOut(200) $('.mask').hide() }) $('.no').on('click',function(){ $('.yn-box').fadeOut(200) $('.mask').hide() }) }) //完成未完成转换按钮 $('.notodo-ul, .hasdo-ul').on('click','input',function(){ //获取更改数据 var data=getData() var index=$(this).siblings('.right-btn').attr('id') data[index].done=$(this).prop('checked') //保存数据 saveData(data) //重新渲染页面 show(); notodo_num() hasdo_num() isnull(); }) function isnull(){ if($('.notodo-ul').children().length==0){$('.notodo-null').fadeIn(500) } else if($('.notodo-ul').children().length!=0){ $('.notodo-null').fadeOut(200)}if($('.hasdo-ul').children().length==0){ $('.hasdo-null').fadeIn(500) } else if($('.hasdo-ul').children().length!=0){ $('.hasdo-null').fadeOut(200) } }//待完成个数 function notodo_num(){ var notodonum=$('.notodo-ul').children().length $('.notodo-number').text(notodonum) }//完成个数 function hasdo_num(){ var hasdonum=$('.hasdo-ul').children().length $('.hasdo-number').text(hasdonum) }//改变主题//粉色系$('.pink').on('click',function(){ $('.head-map, .tool-box, .tdlist-brand, .op').css({ 'backgroundColor':'rgb(255, 117, 117)' }) $('.select-text').css({ 'backgroundColor':'rgb(255, 165, 165)' }) $('.value').css({ 'backgroundColor':'#ffe2e2' }) $('.notodo-banner').css({ 'backgroundColor':'#b055e8' }) $('.hasdo-banner').css({ 'backgroundColor':'#b055e8' }) $('.mask').css({ 'backgroundColor':'rgba(0, 0, 0, 0.324)'})})//原色$('.ori').on('click',function(){ $('.head-map, .tool-box, .tdlist-brand, .op').css({ 'backgroundColor':'rgb(87, 87, 87)' }) $('.select-text').css({'backgroundColor':'rgb(165, 165, 165)' }) $('.value').css({'backgroundColor':'rgb(222, 219, 194)' }) $('.notodo-banner').css({'backgroundColor':'rgb(0, 100, 123)' }) $('.hasdo-banner').css({'backgroundColor':'rgb(0, 94, 41)' }) $('.mask').css({ 'backgroundColor':'rgba(0, 0, 0, 0.324)' })})//操作说明 $('.op').on('click',function(){ $('.op-box').stop().fadeIn(100) $('.mask').stop().show() $('.close').on('click',function(){ $('.op-box').stop().fadeOut(100) $('.mask').stop().hide() }) })})
HTML文件:
todoList 待办事项清单 todoList 待办事项清单 ▼ 切换主题 - 粉色系主题
- ...
- ...
- ...
- ...
- 原色
- ...敬请期待
操作说明 无待完成事项
无已完成事项
todoList@MYT 卡卡西最近怎么样 V1.0.0版本 2022 5.21 确认删除该事项?
x
css文件:
/* todoList 清单css样式文件 */*{ margin: 0; padding: 0;}/* body{ background-color: rgb(196, 196, 196);} *//* 头部导航块 */.head-map{ position: relative; box-sizing: border-box; width: 100%; height: 90px; background-color: rgb(87, 87, 87); border-bottom:8px solid rgb(255, 255, 255)}.map-img{ box-sizing: border-box; position: absolute; top: 15px; left: 40px; width: 47px; height: 47px;}.map-title{ box-sizing: border-box; word-spacing: 3px; letter-spacing: 1px; width: 340px; height: 50px; line-height: 50px; text-align: center; /* background-color: #fff; */ margin-top: 15px; margin-left: 70px; font-size: 24px; font-weight: bold; color: rgb(255, 255, 255);}.text{ position: absolute; right: 420px; top: 22px; box-sizing: border-box; width: 380px; height: 38px; outline:none; line-height:40px ; padding-left: 10px; border-radius:12px ; border: none; font-size: 18px; font-weight: bold; color: rgb(90, 90, 90);}::placeholder{ font-size: 18px; font-weight: 500; color: rgb(122, 122, 122);}.add-img{ box-sizing: border-box; position:absolute; top: 16px; right: 355px; width: 50px; height: 50px; cursor: pointer;}.select-box{ position: absolute; top: 22px; right: 50px; width: 160px; height: 40px; background-color: #fff;}.select-text{ border: 1px solid rgb(63, 63, 63); text-align: center; padding-right: 10px; line-height: 40px; box-sizing: border-box; width: 160px; height: 40px; font-weight: bold; letter-spacing: 1.4px; text-shadow: 1.2px 1.2px 1.2px black; background-color: rgb(165, 165, 165); color: rgb(255, 255, 255); cursor: pointer;}.select-text:hover{ text-shadow: 1.2px 1.2px 1.2px white; text-shadow: none; color: rgb(81, 81, 81); background-color: rgb(193, 193, 193);}.select{ position: absolute; box-sizing: border-box; width: 160px; display: none; z-index: 10;}.select li{ box-sizing: border-box; border-left: 1px solid black; border-right: 1px solid black; width: 160px; height: 40px; border-bottom: 1px solid black; background-color: #fff; list-style: none; text-align: center; line-height: 38px; cursor: pointer;}.select li:last-child{ background-color: rgb(207, 207, 207);}.select li:last-child:hover{ background-color:rgb(207, 207, 207);}.select li:hover{ background-color: rgb(255, 229, 182);}/* 双伪元素解决塌陷 */.clearfix::before,.clearfix::after{ content: ' '; display: table;}.clearfix::after{ clear: both;}.value{ position: relative; width: 100%; background-color: rgb(222, 219, 194); overflow: hidden; /*清除浮动影响*/}.tool-box{ position: relative; float: left; width: 6.6%; height: 758.4px; background-color: rgb(87, 87, 87);}.op{ position: absolute; width: 100%; height: 60px; background-color: rgb(87,87,87); border-bottom: 2px solid white; text-align: center; line-height: 60px; color: white; font-size: 16px; letter-spacing: 1.3px; cursor: pointer;}.right-box{ float: left; width: 93.4%; height: 700px;}.notodo-box{ position: relative; box-sizing: border-box; float: left; width: 50%; height: 700px; overflow: scroll;}.notodo-null{ box-sizing: border-box; position: absolute; top: 180px; left: 200px; width: 260px; height: 320px; display: none;}.notodo-null-img{ position: absolute; top: 50px; left: 60px; width: 135px; height: 135px;}.notodo-null-p{ position: absolute; top: 180px; left: 36px; font-size: 30px; font-weight: bold; color: rgb(134, 134, 134);}.notodo-banner{ position: fixed; top: 90px; left: 98px; background-color: rgb(0, 100, 123); width: 45%; height: 90px; border-radius: 30px; z-index: 11;}.notodo-img{ position: absolute; bottom: 30px; left: 235px; width: 30px; height: 30px;}.notodo-p{ letter-spacing: 2px; position: absolute; font-size: 26px; font-weight: bold; bottom: 30px; left: 270px; color: rgb(233, 233, 233);}.notodo-number{ position: absolute; top: 23px; right: 28px; background-color: #fff; width: 40px; height: 40px; text-align: center; line-height: 40px; border-radius: 100%; font-size: 18px; color: rgb(73, 73, 73); font-weight: bold; background-color: rgb(246, 246, 246);}.hasdo-box{ position: relative; box-sizing: border-box; float: left; width: 50%; height: 700px; overflow: scroll;}.hasdo-null{ box-sizing: border-box; position: absolute; top: 180px; left: 200px; width: 260px; height: 320px; display: none;}.hasdo-null-img{ position: absolute; top: 50px; left: 60px; width: 135px; height: 135px;}.hasdo-null-p{ position: absolute; top: 180px; left: 36px; font-size: 30px; font-weight: bold; color: rgb(133, 133, 133);}.hasdo-banner{ position: fixed; bottom: 76px; right: 21px; background-color: rgb(0, 94, 41); width: 45%; height: 90px; border-radius: 30px; z-index: 11;}.hasdo-img{ position: absolute; top: 30px; left: 235px; width: 30px; height: 30px;}.hasdo-p{ letter-spacing: 2px; position: absolute; font-size: 26px; font-weight: bold; top: 25px; left: 270px; color: rgb(232, 232, 232);}.hasdo-number{ position: absolute; top: 23px; right: 28px; background-color: #fff; width: 40px; height: 40px; text-align: center; line-height: 40px; border-radius: 100%; font-size: 18px; color: rgb(73, 73, 73); font-weight: bold; background-color: rgb(246, 246, 246);}/*notodo ul li */.notodo-ul{ margin-top: 100px; box-sizing: border-box;}.notodo-ul li{ overflow: hidden; position: relative; box-sizing: border-box; border-top-left-radius: none; border-bottom-left-radius: none; border-top-right-radius: 25px; border-bottom-right-radius: 25px; border-top: 1px solid grey; border-bottom: 1px solid grey; border-right: 1px solid grey; border-left: 10px solid rgb(0, 100, 123);; list-style: none; margin-bottom: 12px; margin-left: 27px; width: 610px; background-color: #fff; /* border: 1px solid grey; */ }.notodo-check{ cursor: pointer; float: left; position: absolute; left: 15px; top:50%; transform: translateY(-50%); width: 20px; height: 20px; background-color: rgb(187, 187, 187);}.notodo-main{ padding: 3px; margin-left: 50px; float: left; width: 475px; line-height: 40px; padding-left: 10px; padding-right:10px; letter-spacing: 1px; word-break: break-all; background-color: rgb(239, 239, 239);}.right-btn{ cursor: pointer; position: absolute; right: 15px; top: 50%; transform: translateY(-50%); box-sizing: border-box; border: 1px solid rgb(72, 72, 72); border-radius: 100%; float: left; width: 26px; height: 26px; text-align: center; line-height:20px ; font-size: 20px; font-weight: bold; color: rgb(94, 94, 94); background-color: rgb(255, 210, 98);}.right-btn:hover{ background-color: rgb(255, 244, 180);}/*hasdo ul li */.hasdo-ul{ position: absolute; bottom: 90px; box-sizing: border-box;}.hasdo-ul li{ overflow: hidden; position: relative; box-sizing: border-box; border-top-left-radius: none; border-bottom-left-radius: none; border-top-right-radius: 25px; border-bottom-right-radius: 25px; border-top: 1px solid grey; border-bottom: 1px solid grey; border-right: 1px solid grey; border-left: 10px solid rgb(57, 80, 85);; list-style: none; margin-bottom: 12px; margin-left: 27px; width: 610px; background-color: rgb(205, 205, 205); /* border: 1px solid grey; */ }.hasdo-check{ cursor: pointer; float: left; position: absolute; left: 15px; top:50%; transform: translateY(-50%); width: 20px; height: 20px; background-color: rgb(205, 205, 205);}.hasdo-main{ padding: 3px; margin-left: 50px; float: left; width: 475px; line-height: 40px; padding-left: 10px; padding-right:10px; letter-spacing: 1px; word-break: break-all; /* background-color: rgb(155, 155, 155); */}.right-btn{ cursor: pointer; position: absolute; right: 15px; top: 50%; transform: translateY(-50%); box-sizing: border-box; border: 1px solid rgb(72, 72, 72); border-radius: 100%; float: left; width: 26px; height: 26px; text-align: center; line-height:20px ; font-size: 20px; font-weight: bold; color: rgb(94, 94, 94); background-color: rgb(255, 210, 98);}.right-btn:hover{ background-color: rgb(255, 244, 180);}/* 底部商标 */.tdlist-brand{ box-sizing: border-box; float: left; background-color: rgb(87, 87, 87); width: 93.4%; height: 58.4px; border-left: 3px solid white; text-align: center; line-height:58.4px ;}.brand{ font-size: 6px; color: white;}.yn-box{ z-index: 100; position: absolute; top: 160px; left: 500px; box-sizing: border-box; width: 500px; height: 450px; background-color: rgb(233, 233, 233); border: 1px solid black; border-top: 30px solid rgb(105, 105, 105); display: none;}.yn-text{ position: relative; box-sizing: border-box; width: 498px; height: 300px; background-color: rgb(233, 233, 233);}.yn-img{ position: absolute; top: 85px; left: 180px; width: 135px; height: 135px;}.yn-p{ position: absolute; top: 245px; left: 153px; font-size: 25px; font-weight: bold; color: rgb(98, 98, 98);}.no{ position: absolute; bottom: 43px; left: 40px; width: 180px; height: 60px; background-color: #fff; border: none; background-color: rgb(209, 0, 0); color: white; font-size: 23px; font-weight: bold; text-align: center; line-height: 60px; border-radius: 12px;}.yes{ position: absolute; bottom: 43px; right: 40px; width: 180px; height: 60px; background-color: #fff; border: none; background-color: rgb(0, 169, 34); color: white; font-size: 23px; font-weight: bold; text-align: center; line-height: 60px; border-radius: 12px;}.yes:hover{ background-color: rgb(84, 215, 104); color: rgb(52, 52, 52);}.no:hover{ background-color: rgb(213, 90, 90); color: rgb(42, 42, 42);}.mask{ position: absolute; top: 0; left: 0; z-index: 50; background-color: rgba(0, 0, 0, 0.324); width: 100%; height: 849px; display: none;}.op-box{ display: none; z-index: 101; position: absolute; top: 25px; left: 120px; width: 1200px; height: 800px; border: 1px solid black; background-color: #fff;}.close{ cursor: pointer; z-index: 101; position: absolute; top: 0; right: -25px; width: 20px; height: 20px; border: 1px solid black; background-color: #fff; text-align: center; line-height:16px ; font-size: 20px; font-weight:600;}.op-p{ position: absolute; top: 0; left: 0; width: 800px; height: 300px;}.op-img{ position: absolute; bottom: 50px; left: 40px; width: 700px; height: 450px;}