Todos Storage
-- 待办应用(存储版本)
课程概要
本课程主要讲解本地存储,概括了 JavaScript 在 Web 页面中的存储操作的注意事项,包括 保存、获取数据,及 JSON 数据格式的转换。通过本课程,完成一个存储版 Todos 待办应用,大家将对本地数据存储的操作有更深入的了解。
主要包含以下功能:
- 页面打开时获取本地数据
- 页面关闭时保存本地数据
知识点
本课程涉及到的主要知识点有:
- JSON.stringify 转换为一个 JSON 字符串
let arr = [1, 2, 3, 4]
JSON.stringify(arr); // "[1, 2, 3, 4]"
- JSON.parse 解析 JSON 字符
let str = "[1, 2, 3, 4]";
JSON.parse(str); // [1, 2, 3, 4]
- localStorage.setItem 存储数据
localStorage.setItem("name", "Nicholas");
注意:localStorage 存储格式为字符串,其他格式的数据请 JSON.stringify 一下
- localStorage.getItem 读取数据
let name = localStorage.getItem("name");
注意:如果 localStorage 之前被 JSON.stringify 过,需要是用 JSON.parse 还原
实现步骤
- 页面关闭时获取 todos 数据,存储到本地
- 页面打开时获取本地数据,更新 todos
存储本地数据
- 绑定页面卸载事件 saveTodo
- 新增 saveTodo 事件
- 获取 PAGE.data.todos
- 对 PAGE.data.todos 转化为 JSON 字符串
- 将 todos 字符串存储在本地存储 localStorage 的 todos 中
const PAGE = {
...,
bind: function() {
...,
// 1. 绑定页面卸载事件 saveTodo
window.addEventListener('unload',this.saveTodos);
},
...,
// 2. 新增 saveTodo 事件
saveTodos: function() {
// 3. 获取 PAGE.data.todos
let todos = PAGE.data.todos;
// 4. 对 PAGE.data.todos 转化为 JSON 字符串
let todosStr = JSON.stringify(todos);
// 5. 将 todos 字符串存储在本地存储 localStorage 的 todos 中
localStorage.setItem('todos',todosStr);
}
}
PAGE.init();
获取本地数据
- 新增 getTodos 事件
- 获取本地存储 localStorage 中 todos 的值
- 对 todos 进行 JSON 转化,同时如果为空时候显示为 [] 空数组。
- 把 todos 设置到 PAGE.data.todos 中
- 渲染页面
- 在 init 方法中的 render 替换为 getTodos 方法,获取数据后再 render
const PAGE = {
...,
init: function() {
this.bind();
// 6. 在 init 方法中的 render 替换为 getTodos 方法,获取数据后再 render
this.getTodos();
},
...,
// 1. 新增 getTodos 事件
getTodos: function() {
// 2. 获取本地存储 localStorage 中 todos 的值
let todos = localStorage.getItem('todos');
// 3. 对 todos 进行 JSON 转化,同时如果为空时候显示为 [] 空数组。
todos = JSON.parse(todos) || [];
// 4. 把 todos 设置到 PAGE.data.todos 中
PAGE.data.todos = todos;
// 5. 渲染页面
this.render();
},
}
PAGE.init();
代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Todes</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
font: 24px Helvetica, Arial, sans-serif;
font-weight: 400;
box-sizing: border-box;
color: #666;
}
.todos-container{
margin: 100px auto;
width: 550px;
}
.todos-title{
font-size: 100px;
text-align: center;
font-weight: 100;
color: rgba(175, 47, 47, 0.15);
margin-bottom: 20px;
}
.todos-content{
position: relative;
background: #fff;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
}
.todos-input{
display: block;
width: 100%;
padding: 16px 16px 16px 60px;
border: none;
outline: none;
font-weight: 200;
}
.todo-item{
padding: 16px;
display: flex;
border-top: 1px solid #e4e4e4;
}
.todo-item-hd{
position: relative;
width: 28px;
height: 28px;
border: 1px solid #e4e4e4;
border-radius: 50%;
margin-right: 16px;
cursor: pointer;
}
.todo-item-bd{
flex: 1;
color: #333;
}
.todo-item-ft{
display: none;
cursor: pointer;
}
.todo-item:hover .todo-item-ft{
display: inline-block;
color: #999;
}
.todo-item.active .todo-item-hd:before{
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(175, 47, 47, 0.15);
}
.todo-item.active .todo-item-bd{
text-decoration: line-through;
color: #999;
}
/*筛选*/
.todos-filter{
border-top: 1px solid #e4e4e4;
padding: 8px 16px;
text-align: center;
}
.todos-filter .filter-item{
display: inline-block;
margin: 0 10px;
padding: 4px 8px;
font-size: 14px;
color: #999;
border: 1px solid #999;
border-radius: 4px;
cursor: pointer;
}
.todos-filter .filter-item.active{
border-color: #333;
color: #333;
}
</style>
</head>
<body>
<div class="todos-container">
<h1 class="todos-title">todos</h1>
<div class="todos-content">
<div class="todos-input-cell">
<input id="todo-input" class="todos-input" type="text" name="todo" placeholder="请输入计划事项">
</div>
<div id="todos-list" class="todos-list">
<div class="todo-item">
<div class="todo-item-hd"></div>
<div class="todo-item-bd">打一瓶酱油</div>
<div class="todo-item-ft">x</div>
</div>
<div class="todo-item active">
<div class="todo-item-hd"></div>
<div class="todo-item-bd">跑步800米</div>
<div class="todo-item-ft">x</div>
</div>
</div>
<div class="todos-filter" id="todos-filter">
<span class="filter-item active">全部</span>
<span class="filter-item">待办</span>
<span class="filter-item">已办</span>
</div>
</div>
</div>
<script type="text/javascript">
const PAGE = {
data: {
todos: [{
title: '打一瓶酱油',
completed: false
},{
title: '跑步800米',
completed: true
}],
filter: 1,
filters: {
1: '全部',
2: '待办',
3: '已办',
}
},
init: function() {
this.bind();
this.getTodos();
},
bind: function() {
let input = document.getElementById('todo-input');
let todoList = document.getElementById('todos-list');
let todoFilter = document.getElementById('todos-filter');
input.addEventListener('keyup',this.addTodo);
this.onEventLister(todoList,'click','todo-item-hd',this.toggleTodo);
this.onEventLister(todoList,'click','todo-item-ft',this.removeTodo);
this.onEventLister(todoFilter,'click','filter-item',this.filterTodo);
window.addEventListener('unload',this.saveTodos);
},
getTodos: function() {
let todos = localStorage.getItem('todos');
todos = JSON.parse(todos) || [];
PAGE.data.todos = todos;
this.render();
},
saveTodos: function() {
let todos = PAGE.data.todos;
let todosStr = JSON.stringify(todos);
localStorage.setItem('todos',todosStr);
},
onEventLister: function(parentNode,action,childClassName,callback) {
parentNode.addEventListener(action,function(e){
e.target.className.indexOf(childClassName) >= 0 && callback(e);
})
},
render: function() {
let todos = this.data.todos;
let filters = this.data.filters;
let filter = this.data.filter;
todos.forEach((data,index)=> data.index = index);
let showTodos;
switch (filter) {
case 2:
showTodos = todos.filter( data => !data.completed );
break;
case 3:
showTodos = todos.filter( data => data.completed );
break;
default:
showTodos = todos;
break
}
let todosElement = showTodos.map((data)=>{
return `
<div class="todo-item ${data.completed ? 'active' : ''}" data-index="${data.index}">
<div class="todo-item-hd"></div>
<div class="todo-item-bd">${data.title}</div>
<div class="todo-item-ft">x</div>
</div>
`
}).join('');
let filterElement = Object.keys(filters).map( key => {
return `<span class="filter-item ${filter == key ? 'active' : ''}" data-id="${key}">${filters[key]}</span>`
}).join('');
let todoList = document.getElementById('todos-list');
let todoFilter = document.getElementById('todos-filter');
todoList.innerHTML = todosElement;
todoFilter.innerHTML = filterElement;
},
addTodo: function(e){
let value = this.value.trim();
if (e.which !== 13 || !value) {
return;
}
let todos = PAGE.data.todos;
todos.push({
title: value,
completed: false
})
this.value = '';
PAGE.render();
},
toggleTodo: function(e) {
let todos = PAGE.data.todos;
let todoItem = e.target.parentNode;
let index = todoItem.dataset.index;
todos[index].completed = !todos[index].completed;
PAGE.render();
},
removeTodo: function(e) {
let todos = PAGE.data.todos;
let todoItem = e.target.parentNode;
let index = todoItem.dataset.index;
todos.splice(index,1);
PAGE.render();
},
filterTodo: function(e) {
let filterItem = e.target;
let filter = filterItem.dataset.id;
PAGE.data.filter = Number(filter);
PAGE.render();
}
}
PAGE.init();
</script>
</body>
</html>