基于浏览器localStorage作为数据库完成todolsit项目

一、文章内容

TodoList结构搭建HTML代码

TodoList样式编写Css代码

TodoList行为表现JavaScript代码

二、项目展示

项目介绍

Todolist是一个基于B/S模式开发的待办事项软件,主要功能是离线记录用户的待办事项和已经完成的事情,基于html+css+js实现,涉及到的知识主要是事件监听和数据缓冲技术.

项目展示GIF

演示效果new.gif

图1 项目功能展示

项目中的亮点:

  1. 点击代办事项可以让输入框获取焦点,我愿称之为:梦幻联动效果.
  2. 在输入框里按下回城键调用add()函数,自动判断:自动检测.
  3. 在左下角有一个“我要到上面”的标签,点击就可以快速回到顶部,我称为:一键返航.

三、代码细节

Html部分

相信大家在学习过前导课的基础上我们学习下面代码会轻松很多,不过我也会一行一行给大家解释的。
我们讲html页面分为顶部和内容部分,也就是header和section部分,第一个header包裹的内容就是todolist的头部,如图2所示.

image.png

图2 todolist顶部

夹在header和footer中间的section为页面主题部分,也就是项目展示的地方,里面包含了todo和done列表,如图3所示.

image.png

图3 网页中心界面

html框架搭建总体比较简单所以大家可以根据自己的想法设计,或者跟着我这个框架敲一遍.下面是所有的html代码:

  <header><section><div id="form"><label for="title">todo</label><input type="text" id="title" placeholder="输入todolist"></div></section></header><section><h2>需要进行的事情<span id="todocount">0</span></h2><ol id="todolist" class="demo-box"></ol><h2>已经完成的事情<span id="donecount">0</span></h2><ul id="donelist"></ul></section><footer><a href="#top" id="mao">我要到上面</a></footer>

Css样式代码

当然要让css和html联动必须在html中引入自己的css文件,语法如下<link rel="stylesheet" href="main.css">因为我的css样式新建了文件,并且跟index.html在同根目录下,所以可以这样引入,路径不同的读者可以自己修改src的属性值.
如果自己有设计的同学可以直接看下面JavaScript逻辑代码了,这块主要负责美化样式.

body{}选择器是将body标签选中,将内外边距都设置为0,方便后期开发,如果不设置该属性会默认有偏移.剩下俩个是整个网页的字体大小跟背景色全局生效.

html,body{}这个选择器里的属性只有一个scroll-behavior这个在前导课里讲过,功能使得页面滑动平滑.

header{}这个选择器设置了顶部的高度和背景色.

section{}选择器设置了主要内容居中显示,margin:0 auto;auto是关键.

#mao{}id选择器主要通过浮动设置了一键返航的位置.

label{}选择器是待办事项这几个字的位置也是使用了浮动,这是左浮动,通过line-hight设置垂直方向的字体位置,如果跟父级元素高度一样,里面的字就可以实现垂直居中了.cursor:pointer;设置了鼠标悬浮的样式,我记得有6种样式,大家可以自己尝试.

header input{}组合选择器主要负责的是输入框的内容,这里设置了右浮动,同时设置了宽度50%,意思是只占父级元素的一半宽度,text-indent是输入框的缩进,单位像素,自己可以测试一个合适的value,我选择10个像素,border-radius: 5px; 这个属性是输入框的圆角,值越大越⚪.box-shadow: 0 1px 0 rgba(255,255,255,0.24), 0 1px 6px rgba(0,0,0,0.45) inset; 这个属性设置了输入框的盒子阴影,具体参数大家可以去看看CSS box-shadow 属性 (w3school.com.cn).border:none; 这样设置输入框的外边框就没了.

input:focus{}伪类选择器中outline-width: 0; 设置了输入框的边框,也就是当输入框获取焦点,默认是有边框的,这样设置就可以解决这个问题.

