DOM实战-js todo

1.需求:

实现一个如下页面:

  • 最上面是输入框,后面是add按钮,输入文本点击add按钮,在下面就会出现一行,下面出现的每行最前面是两个按钮,然后后面是todo(要做的事)
  • 第一个按钮是完成按钮,第二个按钮是删除按钮,点击完成按钮后这一行虽然不会消失,但是这一行会有一条横线在上面表示完成,点击删除按钮后这一行的数据就会消失不见
  • 第三个按钮是修改,点击修改即可修改todo中的内容,修改完成后回车或按界面其他地方即可保存
  • 两个按钮后面的第一个是todo,第二个是发布时间

 

 

2.实现代码:

HTML:

 1 <!-- author: wyb -->
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>js todo</title>
 7     <style>
 8         *{
 9             margin: 0;
10             padding: 0;
11         }
12         .container{
13             width: 60%;
14             margin: 0 auto;
15         }
16         .button{
17             margin-right: 5px;
18         }
19         .complete{
20             color: red;
21             text-decoration: line-through;
22         }
23         .pub-time{
24             margin-left: 15px;
25         }
26     </style>
27 </head>
28 <body>
29 
30 <div class="container">
31     <!-- todo输入框 -->
32     <div class="todo-form">
33         <input type="text" id="input">
34         <button id="button-add">add</button>
35     </div>
36     <!-- todo列表 -->
37     <div id="todo-list">
38         <!-- 示例todo -->
39         <div class="todo-cell">
40         </div>
41     </div>
42 </div>
43 
44 <script src="todo.js"></script>
45 </body>
46 </html>

 

 

JavaScript:

  1 // 封装输出
  2 var log = function() {
  3     console.log.apply(console, arguments)
  4 };
  5 
  6 // 字符串处理:
  7 var todoTemplate = function (todo) {
  8     // 下面是JavaScript中的字符串替换:
  9     var t = `<div class="todo-cell"><button class="button-complete button">完成</button><button class="button-delete button">删除</button><button class="button-update button">编辑</button><span contenteditable='false' class="todo-label">${todo.task}</span><span class="pub-time">发布时间: ${todo.time}</span>`;
 10     return t;
 11 };
 12 
 13 // 插入新元素
 14 var insertTodo = function (todo) {
 15     // 获得todo-cell的HTML字符串:
 16     var todoItem = todoTemplate(todo);
 17 
 18     var todoList = document.querySelector("#todo-list");
 19     todoList.insertAdjacentHTML('beforeend', todoItem);
 20 };
 21 
 22 // 开关一个元素的某个class
 23 var toggleClass = function(element, className) {
 24     if (element.classList.contains(className)) {
 25         element.classList.remove(className)
 26     } else {
 27         element.classList.add(className)
 28     }
 29 };
 30 
 31 // 得到当前时间
 32 var currentTime = function () {
 33     var d = new Date();
 34     var year = d.getFullYear();
 35     var month = d.getMonth() + 1;
 36     var day = d.getDate();
 37     var hour = d.getHours();
 38     var minute = d.getMinutes();
 39     var second = d.getSeconds();
 40     // 时间格式处理
 41     if(minute <= 9){
 42         minute = "0" +minute
 43     }
 44     if(second <= 9){
 45         second = "0" +second
 46     }
 47 
 48     var timeString = `${year}/${month}/${day} ${hour}:${minute}:${second}`;
 49     log("now time is: ", timeString);
 50     return timeString
 51 };
 52 
 53 // 返回自己在父元素中的下标
 54 var indexOfElement = function(element) {
 55     var parent = element.parentElement;
 56     for (var i = 0; i < parent.children.length; i++) {
 57         var e = parent.children[i];
 58         if (e === element) {
 59             return i
 60         }
 61     }
 62 };
 63 
 64 // 保存 todoList
 65 var saveTodos = function() {
 66     var s = JSON.stringify(todoArray);
 67     localStorage.todoArray = s;
 68 };
 69 
 70 var loadTodos = function() {
 71     var s = localStorage.todoArray;
 72     return JSON.parse(s);
 73 };
 74 
 75 
 76 // 事件处理相关:
 77 // 响应事件函数:
 78 var bindEventAdd = function () {
 79     var buttonAdd = document.getElementById("button-add");
 80     buttonAdd.addEventListener('click', function () {
 81         log("button-add click");
 82 
 83         // 获得todo的值:
 84         var task = document.getElementById("input").value;
 85         // log(task);
 86         // 获得todo对象
 87         var todo = {
 88             'task': task,
 89             'time': currentTime(),
 90         };
 91         // 将数据存入数组中
 92         todoArray = loadTodos();
 93         todoArray.push(todo);
 94         saveTodos();
 95 
 96         // 插入todo-list:
 97         insertTodo(todo)
 98     });
 99 };
