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的更多相关文章

  1. 使用angular.js开发的一个简易todo demo

    前沿 在CVTE实习考察的一周里,接触到了angular,并在最后的一天任务里要求使用angular做一个功能主要包括创建.编辑.恢复.删除以及留言的todo demo,并支持响应式布局.因为之前没怎 ...

  2. JS框架整理

    1. Dojo (演示地址) Dojo是一个强大的面向对象JavaScript框架.主要由三大模块组成:Core.Dijit.DojoX.Core提供ajax,events,packaging,CSS ...

  3. Meteor入门

    转载Meteor入门介绍   Meteor是什么 基于nodejs的实时web APP开发框架. Meteor能带来什么 简单的说,你可以用js搞定客户端.服务端的开发.另外,客户端.服务端的界限被极 ...

  4. Meteor入门介绍

    Meteor是什么 基于nodejs的实时web APP开发框架. Meteor能带来什么 简单的说,你可以用js搞定客户端.服务端的开发.另外,客户端.服务端的界限被极大的模糊.客户端的界面跟服务端 ...

  5. 自动调整速率的Actor设计模式

    问题背景 与数据库或者存储系统交互是所有应用软件都必不可少的功能之一,akka开发的系统也不例外.但akka特殊的地方在于,会尽可能的将所有的功能都设计成异步的,以避免Actor阻塞,然而无法避免IO ...

  6. java并发之synchronized详解

    前言 多个线程访问同一个类的synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了类java的内置锁, 即锁住的是方法所属对象本身. 同 ...

  7. etcd 使用: golang 例子

    一:连接到 etcd package main import ( "fmt" "go.etcd.io/etcd/clientv3" "time&quo ...

  8. 通过一个demo了解Redux

    TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...

  9. 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo

    Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...

随机推荐

  1. CSS如何修改tr边框属性

    有很多时候,我们都要自定义为表格合并边框,这个只要 table{ border-collapse:collapse; } 就可以了 参数: separate 默认值.边框会被分开.不会忽略border ...

  2. java property 配置文件管理工具框架,避免写入 property 乱序

    property property 是 java 实现的 property 框架. 特点 优雅地进行属性文件的读取和更新 写入属性文件后属性不乱序 灵活定义编码信息 使用 OO 的方式操作 prope ...

  3. MySQL数据库的10大经典错误案例

    学习任何一门技术的同时,其实就是自我修炼的过程.沉下心,尝试去拥抱数据的世界! 案例一 Too many connections (连接数过多,导致连接不上数据库,业务无法正常进行) 问题还原: 解决 ...

  4. 微擎JS资源请求 403

    微擎JS资源请求 403 1.确认JS是否指定 type ==> text/javascript 2.确认src的路径是否正确,{MODULE_URL}项目的根目录带反斜杠 3.实例:(PS:t ...

  5. nyoj 108-士兵杀敌(一)(数学)

    108-士兵杀敌(一) 内存限制:64MB 时间限制:1000ms 特判: No 通过数:60 提交数:221 难度:3 题目描述: 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. ...

  6. openresty(nginx)中使用lua脚本获取请求IP地址的代码

    人狠话不多,直接上代码:------------------------------------------------------------------------------------- lo ...

  7. FPGA基础(verilog语言)——语法篇

    verilog语言简介 verilog语言是一种语法类似于c的语言,但是与c语言也有不同之处,比如: 1.verilog语言是并行的,每个always块都是同时执行,而c语言是顺序执行的 2.veri ...

  8. spring security进阶 使用数据库中的账户和密码认证

    目录 spring security 使用数据库中的账户和密码认证 一.原理分析 二.代码实现 1.新建一个javaWeb工程 2.用户认证的实现 3.测试 三.总结 spring security ...

  9. 阿里云ECS搭建kubernetes1.11

    环境信息 说明 1.使用kubeadm安装集群 虚拟机信息 hostname memory cpu disk role node1.com 4G 2C vda20G vdb20G master nod ...

  10. toString() 方法的参数

    除开null 和 undefined之外所有的数据类型都是拥有toString方法的. 通常情况下我们使用toString()方法的时候都是不用传递参数的,但是Number类型的toString方法是 ...