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-inputtype="checkbox"/>
165            <span class="todo-taskcontenteditable ="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        date17,
6        h: 18,
7        m: 04
8    },
9    {
10        date18,
11        h: 18,
12        m: 04
13    },
14    {
15        date19,
16        h: 18,
17        m: 04
18    },
19    {
20        date19,
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[{ date19, h: 20, m: 4 },{ date19, h: 18, m: 4 },{ date18, h: 18, m: 4 },{ date17, h: 18, m: 4 }]​​
posted @ 2018-08-18 11:47  rencoo  阅读(531)  评论(0编辑  收藏  举报