todo demo
Todo
功能实现
https://rencoo.github.io/appDemo/todo/index.html
---1.添加事项
---2.置顶事项
---3.删除事项
---4.用时排序
---5.删除所有
1class Todo{
2 constructor(id) {
3 this.container = document.getElementById(id);
4 this.todoContainer = this.container.querySelector('.todo-container');
5 this.delContainer = this.container.querySelector('.delete-container');
6
7 this.todoList = []; // 用于存放todo
8 this.todoDelList = []; // 用于存放删除的todo
9
10 let container = this.container,
11 addBtn = container.querySelector('.addBtn'),
12 input = container.querySelector('.todo-ctrl input'),
13 delBtn = container.querySelector('.delBtn'),
14 sortBtn = container.querySelector('.sortBtn');
15
16 /* 待办板块 */
17 // 点击按钮添加
18 addBtn.addEventListener('click', () => this.createTodo());
19 // 回车添加
20 input.addEventListener('keydown', evt => {
21 if(evt.key == 'Enter') {
22 this.createTodo();
23 }
24 })
25 // 点击todo文本,编辑todo
26 let todoContainer = this.todoContainer;
27 todoContainer.addEventListener('click', evt => {
28 let target = evt.target;
29 if(target.classList.contains('todo-task')) {
30 target.setAttribute('contenteditable', 'true');
31 }
32 target.focus(); // 点击后使文本框立即获得焦点
33 })
34 // 回车按钮完成编辑,并失焦
35 todoContainer.addEventListener('keydown', evt => {
36 let target = evt.target;
37 if(evt.key == 'Enter') {
38 if(target.classList.contains('todo-task')) {
39 let todoCell = target.parentNode,
40 idx = this.indexOfElement(todoCell),
41 task = target.value, // 获取task
42 todo = this.todoList[idx];
43 todo.task = task; // 修改todo
44 this.saveTodos() // 保存
45 target.blur(); // 失焦
46 evt.preventDefault(); // 阻止回车默认换行行为
47 }
48 }
49 })
50 // 点击时间进行置顶
51 todoContainer.addEventListener('click', evt => {
52 let target = evt.target;
53 let todoList = this.todoList;
54 if(target.classList.contains('todo-time')) {
55 let todoCell = target.parentNode,
56 idx = this.indexOfElement(todoCell),
57 todo = todoList[idx];
58 todoList.splice(idx, 1); // 在待办数组中删除todo
59 todoList.unshift(todo); // 在待办数组中置顶todo
60 this.saveTodos(); // 保存
61 todoCell.remove(); // 在待办页面删除todoCell
62 todoContainer.prepend(todoCell) // 在删除页面置顶todoCell
63 }
64 })
65 // 点击复选框移除事件(事件委托)
66 todoContainer.addEventListener('click', evt => {
67 let target = evt.target;
68 if(target.classList.contains('todo-input')) { // 判断目标元素;jQuery 可以直接指定响应事件的元素
69 let todoCell = target.parentNode,
70 idx = this.indexOfElement(todoCell), // 获取todo的idx
71 todo = this.todoList[idx]; // 获取todo数据
72 this.todoList.splice(idx, 1) // 从待办数组中删除todo
73 this.saveTodos(); // 保存
74 todoCell.remove(); // 在待办中移除todoCell
75
76 let time = this.getInterval(todo); // 获取删除和创建的时间差
77 todo.time = time;
78 this.todoDelList.push(todo); // 往删除数组中添加todo
79 this.saveTodos(); // 保存
80 this.insertTodo(delContainer, todo); // 在删除中显示todoCell
81 }
82 })
83
84 /* 删除板块 */
85 // 点击复选框移除单条(删除板块)
86 let delContainer = this.delContainer;
87 delContainer.addEventListener('click', evt => {
88 let target = evt.target;
89 if(target.classList.contains('todo-input')) {
90 let todoCell = target.parentNode,
91 idx = this.indexOfElement(todoCell);
92 this.todoDelList.splice(idx, 1);
93 this.saveTodos();
94 todoCell.remove();
95 }
96 })
97 // 清空删除板块
98 delBtn.addEventListener('click', evt => {
99 var r = confirm("确定要删除所有吗");
100 if (r==true) {
101 this.todoDelList.length = 0 // 清空todoDelList
102 delContainer.innerHTML = ''; // 删除板块清空todoCell
103 this.saveTodos(); // 保存
104 }
105 else{
106 return;
107 }
108 })
109 // 事项用时排序
110 sortBtn.addEventListener('click', evt => {
111 let todoDelList = this.todoDelList;
112 todoDelList.sort(desc); // 对数组进行排序
113 this.saveTodos(); // 保存
114 delContainer.innerHTML = '';
115 let len = todoDelList.length;
116 for(let i = 0; i < len; i++) {
117 let todo = todoDelList[i];
118 this.insertTodo(delContainer, todo);
119 }
120 })
121 // sort rule,完成事项用时的排序规则
122 let desc = function(a, b) {
123 let m = this.time_to_minute(a.time),
124 n = this.time_to_minute(b.time);
125 return m > n ? -1 : 1;
126 }.bind(this);
127
128 // 初始化,载入数据和渲染页面
129 this.initTodos();
130 }
131 // 创建todo
132 createTodo() {
133 let task = this.getInputValue(); // 获取文本框中的值
134 if(!task) { // 文本框是否为空
135 alert("请输入!");
136 return;
137 }
138 let currentTime = this.getCurrentTime(); // 获取当前时间
139 let todo = { // 创建todo对象
140 'task': task,
141 'time': currentTime
142 }
143 this.todoList.push(todo); // 待办数组添加todo
144 this.saveTodos(); // 保存数据
145 this.insertTodo(this.todoContainer, todo); // 在页面上添加todoCell
146 let input = this.container.querySelector('.todo-ctrl input');
147 input.value = '';
148 }
149 // 获取输入框的值
150 getInputValue() {
151 let input = this.container.querySelector('.todo-ctrl input'),
152 value = input.value;
153 return value;
154 }
155 // 插入todo;
156 insertTodo(box, todo) { // 参数:容器;todo;
157 let t = this.templateTodo(todo);
158 box.insertAdjacentHTML('beforeEnd', t);
159 }
160 // 模板todo
161 templateTodo(todo) {
162 let t = `
163 <div class="todo-cell">
164 <input class="todo-input" type="checkbox"/>
165 <span class="todo-task" contenteditable ="false">${todo.task}</span>
166 <span class="todo-time">${todo.time}>>Top</span>
167 </div>
168 `
169 return t;
170 }
171 // 获取当前时间
172 getCurrentTime() {
173 var d= new Date(),
174 addZero = this.addZero,
175 month = addZero(d.getMonth() + 1),
176 date = addZero(d.getDate()), // 几号;区别于getDay星期几
177 hour = addZero(d.getHours()),
178 minute = addZero(d.getMinutes());
179 var t = `${month}/${date} ${hour}:${minute}`;
180 return t;
181 }
182 addZero(t) {
183 t = t < 10 ? '0' + t : t;
184 return t;
185 }
186 // 获取元素的序号
187 indexOfElement(el) {
188 let parent = el.parentElement,
189 len = parent.children.length;
190 for (let i = 0; i < len; i++) {
191 let e = parent.children[i];
192 if (e === el) {
193 return i;
194 }
195 }
196 }
197 // todo的时长
198 getInterval(todo) { // 时间差是分钟级别的
199 let createdTime = todo.time, // 创建时刻
200 delTime = this.getCurrentTime(), // 删除时刻
201 interval = this.time_to_minute(delTime) - this.time_to_minute(createdTime), // 时间差
202 time = this.minute_to_time(interval); // 将分钟转化为标准格式
203 return time;
204 }
205 // 时间积累量(相对于月初)
206 time_to_minute(time) {
207 let t = time.split('/'), // '08', '17 16:34'
208 s= t[1].split(' '), // '17', '16:34'
209 d = s[1].split(':'), // '16', '34'
210 // month = t[0], // 默认事件都在同一个月内发生
211 date = s[0],
212 hour = d[0],
213 minute = d[1],
214 m = Number(date * 1440) + Number(hour * 60) + Number(minute);
215 return m;
216 }
217 // 积累量转化为时间
218 minute_to_time(m) {
219 let addZero = this.addZero;
220 let date = addZero(Math.floor(m / 1440)),
221 hour = addZero(Math.floor(m / 60) % 24),
222 minute = addZero(m % 60),
223 time = `00/${date} ${hour}:${minute}`;
224 return time;
225 }
226 // localStorage 网页存储数据
227 saveTodos() {
228 let m = JSON.stringify(this.todoList),
229 n = JSON.stringify(this.todoDelList);
230 localStorage.todoList = m;
231 localStorage.todoDelList = n;
232 }
233 // 载入数据
234 loadTodos() {
235 let m = localStorage.todoList,
236 n = localStorage.todoDelList,
237 obj ={
238 todoList: JSON.parse(m),
239 todoDelList: JSON.parse(n)
240 };
241 return obj;
242 }
243 // 初始化todo
244 initTodos() {
245 let obj = this.loadTodos();
246 this.todoList = obj.todoList;
247 let len = this.todoList.length;
248 for (let i = 0; i < len; i++) {
249 let todo = this.todoList[i];
250 this.insertTodo(this.todoContainer, todo);
251 }
252
253 this.todoDelList = obj.todoDelList;
254 let length = this.todoDelList.length;
255 for(let j = 0; j < length; j++) {
256 let todoDel = this.todoDelList[j];
257 this.insertTodo(this.delContainer, todoDel);
258 }
259 }
260}
261
262// 实例化对象
263const todo = new Todo('todoDemo');
200多行的小demo收获
由于demo存在两个类似的板块,因此很多方法都需要抽象出来,使得两者都能使用;
由于有了数据存储这个操作,因此每次进行删除、插入、置顶、排序等操作时,就先必须把数据库中(数组)的数据信息更新并保存(类似于告诉后端你更新了什么数据),再在页面上显示相关改动(相当于展示在用户面前的前端重新局部渲染)
利用localStorage存储数据(localStorage中只能存储字符串,所以我们要借助于JSON.stringify()和JSON.parse();)
处理数据:利用对象保存解析localStorage的多条数据信息,而不能使用数组;然后要使用数据的时候,再读取对象的属性(这里是todoList与todoDelList);
对处理数据有了初步认知,处理好数据是编程中十分重要的内容,利用合理的数据结构存储数据,能方便数据的管理与使用
localStorage存储数据不安全,每条数据的key-value都被清晰的记录在浏览器的面板上;
根据数组中元素的特性,可以自定义排序规则,再根据规则利用sort对数组进行排序;
完成事项用时(ex 00/00 01:08)的排序,可以分别对每个位置进行比较,一旦出现大小结果就直接返回,这样排序开销会比较低;排序规则如下
1// ex
2// 先将标准时间的各项数据提取出保存在对象中(处理数据)
3var times = [
4 {
5 date: 17,
6 h: 18,
7 m: 04
8 },
9 {
10 date: 18,
11 h: 18,
12 m: 04
13 },
14 {
15 date: 19,
16 h: 18,
17 m: 04
18 },
19 {
20 date: 19,
21 h: 20,
22 m: 04
23 },
24]
25// 制定比较规则
26var desc = function(a, b) {
27 if (a.date !== b.date) {
28 return a.date > b.date ? -1 : 1;
29 } else {
30 if (a.h !== b.h) {
31 return a.h > b.h ? -1 : 1;
32 } else {
33 if (a.m !== b.m) {
34 return a.m > b.m ? -1 : 1;
35 }
36 }
37 }
38}
39// 对时间数组中的对象进行排序
40times.sort(desc)
41
42// 结果如下,符合预期
43[{ date: 19, h: 20, m: 4 },{ date: 19, h: 18, m: 4 },{ date: 18, h: 18, m: 4 },{ date: 17, h: 18, m: 4 }]
todo demo的更多相关文章
- 使用angular.js开发的一个简易todo demo
前沿 在CVTE实习考察的一周里,接触到了angular,并在最后的一天任务里要求使用angular做一个功能主要包括创建.编辑.恢复.删除以及留言的todo demo,并支持响应式布局.因为之前没怎 ...
- JS框架整理
1. Dojo (演示地址) Dojo是一个强大的面向对象JavaScript框架.主要由三大模块组成:Core.Dijit.DojoX.Core提供ajax,events,packaging,CSS ...
- Meteor入门
转载Meteor入门介绍 Meteor是什么 基于nodejs的实时web APP开发框架. Meteor能带来什么 简单的说,你可以用js搞定客户端.服务端的开发.另外,客户端.服务端的界限被极 ...
- Meteor入门介绍
Meteor是什么 基于nodejs的实时web APP开发框架. Meteor能带来什么 简单的说,你可以用js搞定客户端.服务端的开发.另外,客户端.服务端的界限被极大的模糊.客户端的界面跟服务端 ...
- 自动调整速率的Actor设计模式
问题背景 与数据库或者存储系统交互是所有应用软件都必不可少的功能之一,akka开发的系统也不例外.但akka特殊的地方在于,会尽可能的将所有的功能都设计成异步的,以避免Actor阻塞,然而无法避免IO ...
- java并发之synchronized详解
前言 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同 ...
- etcd 使用: golang 例子
一:连接到 etcd package main import ( "fmt" "go.etcd.io/etcd/clientv3" "time&quo ...
- 通过一个demo了解Redux
TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...
- 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo
Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...
随机推荐
- CSS如何修改tr边框属性
有很多时候,我们都要自定义为表格合并边框,这个只要 table{ border-collapse:collapse; } 就可以了 参数: separate 默认值.边框会被分开.不会忽略border ...
- java property 配置文件管理工具框架,避免写入 property 乱序
property property 是 java 实现的 property 框架. 特点 优雅地进行属性文件的读取和更新 写入属性文件后属性不乱序 灵活定义编码信息 使用 OO 的方式操作 prope ...
- MySQL数据库的10大经典错误案例
学习任何一门技术的同时,其实就是自我修炼的过程.沉下心,尝试去拥抱数据的世界! 案例一 Too many connections (连接数过多,导致连接不上数据库,业务无法正常进行) 问题还原: 解决 ...
- 微擎JS资源请求 403
微擎JS资源请求 403 1.确认JS是否指定 type ==> text/javascript 2.确认src的路径是否正确,{MODULE_URL}项目的根目录带反斜杠 3.实例:(PS:t ...
- nyoj 108-士兵杀敌(一)(数学)
108-士兵杀敌(一) 内存限制:64MB 时间限制:1000ms 特判: No 通过数:60 提交数:221 难度:3 题目描述: 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. ...
- openresty(nginx)中使用lua脚本获取请求IP地址的代码
人狠话不多,直接上代码:------------------------------------------------------------------------------------- lo ...
- FPGA基础(verilog语言)——语法篇
verilog语言简介 verilog语言是一种语法类似于c的语言,但是与c语言也有不同之处,比如: 1.verilog语言是并行的,每个always块都是同时执行,而c语言是顺序执行的 2.veri ...
- spring security进阶 使用数据库中的账户和密码认证
目录 spring security 使用数据库中的账户和密码认证 一.原理分析 二.代码实现 1.新建一个javaWeb工程 2.用户认证的实现 3.测试 三.总结 spring security ...
- 阿里云ECS搭建kubernetes1.11
环境信息 说明 1.使用kubeadm安装集群 虚拟机信息 hostname memory cpu disk role node1.com 4G 2C vda20G vdb20G master nod ...
- toString() 方法的参数
除开null 和 undefined之外所有的数据类型都是拥有toString方法的. 通常情况下我们使用toString()方法的时候都是不用传递参数的,但是Number类型的toString方法是 ...