100 
101 var bindEventEnter = function(){
102     var todoList = document.querySelector("#todo-list");
103     todoList.addEventListener('keydown', function (event) {
104         log('todo keydown: ', event, event.target);
105         var target = event.target;
106         if(event.key === 'Enter') {
107             log('按了回车');
108             // 失去焦点
109             target.blur();
110             // 阻止默认行为的发生, 也就是不插入回车
111             event.preventDefault();
112             // 更新todo
113             var index = indexOfElement(target.parentElement);
114             log('update index: ',  index);
115             // 把元素在 todoList 中更新
116             todoArray = loadTodos();
117             todoArray[index-1].task = target.innerText;
118             saveTodos();
119         }
120     });
121 };
122 
123 var bindEventButton = function () {
124     // bindEventButton -> 复制todo所在div中的3个按钮的响应
125     var todoList = document.querySelector("#todo-list");
126     todoList.addEventListener('click', function (event) {
127         log('click: ', event, event.target);
128         // 获得点击对象和其父元素(todo的div)
129         var target = event.target;
130         var todoDiv = target.parentElement;
131 
132         // complete和delete和update的具体操作:
133         if(target.classList.contains('button-complete')) {
134             // 给 todo的div 开关一个状态 class
135             toggleClass(todoDiv, 'complete')
136         } else if (target.classList.contains('button-delete')) {
137             log('delete');
138             var index = indexOfElement(todoDiv) - 1;
139             log(index);
140             // 删除父节点
141             todoDiv.remove();
142             // 把元素从 todoArray 删除:
143             // delete todoArray[index] -> 不是完全删除,删除的数据变成了undefined依然留着数组中
144             todoArray = loadTodos();
145             log("delete: ", todoArray[index]);
146             todoArray.splice(index, 1);
147             log(todoArray);
148             saveTodos();
149         }
150         else if (target.classList.contains('button-update')) {
151             log('update');
152             var cell = target.parentElement;
153             var span = cell.children[3];
154             log("span is: ", span);
155             span.setAttribute("contenteditable", true);
156             // span.contentEditable = true  // 同理
157             span.focus();
158         }
159     });
160 };
161 
162 var bindEventBlur = function() {
163     var todoList = document.querySelector('#todo-list');
164     todoList.addEventListener('blur', function(event){
165         log('todo blur: ', event, event.target);
166         var target = event.target;
167         if (target.classList.contains('todo-label')) {
168             log('update and save');
169             // 让 span 不可编辑
170             target.setAttribute('contenteditable', 'false');
171             // 更新todo
172             var index = indexOfElement(target.parentElement);
173             log('update index: ',  index);
174             // 把元素在 todoList 中更新
175             todoArray = loadTodos();
176             todoArray[index-1].task = target.innerText;
177             saveTodos()
178         }
179     }, true)
180 };
181 
182 
183 // 绑定事件:
184 var bindEvents = function () {
185     // 添加todo
186     bindEventAdd();
187     // 文本框输入todo 按回车保存
188     bindEventEnter();
189     // 完成按钮和删除按钮和编辑按钮
190     bindEventButton();
191     // 文本框失去焦点后保存todo
192     bindEventBlur()
193 };
194 
195 
196 // 初始化todo:
197 var initTodos = function () {
198     var todoArray = loadTodos();
199     for (var i = 0; i < todoArray.length; i++) {
200         var todo = todoArray[i];
201         insertTodo(todo);
202     }
203 };
204 
205 // 存储数据
206 var todoArray = [];
207 // 程序主入口
208 var __main = function (){
209     // 绑定事件:
210     bindEvents();
211 
212     // 程序加载后, 加载 todoArray 并且添加到页面中
213     initTodos();
214 
215 };
216 
217 __main();
218 
219 
220 
221 // 一些说明:
222 // 事件委托相关概念
223 // ===
224 //
225 // 问题在于, todo都是运行的时候才添加的元素
226 // 对于这样的元素, 我们没办法实现绑定事件
227 // 我们可以把 click 事件绑定在事先存在的父元素上
228 // 通过父元素响应click事件 调用相应的事件响应函数
229 // 而事件响应函数会被传入一个参数, 就是事件本身
230 // 然后在运行的时候通过 event.target 属性(发起事件的元素,例如某个按钮)
231 // 来检查被点击的对象是否是需要的对象, 这个概念就是事件委托
232 
233 // 与存储相关的问题:
234 // ===
235 // localStorage 可以用来存储字符串数据, 在浏览器关闭后依然存在
236 // 存储方法如下:
237 // localStorage.name = 'wyb';
238 // 关闭浏览器, 注释上一句代码
239 // 再次用同一个浏览器打开该项目, 仍然能获取到这个值
240 // log('关闭浏览器后: ', localStorage.name);
241 // localStorage删除数据:
242 // localStorage.removeItem("name");
243 // 注意:
244 // 利用 localStorage 就可以 存储todo
245 // 但是 todo存在于array中
246 // 而 localStorage 只能存储 string 数据
247 // 所以没办法直接存储todo数据
248 //
249 // 可行的办法如下:
250 // 存储的时候把 array 转换为字符串   读取的时候把字符串转成 array
251 // 这个过程通常被称之为 序列化 和 反序列化
252 // 在 js 中, 序列化使用 JSON 格式
253 //
254 // var s = JSON.stringify([1, 2, 3, 4]);
255 // log('序列化后的字符串', typeof s, s);
256 // var a = JSON.parse(s);
257 // log('反序列化后的数组', typeof a, a);
258 // 输出结果:
259 // 序列化后的字符串 string [1,2,3,4]
260 // 反序列化后的数组 object Array(4)
261 //
262 // 使用 JSON 序列化后, 就可以把 todo存入浏览器的 localStorage 了
263 //
264 // 与时间相关的问题: JavaScript中的时间对象 -> Date对象
265 // ===
266 // 常用用法如下:
267 /*
268 var d = new Date()
269 d.getFullYear()
270 年份, 2016
271 d.getMonth()
272 月份, 0-11
273 d.getDate()
274 日期, 1-31
275 d.getHours()
276 小时, 0-23
277 d.getMinutes()
278 分钟, 0-59
279 d.getSeconds()
280 秒数, 0-59
281 d.getMilliseconds()
282 毫秒, 0-999
283 d.getDay()
284 星期几, 0-6
285 */

 

 

3.实现效果:

(1)最开始界面

 

 

(2)输入信息点击add

 

(3)点击完成

 

(4)点击删除

 

 

(5)点击编辑 

修改完成后回车后或点击其他页面即可

 

(6)刷新或关闭网页再次打开依然是之前保存的todo

 

posted @ 2018-07-25 12:55  woz333333  阅读(838)  评论(0编辑  收藏  举报