h2{}选择器标签只加了一个定位属性,原因是为了span可以很好的定位.

span{}选择器是todolist和donelist的个数,也就是最左边哪个圆点,line-height: 20px; text-align: center;是让文字水平和垂直居中的,其他属性都在上面介绍过,所以不一一介绍了.

ol,ul{}选择器是todolist和donelist列表外部标签list-style: none; 该属性可以让有序列表和无序列表的数字和圆点消失,加强了美观.

li input{}选择器是每个任务item的选择框样式,设置了position:absolute,进行了元素定位,然后设置了鼠标悬浮的样式.

li{}选择器设置了相对的位置,border-radius: 3px;
border-left: 6px solid #FFB800;
border-right : 6px solid #FFB800;
分别设置了选择框的左右边框,和item的圆角.

li:hover{}伪类选择器设置了todolist和donelist中item触摸上去颜色变为蓝色color:#01AAED;

ul li{}组合选择器设置了完成item的透明色和左右边框的颜色border-left: 5px solid #c2c2c2;
border-right: 5px solid #c2c2c2;
opacity: 0.5;

最后使用@media screen and (min-width){}选择器设置了自适应布局.
下面是css的所有代码:

body{padding: 0;margin: 0;font-size: 16px;background:#dddddd;
}
html,body{scroll-behavior: smooth ;
}
header{height: 50px;background: #333;
}
section{margin: 0 auto;
}
#mao{float:right;margin-right: 40px;margin-bottom: 20px;
}
label{float: left;width: 100px;line-height: 50px;color: #DDD;font-size: 24px;cursor:pointer;
}
header input{float: right;width: 50%;height: 25px;margin-top: 12px;/* 所经 text_indent */text-indent: 10px; border-radius: 5px;box-shadow: 0 1px 0 rgba(255,255,255,0.24), 0 1px 6px rgba(0,0,0,0.45) inset;border:none;
}input:focus{outline-width: 0;
}
h2{position: relative ;
}
span{position: absolute;top: 10px;right: 5px;display: inline-block;padding: 0 5px;height: 20px;border-radius: 20px;background-color: seagreen;line-height: 20px;text-align: center;color:whitesmoke;font-size: 14px;
}
ol,ul{padding: 0;list-style: none;
}
li input{position:absolute;top:2px;left: 10px;width: 22px;height: 22px;cursor: pointer;
} 
li{height: 32px;line-height: 32px;background-color: #fff;position: relative;margin-bottom: 10px;padding: 0 45px;border-radius: 3px;border-left: 6px solid #FFB800;border-right : 6px solid #FFB800;
}
li:hover {color:#01AAED;
}ul li{border-left: 5px solid #c2c2c2;border-right: 5px solid #c2c2c2;opacity: 0.5;}
@media screen and (max-device-width: 620px) {section{width:96%;padding:0 2%;}}
@media screen and (min-width: 620px) {section{width:600px;padding:0 10px;}}

JavaScript逻辑代码

站在产品经理角度分析:如何在完成基本需求的基础上,使得用户体验更好?

答案:1.点击todolist(待办事务)输入框自动获取焦点.
2.在输入框里回车即可提交事务,不必加button.3.在任务数量较多的时候提供一家返回功能.

当然在满足上述功能后我们需要弄清楚JavaScript的逻辑代码,其实只有todolist和donelist需要我们更新,只要2者发生变化我们就需要load()加载一次,所以load()需要写为一个函数多次调用,再其次就是将add()按键触发也写为一个函数方便,在里边修改规则,这是简单的解耦,最后我门将pd()和pd2()封装为俩个函数分别将不同checkbox发出的按键逻辑进行代码编写,方便修改对应逻辑.
在明白需求后开始写代码,每个函数都写了一个小标题进行了函数解释,最后将文末的俩行监控代码加进去组成js代码引入到html页面里即可.

未命名文件.png

图4 简易框架图

只要打开页面就要启用监听事件,将load()加载,这样做的好处是无论你是第一次打开网站,还是第二次都可以准确无误的加载item.语法是:**window.addEventListener(“load”, load); //页面加载完毕调用load函数 **

然后监听输入框按键,语法规则document.addEventListener(“keyup”, add); 然后编写对应的add()函数即可.

函数解释load():

第一行和第二行是获取todolist和donelist个数的元素(小圆点),方便下面对数量进行更新,使用localStorage.todo读取本地缓存中的todo数据,如果有数据,进行数据解析将缓冲解析为JavaScript的数组,然后声明一个todohtml变量保存item数据=》然后根据todo的长度for循环,根据规则加li标签最后使用JavaScript操作dom找到todolist的元素将innerhtml赋值为todohtml即可将todolist渲染完成.最后简单判断一下todo的长度,如果有长度,将控制item数量的元素找到然后修改value,如果没长度也就是第一次打开默认为0!todolist和donelist渲染方式一样,我就不多解释了,请认真结合文字读这段代码.你一定可以读懂.

 function load(){todocount = document.getElementById("todocount");donecount = document.getElementById("donecount");if(localStorage.todo){todo = JSON.parse(localStorage.todo);var todohtml = "";for(var i = 0 ; i < todo.length; i++){todohtml += '<li><input type="checkbox" cheched onclick="pd(this)" value="'+ i +'" name="' + todo[i]  + '">' + todo[i] + '</li>';}document.getElementById('todolist').innerHTML = todohtml;if(todo.length!=0) {todocount.innerHTML = todo.length;} else{todocount.innerHTML = 0;}}if(localStorage.done){done = JSON.parse(localStorage.done);var donehtml = "";for(var i = 0 ; i < done.length; i++){donehtml += '<li><input type="checkbox" cheched onclick="pd2(this)" value="'+ i   +'" name="' + done[i]  + '">' + done[i] + '</li>';}document.getElementById('donelist').innerHTML = donehtml;if(done.length!=0){donecount.innerHTML = done.length;}else{donecount.innerHTML = 0 ;}
}
}
函数解释add():

add()函数是当回车键触发且判断加入的事件不为空更新本地缓冲的一个函数并且load().

函数第一行使用JavaScript操作dom获取到input输入框元素,然后判断按键是否为13也就是回车键的Unicode码==13 并且输入框的内容不为空,然后将todo使用堆栈操作添加输入框的内容,接着更新本地缓冲(需要json变为字符串),然后将输入框内容置为空,然后就可以加载load()函数更新页面了.如果不满足条件弹出alert信息框.
完整代码如下:

function add(e){info = document.getElementById('title') ;if(e.keyCode == 13 && info.value != ''){todo.push(info.value);localStorage.todo = JSON.stringify(todo);info.value = '';load();}else if(e.keyCode == 13 && info.value == ''){alert('输入内容提交');}
}
函数解释pd():

细心的读者一定发现这个函数在todolist的每一个item里有个checkbox然后绑定了一个onclicked的事件,当你选择该选择框的时候就会调用pd()函数,该函数是将todolist里的元素删除并且添加到donelist的.

done.push(e.name)将本元素的name加到donelist里,为什么可以这样操作? 因为在load()函数里写了
'<li><input type="checkbox" cheched onclick="pd(this)" value="'+ i +'" name="' + todo[i] + '">' + todo[i] + '</li>';

看到name属性的值是todo[i]item的值,所以可以直接push,然后在todolist里删除该item使用,splice函数,该函数专门为数组设计的用法挺多,详细了解的读者可以自行百度,因为input的value记录的是该item的index下标所以可以直接pareint转换,既然todo和done都更新了,我们接着将浏览器缓冲更新一下就是**localStorage.done = JSON.stringify(done);

localStorage.todo = JSON.stringify(todo);** 这样,接着在列表改动后继续load()重载一下,下面是代码:
function pd(e){done.push(e.name);todo.splice(parseInt(e.value),1);localStorage.done = JSON.stringify(done);localStorage.todo = JSON.stringify(todo);load(); 
}
函数解释pd2():

既然有pd()肯定页有pd2(),一个操作todo里面的checkbox,一个操作done里面的checkbox,好了言归正传,pd2()和pd()操作逻辑一样,但是实体却不一样,一个是todo数组push一个是done数组push,读者可以自行比较,这段代码相对简单,在todo和done数组修改完毕,接着就是更新本地缓冲了,然后load()函数.

function pd2 (e){done.splice(parseInt(e.value),1);todo.push(e.name);localStorage.done = JSON.stringify(done);localStorage.todo = JSON.stringify(todo);load(); 
}

将上述代码和下面这俩行代码组合成一个js文件就是JavaScript所有代码了.

window.addEventListener("load", load); //页面加载完毕调用load函数 
document.addEventListener("keyup", add); //按键监听

能读到这里的小伙伴属实不容易,相信你能坚持这么久你一定可以自己独立开发todolist程序了,恭喜你呀.

四、参照文献

CSS 简介 (w3school.com.cn)

JavaScript 教程 | 菜鸟教程 (runoob.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/761111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】---string的模拟

【C】---string的模拟 一、string类实现1.string类的构造函数2.swap&#xff08;&#xff09;函数3.拷贝构造函数4.赋值运算符重载5.析构6.迭代器7.operator[ ]8.size9.c_str&#xff08;&#xff09;10.reserve&#xff08;&#xff09;11.resize&#xff08;&#xff09;12.p…

flutter 局部view更新,dialog更新进度,dialog更新

局部更新有好几种方法&#xff0c;本次使用的是 StatefulBuilder 定义 customState去更新对话框内容 import package:flutter/cupertino.dart; import package:flutter/material.dart;class ProgressDialog {final BuildContext context;BuildContext? dialogContext;double _…

【DL经典回顾】激活函数大汇总(四十一)(SinReLU附代码和详细公式)

激活函数大汇总(四十一)(SinReLU附代码和详细公式) 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里,激活函数扮演着不可或缺的角色,它们决定着神经元的输出,并且影响着网络的学习能…

8节点空间壳单元Matlab有限元编程 | 曲壳单元 | 模态分析 | 3D壳单元 | 板壳理论| 【源代码+理论文本】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

Mysql的行级锁

MySQL 中锁定粒度最小的一种锁&#xff0c;是 针对索引字段加的锁 &#xff0c;只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小&#xff0c;并发度高&#xff0c;但加锁的开销也最大&#xff0c;加锁慢&#xff0c;会出现死锁。行级锁和存…

数据结构面试常见问题之Insert or Merge

&#x1f600;前言 本文将讨论如何区分插入排序和归并排序两种排序算法。我们将通过判断序列的有序性来确定使用哪种算法进行排序。具体而言&#xff0c;我们将介绍判断插入排序和归并排序的方法&#xff0c;并讨论最小和最大的能区分两种算法的序列长度。 &#x1f3e0;个人主…

Postman接口做关联测试的方法步骤

应用场景 假设下一个接口登录需要上一个接口的返回值&#xff0c;例如请求需要先登录获取到token&#xff0c;下一个请求要携带对应的token才能进行请求 方法&#xff1a;通过设置全局变量/环境变量 方法一&#xff1a;设置全局变量 1.先请求登录接口&#xff0c;请求成功之后…

力扣Lc20--- 202.快乐数(java版)-2024年3月20日

1.题目 2.知识点 &#xff08;1&#xff09;while (seen.contains(n) false) { // 循环体 } 与 !seen.contains(n) 等同 &#xff08;2&#xff09; 当传入数字 19 给 isHappy(19) 方法时&#xff0c;下面是每一行代码的执行过程&#xff1a; 初始化一个空的 HashSet&#…

32.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-网络数据分析原理与依据

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;31.其它消息的实…

el-table的border属性失效问题解决方案

目录 问题&#xff1a; 使用的代码&#xff1a; 官方文档的说明&#xff1a; 可能的问题所在&#xff1a; 关于使用了作用域插槽&#xff1a; a.自定义内容的样式覆盖&#xff1a; b.表格结构的改变&#xff1a; 解决方案&#xff1a; 通过css样式解决&#xff1a; 下面…

打流仪/网络测试仪这个市场还能怎么卷?

#喝了点&#xff0c;码点字# 以下为个人观点&#xff0c;看看就好&#xff0c;如有冒犯&#xff0c;私信删稿 都有哪些厂商在做打流仪/网络测试仪 -洋品牌&#xff1a;思博伦/Viavi-Spirent&#xff0c;是德/Keysight-Ixia&#xff0c;信雅纳/Lecroy-Xena&#xff0c; -国产…

Pytest配置文件pytest.ini的具体使用

前言 说到配置&#xff0c;大家可能想到的是不经常更改的内容&#xff0c;比如Django里的settings.py文件&#xff0c;或者我们做自动化的时候&#xff0c;把测试环境的域名和正式环境的域名放到一个配置文件里&#xff0c;所有的接口都从这个文件里读取。这样&#xff0c;如果…

python中获取当前项目的目录

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天介绍一下&#xff0c;如何在python中获取当前项目所在的目录&#xff0c;而不是运行脚本的目录。 class ProjectPaths:# 初始化时获取当前脚本的路径staticmethoddef get_script_dir():…

MySQL进阶-----存储引擎

目录 前言 一、MySQL体系结构 二、存储引擎介绍 三、存储引擎特点 1.InnoDB 2.MyISAM 3.Memory 4.区别及特点 四、存储引擎选择 前言 从本期开始&#xff0c;我们就正式进入到MySQL进阶篇的学习了&#xff0c;前面的基础篇就告一段落了。进阶篇的第一期我们就从MySQL的…

opengl 学习(六)-----坐标系统与摄像机

坐标系统与摄像机 分类引言坐标系统摄像机教程在CMake中使用全局定义预编译宏,来控制是否开启错误检查补充 分类 opengl c 引言 OpenGL希望在每次顶点着色器运行后&#xff0c;我们可见的所有顶点都为标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说&#xff…

Python使用指定端口进行http请求的例子

使用requests库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class SourcePortAdapter(HTTPAdapter): """"Transport adapter" that allows us to set the source port.""" def __init__(self, port, *args, **kwargs): self.poolm…

JetPack之LiveData粘性原因分析及hook解决

目录 前言一、LiveData粘性原因分析1.1 发送消息流程1.2 监听消息流程1.3 根因分析 二、hook解决 前言 在 Android 中&#xff0c;LiveData 的默认行为是粘性的&#xff0c;即 LiveData 在设置数据后&#xff0c;即使观察者订阅时已经有数据存在&#xff0c;观察者仍会立即收到…

【链表】Leetcode 19. 删除链表的倒数第 N 个结点【中等】

删除链表的倒数第 N 个结点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 解题思路 1、使用快慢指针找到要删除节点的前一个节点。2、删…

使用Jmeter进行http接口测试的实践

前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设计之初是用于做性能测试的&#xff0c;它在实现对各种接口的调用方面已经做的比较成熟&#xff0c;因此&#xff0c;本次直接使用Jmeter工具来完成对Http接口的测试。 一、开发接口…

深入理解二叉树构建和中序遍历

在计算机科学中&#xff0c;二叉树是一种重要的数据结构&#xff0c;用于模拟层次化结构的关系。本文将介绍一个简单的C语言程序&#xff0c;该程序实现了对输入字符数组的解析&#xff0c;并构建相应的二叉树&#xff0c;随后对二叉树进行中序遍历。 二叉树定义 首先&#x…