多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] # 前情回顾 上一节课, 我们做了 jquery 备忘清单(1.0) 1. 输入内容在 input 框中回车, 或者点击蓝色按钮, 可以把内容插入备忘内容区 需要确定回车的 keyCode(13) 1. 最后插入的排在最上面 数组 unshift 1. 如果文字内容过长, 展示时要有省略号 字符串 slice 1. 页面刷新或者重启浏览器, 内容不丢失 localStorage ## 昨天 html, css, js 的完整代码 ```html <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <link rel="stylesheet" href="css/style.css" /> <link rel="stylesheet" type="text/css" href="css/iconfont.css" /> <script src="js/jquery.js"></script> <script src="js/index.js"></script> <title>备忘清单</title> </head> <body> <!-- 总容器开始 --> <div class="container"> <h1 class="myTitle">我的备忘清单</h1> <!-- 输入框和按钮开始 --> <div class="add-task"> <input type="text" placeholder="写下你的备忘吧..." name="content" /> <button>添加备忘</button> </div> <!-- 输入框和按钮结束 --> <!-- 清单列表开始 --> <div class="task-list"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> </div> <!-- 清单列表结束 --> </div> <!-- 总容器结束 --> </body> </html> ``` ```css * { margin: 0; padding: 0; outline: none; -webkit-transition: background 200ms; -moz-transition: background 200ms; -ms-transition: background 200ms; -o-transition: background 200ms; transition: background 200ms; } body { background: #00334b; color: #fff; } h1.myTitle { text-align: center; margin: 20px; } .container { margin: 0 auto; /* background: red; */ max-width: 600px; } .task-item { background: #fff; color: #333; margin-bottom: 3px; cursor: pointer; padding: 10px; border-radius: 3px; } .task-item:hover { background: #ddd; } .iconfont { float: right; margin-right: 10px; } input[type="text"] { background: #ddd; float: left; width: 84%; margin-right: 1%; padding: 10px; -moz-box-sizing: border-box; /*Firefox3.5+*/ -webkit-box-sizing: border-box; /*Safari3.2+*/ -o-box-sizing: border-box; /*Opera9.6*/ -ms-box-sizing: border-box; /*IE8*/ box-sizing: border-box; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } input[type="text"]:focus, input[type="text"]:hover { background: #eee; } .add-task button { width: 15%; background: rgb(3, 174, 255); } .add-task button:hover { background: rgb(77, 195, 251); } input[type="text"], .add-task button { border: 0; height: 100%; } .add-task { height: 37px; } .task-list { margin: 10px 0; } input, button { border-radius: 3px; } .task-content { margin-left: 5px; } .iconfont:hover { filter: drop-shadow(0 0 0 black); } ``` ```javascript $(function() { /* 声明变量 */ if (localStorage.getItem("task_list")) { aTaskList = JSON.parse(localStorage.getItem("task_list")); } else { aTaskList = []; } showTask(); /* 声明函数 */ function showTask() { var taskHTML = ""; for (var i = 0; i < aTaskList.length; i++) { taskHTML += '<div class="task-item">'; taskHTML += '<span><input type="checkbox"></span>' + "\n"; taskHTML += '<span class="task-content">' + aTaskList[i] + "</span>"; taskHTML += '<span><i class="iconfont icon-del"></i></span>'; taskHTML += '<span><i class="iconfont icon-edit"></i></span>'; taskHTML += "</div>"; } $(".task-list").html(taskHTML); } /* 逻辑代码 */ $("[name=content]").on("keypress", function(ev) { if (ev.keyCode === 13 && compressContent($(this).val())) { aTaskList.unshift(compressContent($(this).val())); localStorage.setItem("task_list", JSON.stringify(aTaskList)); $(this).val(""); showTask(); } }); $(".add-task button").on("click", function() { if (compressContent($("[name=content]").val())) { aTaskList.unshift(compressContent($("[name=content]").val())); localStorage.setItem("task_list", JSON.stringify(aTaskList)); $("[name=content]").val(""); showTask(); } }); // 文字过多, 就显示省略号 function compressContent(str) { if (str.length >= 30) { return str.slice(0, 30) + "···"; } else { return str; } } }); ``` # 备忘清单 2.0 1. 主要内容分三块, 未完成, 已完成, 逻辑删除(回收站) 1. 三者之间可以互相转化 # 整理样式 首先, 我们需要把 html 的内容区分成三块, 每一块给一个不同的 class ![1545050410028](https://box.kancloud.cn/4340f5e055a924a90671830cdb9fd746_688x334.png) 这是原来的, 现在我复制`class`为`task-list`的 html 代码 ```html <div class="task-list"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> </div> <div class="task-list"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> </div> <div class="task-list"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> </div> ``` 结果就变成了这样... ![1545050507188](https://box.kancloud.cn/409d572eaa5bcb08bc52bf9711fd9845_678x710.png) 然后, 我们为每一块分配一个`class`, 第一块是`task-list` 这个就不用变了... 分别是`task-list`,`task-done`,`task-deleted` ```html <div class="task-list"><!-- 代码省略 --></div> <div class="task-done"><!-- 代码省略 --></div> <div class="task-deleted"><!-- 代码省略 --></div> ``` 这个时候再刷新... ![1545050757786](https://box.kancloud.cn/1458501ab49067407440f59fb8a308fa_662x760.png) 样式错了... 再来调调样式... ```css .task-list, .task-done, .task-deleted { margin: 10px 0; } ``` ![1545050869825](https://box.kancloud.cn/8ab94d620dd5dc3fa4c542fa0b1d1cd0_734x766.png) 现在就恢复正常了, 不过背景应该区分开... ```css .task-done .task-item { background: #ccc; } .task-deleted .task-item { background: #a0a0a0; } ``` ![1545051427438](https://box.kancloud.cn/96c77f9da74ebc31729ce45ba7c944ef_666x782.png) 我们还需要给`已完成`加上删除线 ```css .task-done .task-content { text-decoration: line-through; } ``` ![1545051938418](https://box.kancloud.cn/37665b7627193a59d4f5e3444dda96a7_641x229.png) `已完成` 不应该都是打钩的吗? ```html <span><input checked type="checkbox" /></span> ``` ![1545052079345](https://box.kancloud.cn/0960bbc376f83188dbcc7fa8ea11dcfc_631x233.png) `已完成`不应该有编辑按钮吧, 点击 checkbox, 返回`未完成`, 点击垃圾桶, 扔到`回收站` ```html <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> ``` ![1545052204693](https://box.kancloud.cn/264498b3fd632516cf9e7430bc39e19c_622x228.png) 垃圾桶那一块, `编辑`应该改成`还原`吧, 点击`还原`, 哪来的回哪去, 点击`删除`,`彻底删除` 看来我们需要新的图标... ![1545053249266](https://box.kancloud.cn/273d9fda145c90d314d7b52c6a8ebafc_571x254.png) ```html <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> ``` ![1545053482379](https://box.kancloud.cn/f5b94fdd1f941336d828e0f87c92f7d2_615x237.png) 又要重新下载, 新增一个也需要全部再选择一遍? 岂不是很麻烦? 是需要重新下载, 但是不麻烦, 因为阿里图标支持新建项目, 来管理图标 ![1545053418314](https://box.kancloud.cn/08d728debaea5bd1f8a09f2b9912f7be_789x306.png) 当然, 你也可以, 在原有`iconfont.css`的基础上, 加上新的 css`iconfont.css` ![1545102393112](https://box.kancloud.cn/9f6f569f2af7b196680653ef36662e01_375x173.png) 前提是, 类名不要重复... ![1545102432104](https://box.kancloud.cn/a789502f5790244842eadfd3b041ee12_993x718.png) ![1545102447421](https://box.kancloud.cn/e14b5b6b777a3d76740e20e356cf5308_1338x726.png) 如果两个图标, 都叫`remove`, 但是内容不同怎么办呢? 改 class 就好... ![1545102499762](https://box.kancloud.cn/086da09f75574f782d60f75c6cdda392_1326x693.png) 回收站不应该有`checkbox`吧... 那把他们干掉... ```html <div class="task-item"> <!-- <span><input type="checkbox" /></span> --> <span class="task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> ``` ![1545053575793](https://box.kancloud.cn/a1b6fff43cfe487c29f7ed6bef243a55_633x468.png) 又对不齐了... ![img](https://box.kancloud.cn/277f0a542bd933a004a93cbc95162a2e_300x292.jpg) 试试 css 的`visibility: hidden;`? ```css .task-deleted input[type="checkbox"] { visibility: hidden; } ``` ![1545053869471](https://box.kancloud.cn/77c93696a41512a49421e976212fded2_627x463.png) 完美 ![img](https://box.kancloud.cn/49dfbb31317e2f224ff6316373e4f48d_440x440.jpg) 不过... 貌似`已完成`的 hover 背景, 不是很明显... ![1545053962095](https://box.kancloud.cn/44b59d10bd391cf9972c9fabd8a86908_631x229.png) ```css .task-done .task-item:hover { background: #fffafa; } ``` ![1545054220097](https://box.kancloud.cn/4bf1a2426df7b5c5c4340bc3500daea3_628x230.png) 这回好多了... 到目前为止, 样式就调理的差不多了, 不过眼尖的同学可能会发现, 文字和图标, 好像不在一条直线上... ![1545055557111](https://box.kancloud.cn/f70fcb85449549cf51cfbb586603ef96_628x51.png) 因为文字和图标的行高不一致... ![1545059123108](https://box.kancloud.cn/5c16b6a35f32f218b91c57dd93c0f706_273x76.png) ![1545059238916](https://box.kancloud.cn/7ad366432e13784b3d4712e4c9740eea_260x86.png) 设置一致的行高即可 ```css .iconfont { float: right; margin-right: 10px; line-height: 21px; } ``` ![1545058160555](https://box.kancloud.cn/a7ad19c1e84cb81bba7431ee809d3ef0_979x85.png) # 开始 js 搞定了样式, 我们就可以写 js 了. 首先, 我们要操作单个的`task-item`, 那我们就需要准确的定位... 所以我们需要一个索引`index` 同时, 我们还需要一些属性, 标记他的状态 `done`, `delete` 所以我们需要一个对象... 包含`index,done,delete.content` 同时我们之前又是遍历的数组, 所以我们需要`数组装对象` 首先, 我们需要`index`, 数组的`index`, 因为数组自带索引, 我们就不用新建变量来保存索引了, 直接用就好 `index`的作用, 是告诉程序, 我们现在操作的是数组中的哪个元素 然后, 我们每次添加内容, 就不再是单纯的保存内容了, 而是保存一个对象, 然后压入数组... 所以, 我们需要修改新增`task-item`的两个函数 ```javascript $("[name=content]").on("keypress", function(ev) { if (ev.keyCode === 13) { var oTaskItem = { content: $(this).val(), isDel: 0, isDone: 0 }; aTaskList.unshift(oTaskItem); localStorage.setItem("task_list", JSON.stringify(aTaskList)); $(this).val(""); showTask(); } }); $(".add-task button").on("click", function() { var oTaskItem = { content: compressContent($("[name=content]").val()), isDel: 0, isDone: 0 }; aTaskList.unshift(oTaskItem); localStorage.setItem("task_list", JSON.stringify(aTaskList)); $("[name=content]").val(""); showTask(); }); ``` 当然, 展示的相关函数, 也要变... ```javascript function showTask() { var taskHTML = ""; aTaskList.forEach(function(value, key) { taskHTML += '<div index="' + key + '" class="task-item">'; if (value.isDone) { taskHTML += '<span><input index="' + key + '" checked type="checkbox"></span>' + "\n"; } else { taskHTML += '<span><input index="' + key + '" type="checkbox"></span>' + "\n"; } taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-edit"></i></span>'; taskHTML += "</div>"; }); $(".task-list").html(taskHTML); } ``` 如果属性`isDone` 是 1(true)的话, 就让`checkbox`选中... 同理, 当 checkbox 被选中时, 我们也需要让对象的`isDone`变成 1, 所以我们需要监听 checkbox 先写一下监听, 打印`index` ```javascript // 监听checkbox $("input[type=checkbox]").click(function() { if (!$(this).attr("checked")) { console.log($(this).attr("index")); $(this).attr("checked", "checked"); } else { $(this).removeAttr("checked"); } }); ``` ![1545064018002](https://box.kancloud.cn/d614927ca5cd48883f95f28550bd1125_1920x811.png) 没有问题... 然后我们开始改对象 先写一个改对象的函数... ```javascript function changeObj(index, property, value) { var obj = aTaskList[index]; obj[property] = value; aTaskList[index] = obj; localStorage.setItem("task_list", aTaskList); } ``` 然后在 checkbox 改变时, 调用它 ```javascript $("input[type=checkbox]").on("click", function() { console.log("hello"); if (!$(this).attr("checked")) { changeObj($(this).attr("index"), "isDone", 1); $(this).attr("checked", "checked"); } else { changeObj($(this).attr("index"), "isDone", 0); $(this).removeAttr("checked"); } showTask(); }); ``` 然而, 发现, 只能点击一次... 问题在于, 重新写入 html 代码后, 原来的代码就没有了. 所以需要把监听, 写在动态生成 html()代码之后 ```javascript function showTask() { console.log(aTaskList); var taskHTML = ""; aTaskList.forEach(function(value, key) { taskHTML += '<div index="' + key + '" class="task-item">'; if (value.isDone) { taskHTML += '<span><input index="' + key + '" checked type="checkbox"></span>' + "\n"; } else { taskHTML += '<span><input index="' + key + '" type="checkbox"></span>' + "\n"; } taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-edit"></i></span>'; taskHTML += "</div>"; }); $(".task-list").html(taskHTML); // 监听checkbox $("input[type=checkbox]").on("click", function() { console.log("hello"); if (!$(this).attr("checked")) { changeObj($(this).attr("index"), "isDone", 1); $(this).attr("checked", "checked"); } else { changeObj($(this).attr("index"), "isDone", 0); $(this).removeAttr("checked"); } showTask(); }); } ``` checkbox 已经监听, 对象已经改变, 剩下的就是把不同类型的 task, 分到不同组里... ```javascript function showTask() { console.log(aTaskList); $(".task-list").html(""); $(".task-done").html(""); aTaskList.forEach(function(value, key) { if (!value.isDone && !value.isDel) { var taskHTML = ""; taskHTML += '<div index="' + key + '" class="task-item">'; taskHTML += '<span><input index="' + key + '" type="checkbox" /></span>' + "\n"; taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-edit"></i></span>'; taskHTML += "</div>"; $(".task-list").append(taskHTML); } if (value.isDone && !value.isDel) { var taskHTML = ""; taskHTML += '<div index="' + key + '" class="task-item">'; taskHTML += '<span><input index="' + key + '" checked type="checkbox" /></span>'; taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; taskHTML += "</div>"; $(".task-done").append(taskHTML); } }); // 监听checkbox $("input[type=checkbox]").on("click", function() { console.log("hello"); if (!$(this).attr("checked")) { changeObj($(this).attr("index"), "isDone", 1); $(this).attr("checked", "checked"); } else { changeObj($(this).attr("index"), "isDone", 0); $(this).removeAttr("checked"); } showTask(); }); } ``` ![1545066339514](https://box.kancloud.cn/1e3cd5033cc10ac1159fe47de41bb59b_704x742.png) 成功!!! 试了一下`取消完成`功能, 也是可以的 ![1545066480937](https://box.kancloud.cn/49a255cea7780acf1d67c2ef87f8b597_696x733.png) 现在`未完成`和`已完成`的互转已经实现了!!! 接下来是未完成和`逻辑删除`的互转 在`showTask()`函数下 ```javascript // 监听未完成的删除按钮 $(".task-item .icon-del").on("click", function() { changeObj($(this).attr("index"), "isDel", 1); showTask(); }); ``` 还要写删除的展示... ```javascript // 代码省略... $(".task-deleted").html(""); aTaskList.forEach(function(value, key) { // 代码省略... if (!value.isDone && value.isDel) { var taskHTML = ""; taskHTML += '<div index="' + key + '" class="task-item">'; taskHTML += '<span><input index="' + key + '" type="checkbox" /></span>'; taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-remove"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-undo"></i></span>'; taskHTML += "</div>"; $(".task-deleted").append(taskHTML); } }); // 代码省略... ``` 试一下效果 ![1545067207480](https://box.kancloud.cn/dfa5d98d818ebee886f402d9e0fcfbec_635x507.png) 没有问题... 然后, 同理, 是被删除 task 的还原 ```javascript // 监听已删除的还原按钮 $(".task-deleted .icon-undo").on("click", function() { changeObj($(this).attr("index"), "isDel", 0); showTask(); }); ``` 其他代码不用改, 试了一下, 也没有问题... ![1545067429144](https://box.kancloud.cn/7b0212e2203e1028e185a729fda06855_668x506.png) 现在就剩下, 已完成和删除的互转了, 加油!!! 首先, 我们需要给`已完成`并且`已删除`的 task 加上删除线 先把 js 注释掉, 免得影响我们的内容 ```html <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <link rel="stylesheet" href="css/style.css" /> <link rel="stylesheet" type="text/css" href="css/iconfont.css" /> <script src="js/jquery.js"></script> <!-- <script src="js/index.js"></script> --> <title>备忘清单</title> ``` 然后, 给元素增加一个 class`is-deleted`, 并且设置样式 ```html <span><input type="checkbox" /></span> <span class="task-content is-deleted">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> ``` ```css .task-done .task-content, .is-deleted { text-decoration: line-through; } ``` 我们看一下效果... ![1545067754678](https://box.kancloud.cn/292e365936b61f4d2e961ca6e7c3591a_728x822.png) 前三项是有删除线的 那么我们现在可以写 js 了... 先来`已完成`转`已删除` ```javascript // 监听已完成的删除按钮 $(".task-done .icon-del").on("click", function() { changeObj($(this).attr("index"), "isDel", 1); showTask(); }); ``` ```javascript function showTask() { // 代码省略... if (value.isDone && value.isDel) { var taskHTML = ""; taskHTML += '<div index="' + key + '" class="task-item">'; taskHTML += '<span><input index="' + key + '" type="checkbox" /></span>'; taskHTML += '<span class="task-content is-deleted">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-remove"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-undo"></i></span>'; taskHTML += "</div>"; $(".task-deleted").append(taskHTML); } // 代码省略... } ``` 我们把 js 的注释去掉, 然后看一下效果... 没有问题 ![1545068075421](https://box.kancloud.cn/3c41df409dcf4310fed26987cc619148_679x509.png) 试试还原? 也没有问题! ![1545068165360](https://box.kancloud.cn/e9f845ee629c2728ca73ec267212e5c5_695x513.png) 最后, 请大家思考一个问题 如下图 ![1545068218115](https://box.kancloud.cn/73c629817785639e18deeb6ca288d012_719x530.png) 能不能, 把删除内容区域的`已完成`和`未完成`进行分组呢? 比如`已完成`在上, `未完成`在下? 机智如你, 一定会有答案的!!! ![img](https://box.kancloud.cn/c4a8975b9cf77e04bd9e518db0ee4aa8_240x240.jpg) 最后, 完整代码 ```html <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <link rel="stylesheet" href="css/style.css" /> <link rel="stylesheet" type="text/css" href="css/iconfont.css" /> <link rel="stylesheet" type="text/css" href="css/iconfont_2.css" /> <script src="js/jquery.js"></script> <script src="js/index.js"></script> <title>备忘清单</title> </head> <body> <!-- 总容器开始 --> <div class="container"> <h1 class="myTitle">我的备忘清单</h1> <!-- 输入框和按钮开始 --> <div class="add-task"> <input type="text" placeholder="写下你的备忘吧..." name="content" /> <button>添加备忘</button> </div> <!-- 输入框和按钮结束 --> <!-- 清单列表开始 --> <div class="task-list"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> <span><i class="iconfont icon-edit"></i></span> </div> </div> <div class="task-done"> <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> <div class="task-item"> <span><input checked type="checkbox" /></span> <span class="task-content">item content 1</span> <span><i class="iconfont icon-del"></i></span> </div> </div> <div class="task-deleted"> <div class="task-item"> <span><input type="checkbox" /></span> <span class="is-deleted task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="is-deleted task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="is-deleted task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="is-deleted task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> <div class="task-item"> <span><input type="checkbox" /></span> <span class="is-deleted task-content">item content 1</span> <span><i class="iconfont icon-remove"></i></span> <span><i class="iconfont icon-undo"></i></span> </div> </div> <!-- 清单列表结束 --> </div> <!-- 总容器结束 --> </body> </html> ``` ```css * { margin: 0; padding: 0; outline: none; -webkit-transition: background 200ms; -moz-transition: background 200ms; -ms-transition: background 200ms; -o-transition: background 200ms; transition: background 200ms; } body { background: #00334b; color: #fff; } h1.myTitle { text-align: center; margin: 20px; } .container { margin: 0 auto; /* background: red; */ max-width: 600px; } .task-item { background: #fff; color: #333; margin-bottom: 3px; cursor: pointer; padding: 10px; border-radius: 3px; } .task-item:hover { background: #ddd; } .iconfont { float: right; margin-right: 10px; line-height: 21px; } input[type="text"] { background: #ddd; float: left; width: 84%; margin-right: 1%; padding: 10px; -moz-box-sizing: border-box; /*Firefox3.5+*/ -webkit-box-sizing: border-box; /*Safari3.2+*/ -o-box-sizing: border-box; /*Opera9.6*/ -ms-box-sizing: border-box; /*IE8*/ box-sizing: border-box; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } input[type="text"]:focus, input[type="text"]:hover { background: #eee; } .add-task button { width: 15%; background: rgb(3, 174, 255); } .add-task button:hover { background: rgb(77, 195, 251); } input[type="text"], .add-task button { border: 0; height: 100%; } .add-task { height: 37px; } .task-done .task-item { background: #ccc; } .task-deleted .task-item:hover { background: #ddd; } .task-done .task-item:hover { background: #fff; } .task-deleted .task-item { background: #a9a7a7; } .task-done, .task-deleted, .task-list { margin: 10px 0; } .task-done .task-content, .is-deleted { text-decoration: line-through; } .task-deleted input[type="checkbox"] { visibility: hidden; } input, button { border-radius: 3px; } .task-content { margin-left: 5px; } .iconfont:hover { filter: drop-shadow(0 0 0 black); } ``` ```javascript // 相当于$(document).ready(), html代码, 加载完之后执行, 不会出现找不到对象的情况 $(function() { // 变量初始化, 判断, 如果缓存里有数据, 取出到aTaskList, 注意缓存需要解析, 否则直接取是一个字符串 // 不管有没有数据, aTaskList都是数组, 我们下面操作的就是数组 if (localStorage.getItem("task-list")) { // 解析用的是JSON.parse var aTaskList = JSON.parse(localStorage.getItem("task-list")); } else { // 如果缓存中没有数据, 则直接空数组 var aTaskList = []; } // 展示html代码包裹以后的数据, 刷新内容区 // 进入页面的时候会调一次, 更新数组和缓存后, 也会调用 showTask(); // 监听输入框的回车事件, 获取输入框的内容, 更新数组, 更新缓存, 刷新内容区 $(".add-task input[name=content]").on("keypress", function(ev) { // 先判断, 回车, 并且输入框里有内容, 则进行逻辑操作(往下走) // 回车的keyCode的是13, 使用===来比较 if (ev.keyCode === 13 && $(this).val()) { // 声明一个对象, 用来存储输入框里的内容(content) // 还有其他的属性 // isDel, 用来标记是否逻辑删除, 1 for yes, 0 for no // isDone, 用来标记是否已完成, 1 for yes, 0 for no var oTaskItem = { content: compressContent($(this).val()), // 存储input框里的内容, compressContent函数, 压缩内容, 超过长度加··· isDel: 0, // 标记已删除 isDone: 0 // 标记已完成 }; updateData(oTaskItem); // 更新数组, 更新缓存, 刷新内容区 // 清空input输入区的内容 $(this).val(""); } }); $(".add-task button").on("click", function() { if ($(".add-task input[name=content]").val()) { // 声明一个对象, 用来存储输入框里的内容(content) // 还有其他的属性 // isDel, 用来标记是否逻辑删除, 1 for yes, 0 for no // isDone, 用来标记是否已完成, 1 for yes, 0 for no var oTaskItem = { content: compressContent( $(".add-task input[name=content]").val() ), // 存储input框里的内容, compressContent函数, 压缩内容, 超过长度加··· isDel: 0, // 标记已删除 isDone: 0 // 标记已完成 }; updateData(oTaskItem); // 更新数组, 更新缓存, 刷新内容区 // 清空input输入区的内容 $(".add-task input[name=content]").val(""); } }); // 更新内容区 // 数组和缓存变化后, 会调用, 刷新页面也会调用 function showTask() { // 为什么置空(初始值为空字符串), 因为后面是追加, 所以追加之前, 内容最好为空, 其他特殊需求另说 // 保持数据类型一致, 是个好习惯, 通过给初始值, 来确定变量的数据类型 // 这里如果不给初始值, 会出现undefined字符串 var doneHTML = ""; // 已完成内容区的html代码 var taskHTML = ""; // 未完成内容区的html代码 var delHTML = ""; // 已删除内容区的html代码 console.log(aTaskList); // forEach遍历数组, 参数是一个函数, 函数有两个参数, 一个是value, 一个是key, 注意一下顺序问题, value在前, key在后 aTaskList.forEach(function(value, key) { // 动态拼接html, 然后写入 if (!value.isDone && !value.isDel) { // 未完成时, 进入 taskHTML += '<div index="' + key + '" class="task-item">'; taskHTML += '<span><input index="' + key + '" type="checkbox" /></span>' + "\n"; // 为什么有个\n, 为了保证和原来的html一致, 这个并不是必须的 taskHTML += '<span class="task-content">' + value.content + "</span>"; taskHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; taskHTML += '<span><i index="' + key + '" class="iconfont icon-edit"></i></span>'; taskHTML += "</div>"; } if (value.isDone && !value.isDel) { // 已完成, 进入 doneHTML += '<div index="' + key + '" class="task-item">'; // key的目的: key是对象在数组中的下标, 用来标记对象在数组中的位置 doneHTML += '<span><input checked index="' + key + '" type="checkbox" /></span>' + "\n"; doneHTML += '<span class="task-content">' + value.content + "</span>"; doneHTML += '<span><i index="' + key + '" class="iconfont icon-del"></i></span>'; doneHTML += '<span><i index="' + key + '" class="iconfont icon-edit"></i></span>'; doneHTML += "</div>"; } if (value.isDel) { // 已删除, 进入 delHTML += '<div index="' + key + '" class="task-item">'; delHTML += '<span><input index="' + key + '" type="checkbox" /></span>'; // 加删除线, 方式二, 直接判断, 修改html if (value.isDone) { delHTML += '<span class="task-content is-deleted">' + value.content + "</span>"; } else { delHTML += '<span class="task-content">' + value.content + "</span>"; } delHTML += '<span><i index="' + key + '" class="iconfont icon-remove"></i></span>'; delHTML += '<span><i index="' + key + '" class="iconfont icon-undo"></i></span>'; delHTML += "</div>"; } }); $(".task-done").html(doneHTML); // 写入"已完成区"的html代码, 原来的都覆盖了 $(".task-list").html(taskHTML); // 写入"未完成区"的html代码, 原来的都覆盖了 $(".task-deleted").html(delHTML); // 写入"已删除区"的html代码, 原来的都覆盖了 // 监听复选框 $("input[type=checkbox]").click(function() { if (!$(this).attr("checked")) { // 未选中状态 ==> 已选中, 修改对象属性, isDone ==> 1 aTaskList[$(this).attr("index")]["isDone"] = 1; // 实际上更新了的数组 } else { // 已选中状态 ==> 未选中, 修改对象属性, isDone ==> 0 aTaskList[$(this).attr("index")]["isDone"] = 0; // 实际上更新了的数组 } updateData(); // 牵涉到方法重载, 参数个数不同, 干不同的事 }); // 监听未完成区的删除按钮(小垃圾桶) $(".task-item .icon-del").on("click", function() { // 修改当前对象的isDel属性为1 aTaskList[$(this).attr("index")]["isDel"] = 1; // 改为1之后, html代码也要相应的改变 updateData(); }); // 监听已删除区的还原按钮 $(".task-deleted .icon-undo").on("click", function() { // 修改当前对象的isDel属性为1 aTaskList[$(this).attr("index")]["isDel"] = 0; // 改为1之后, html代码也要相应的改变 updateData(); }); // 监听已完成区的删除按钮 $(".task-done .icon-del").on("click", function() { // 修改当前对象的isDel属性为1 aTaskList[$(this).attr("index")]["isDel"] = 1; // 改为1之后, html代码也要相应的改变 updateData(); }); } // 内容压缩, 如果超过30个, 就截取30, 加上省略号, 如果不超过30, 原路返回 function compressContent(str) { if (str.length >= 30) { return str.slice(0, 30) + "···"; } else { return str; } } // 组合操作, 更新数组, 更新缓存, 更新html内容 function updateData(obj) { if (arguments.length) { // 把对象添加到数组, unshift, 保证最后添加的, 在第一个 aTaskList.unshift(obj); } // 更新完数组, 更新缓存, 保持数组中的数据和缓存中一致 localStorage.setItem("task-list", JSON.stringify(aTaskList)); // 刷新内容区 showTask(); // // 动态添加删除线(方式一) // aTaskList.forEach(function(value, key) { // if (value.isDone) { // $(".task-item[index=" + key + "] .task-content").addClass("is-deleted"); // } // }); } }); ``` # 请问大家几个问题 1. 中文和英文长度不一致, 怎么办? 2. 压缩后, 最后一个字符恰好是标点, 怎么办? 3. 对象加入数组时, 如果用 push, 在展示时, 如何让最后添加的对象, 第一个展示? 4. 能不能, 把删除内容区域的已完成和未完成进行分组呢? 比如已完成在上, 未完成在下?