
前言:小生不才,只懂得一些皮毛,我努力以最简单的语言将心中的想法表述出来,让更多人能够很轻松的弄明白。文章里面有不足之处望各位大牛指出,使得后面的文章能够朝着更好的方向发展。另外,大家记得点赞哟!
欢迎关注微信公众号:前端切图仔
参考文章:
天之蓝源:三分钟在GitHub上搭建个人博客zhuanlan.zhihu.com











实现的效果:

一.todo list应用是什么?
可能很多小伙伴刚学完js或者其他知识的时候,不知道该怎么练习和巩固知识,然后这时候就可能会有人提出这样一个建议:写一个简单的todo list应用吧!
todo list应用其实很简单,就是一个待办事项列表应用,比如我们要做十件事,把这十件事添加到一个列表里面,然后办完一件事,我们就从列表中删除一件事,这就是一个最简单的todo list应用。接下来我们就要来实现这样一个最简单todo list应用,想要更完善的todo list应用可以自行百度学习更多知识。在本篇文章中我们利用localstorage将数据保存到本地,这样刷新浏览器数据就不会消失了。
二.什么是localstorage?
localStorage是HTML5新增的一个特性,主要是用来作为本地存储所使用的,我们都知道在之前,程序员们经常把数据存在cookie中,然后cookie的存储空间大约只有4kb,而新增的localStorage的存储容量大约有5M左右,这就解决了cookie存储容量不够的问题。但是,localStorage还是有它的优势和劣势的:
优势:
- localStorage拓展了cookie的4K限制
- localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的
劣势:
- 浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
- 目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
- localStorage在浏览器的隐私模式下面是不可读取的
- localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
- localStorage不能被爬虫抓取到
另外:说起了localStorage,就不得不说一下sessionStorage,sessionStorage(会话存储)和localStorage(本地存储)的区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的值就会被清空。
用法:
不管是 localStorage,还是 sessionStorage,可使用的API都相同,常用的有如下几个(以localStorage为例):
- 保存数据:localStorage.setItem(key,value);
- 读取数据:localStorage.getItem(key);
- 删除单个数据:localStorage.removeItem(key);
- 删除所有数据:localStorage.clear();
- 得到某个索引的key:localStorage.key(index);
提示: 键/值对通常以字符串存储,你可以按自己的需要转换该格式。
三.什么是事件委托?
事件委托是前端面试中的一道经典题目,事件委托还有另外一个名字,事件代理。在红皮书《JavaScript高级程序设计》里面是这样解释事件委托的:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。官方的语言始终是比较拗口,我们通俗一点来解释:比如我们有10个DOM节点,我们需要给每一个DOM都添加一个相同事件,那么我们就需要执行for循环给每个DOM元素添加事件,那要是有100个DOM元素呢,这样一个一个的赋予事件就是很繁琐的,而且这种繁琐的操作,直接导致了页面优化的问题。因此我们就想到了另外一个方法,那就是利用JS里面的冒泡原理,至于冒泡顾名思义就是一层一层的往上冒或者一层一层的往下冒,这事件也是一样,一层一层的往上冒,一层一层的往下冒,就这样,我们只需要给一个DOM节点赋予了事件,那么所有的DOM节点都有了这个事件。
这里就简单说了一下大致原理,具体内容可以百度:事件委托面试题等等,有很多不错的解释。
四.实现思路及步骤
(一)页面结构------>HTML代码
开局一条狗,后面全靠编。我们先把页面结构搭好,这才是第一步。
<body>
<div class="wrapper">
<h2>我是前端切图仔</h2>
<p></p>
<ul class="plates">
<li>待添加事项</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Item Name" required>
<input type="submit" value="添 加">
</form>
<div class="buttons">
<button data-action="clear">删除所有</button>
<button data-action="check">全部选中</button>
<button data-action="uncheck">取消全选</button>
</div>
</div>
</body>
关于data-*属性,可以参照W3C,上面解释得挺清楚。

(二)页面样式------>CSS代码
没有样式的HTML代码就是没有灵魂的,那么我们来给他添加一点灵魂吧:
html {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper {
padding: 20px;
max-width: 350px;
background: rgba(228, 215, 215, 0.95);
box-shadow: 0 0 0 5px rgba(187, 157, 157, 0.5);
}
h2 {
text-align: center;
margin: 0;
font-weight: 200;
}
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none;
}
.plates li {
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 10px 0;
font-weight: 100;
display: flex;
}
.plates label {
flex:1;
cursor: pointer;
}
.plates input {
display: none;
}
.plates input + label:before {
content: '⬜️';
margin-right: 10px;
}
.plates input:checked + label:before {
content: '☆';
}
.add-items {
margin-top: 20px;
}
.add-items input {
padding:10px;
outline:0;
border:1px solid rgba(0,0,0,0.1);
}
.add-items input:nth-child(1){
width: 61.3%;
}
.add-items input:nth-child(2) {
width: 30%;
color: rgb(85, 108, 128);
font-weight: 700;
}
.buttons button {
width: 100px;
margin-top: 10px;
height: 40px;
color: rgb(85, 108, 128);
font-weight: 700;
border:1px solid rgba(0,0,0,0.1);
}

