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. js 把数组中每个元素的某个字段取出

    方法一:map() let cities = [ {city:"北京",bOn:false}, {city:"上海",bOn:false}, {city:&qu ...

  2. logback日志回顾整理--2018年8月8日

    几年前使用过logback作为项目的日志框架. 当时觉得这个框架比log4j更加好用. 所以系统的学习了一遍. 后来换了公司, 不再使用logback. 如今, 又有机会使用logback了, 所以, ...

  3. hdu 1754 I Hate It (线段树、单点更新)(PS:ios::sync_with_stdio(false)可以加快cin、cout的读取写出速度)

    I Hate ItTime Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  4. Error: Cannot find module 'less'

    这是webpack.config 代码中引入 报错信息(在main.js中引入也是这个报错 解决办法: 安装less: npm install less --save-dev 转自: https:// ...

  5. tcp和udp的网络编程(发送消息及回复)

    一.UDP  无连接的  高效的  基于数据报的  不可靠 的连接 主要的应用场景: 需要资源少,网络情况稳定的内网,或者对于丢包不敏感的应用,比如 DHCP 就是基于 UDP 协议的.不需要一对一沟 ...

  6. MySQL8.0 新特性 Hash Join

    概述&背景 MySQL一直被人诟病没有实现HashJoin,最新发布的8.0.18已经带上了这个功能,令人欣喜.有时候在想,MySQL为什么一直不支持HashJoin呢?我想可能是因为MySQ ...

  7. Linux 7开机自启项查看并设置

      在Linux6中查看及设置开机自启信息是使用chkconfig命令,Linux7中此命令已经被替代,接下来我们就来研究下Linux7中的区别所在. chkconfig --list Note: T ...

  8. 网站优化之使用Free marker静态化网站文章页

    博客做出来的时候就想要把一些栏目和文章页都静态化处理,当时没啥时间搞,就一直没去弄.但是最近的工作就是做网站,用cms快速搭出了几个网站,cms搭建网站是真的方便啊 如果没有需要二次开发实现的功能,那 ...

  9. 移动端App uni-app + mui 开发记录

    前言 uni-app uni-app是DCloud推出的终极跨平台解决方案,是一个使用Vue.js开发所有前端应用的框架,官网:https://uniapp.dcloud.io/ mui 号称最接近原 ...

  10. OpenStack - keystone 问题 'NoneType' object has no attribute 'service_catalog'

    'NoneType' object has no attribute 'service_catalog' 报错 编辑:/etc/keystone/keystone-paste.ini 在[pipeli ...