(三)页面逻辑------>JS代码
下面是最重要的js代码了,js代码负责我们的逻辑部分,是整个demo的核心,只要大家跟着注释解析代码很容易理解。
(function(){
function newFun() {
var addItems = document.querySelector('.add-items');//选中类为.add-items的元素
var itemsList = document.querySelector('.plates');//todolist列表
var buttons = document.querySelector('.buttons');
var items = JSON.parse(localStorage.getItem('items')) || [];//获取本地缓存到的所有item,将一个对象字符串转换为对象
//添加item方法
function handleSubmit(e) {
e.preventDefault();//阻止默认事件的触发,防止在提交后页面自己刷新
var name = this.querySelector('[name=item]').value;//获取输入框中的值
var item = {
name: name,
done: false//增加一个状态bool,后面会用到
};
items.push(item);
localStorage.setItem('items', JSON.stringify(items));//将对象转化为字符串,因为本地存储只能以字符串的形式存储
updateList( items, itemsList);//更新列表
this.reset();
}
function updateList(plates = [], plateList) {
plateList.innerHTML = plates.map( function(plate, i) {
return '<li><input type="checkbox" data-index="' + i + '" id="plate' + i + '" ' + (plate.done ? 'checked' : '') + ' /><label for="plate' + i + '">' + plate.name + '</label></li>';}).join('');
}
//事件委托
// 此处使用到了事件委托:
// 假设我们队一个input列表进行了事件监听,但我们如法保证,此列表在接下来的状态下是否进行了更新,刷新等改变原来节点的操作,如果有这样的操作出现,那么我们之前的事件监听器就无法再起到监听的作用;
// 但我们可以对input列表的父元素进行事件监听,让它们的父元素处于监听状态,当我们所点击的元素是其子元素的话,就告诉它的子元素,执行相应的事件;
// 相当于委托父元素帮我们监听所有子元素,这样无论子元素列表进行怎么样的更新,改变,只要父元素节点不发生改变就可以持续起到监听的 作用。
// 通过e.target.matches('input')可以判断所点击的元素是不是input元素,e.target返回所点击的宿主元素。
// 通过获取到所点击的列表的序号,更改其done属性,更新后进行存储,就可以实现完成状态的事件。
function toggleChecked(e) {
if (!e.target.matches('input')) return;
var item = e.target.dataset.index;
items[item].done = !items[item].done;
localStorage.setItem('items', JSON.stringify(items));
updateList( items, itemsList);
}
//添加button事件
function doButtonPress(e) {
var action = e.target.dataset.action;
switch (action) {
case "clear":
items = [];
break;
case "check":
items.map( function(item) {
return item.done = true;
} );
break;
case "uncheck":
items.map( function(item) {
return item.done = false ;
} );
break;
default:
return;
}
localStorage.setItem('items', JSON.stringify(items));
updateList( items, itemsList);
}
addItems.addEventListener('submit', handleSubmit);
itemsList.addEventListener('click', toggleChecked);
buttons.addEventListener('click', doButtonPress);
updateList(items, itemsList);
}
document.addEventListener('DOMContentLoaded', newFun);
//当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。另一个不同的事件 load 应该仅用于检测一个完全加载的页面。 在使用 DOMContentLoaded 更加合适的情况下使用 load 是一个令人难以置信的流行的错误,所以要谨慎。
//注意:DOMContentLoaded 事件必须等待其所属script之前的样式表加载解析完成才会触发。
}());
(四)所有代码
五.总结
本次这个demo的核心就是localstorage(本地存储)的使用以及事件委托的理解,笔者也是在边写代码边理解,参照网上一些大牛的代码,遇到不懂的就上网百度,慢慢来实现。我们可以回顾一下我们是怎么做的:
- 首先,一进入页面就添加事件DOMContentLoaded ,执行函数newFun
- 然后就是获取dom元素和本地存储的元素
- 然后有三个方法,分别是添加item元素,监听input列表方法,和点击button所执行的方法
- 执行相应的函数
总的来说,虽然流程是比较清楚的,但是代码的细节还是需要仔细斟酌。
六.补充
在浏览器退出的时候可以清除一下缓存,因为这毕竟是我们的demo,没那么正式:
window.onbeforeunload = function (e) {
localStorage.removeItem('items');
e.returnValue = confirmationMessage; // Gecko, Trident, Chrome 34+
};
window.onbeforeunload = function (event) {
var message = 'Important: Please click on 'Save' button to leave this page.';
if (typeof event == 'undefined') {
event = window.event;
}
if (event) {
event.returnValue = message;
}
return message;
};
七.源代码
更多源代码请移步GitHub
Hacker233/JavaScriptgithub.com