个人待办事项记录簿

简介:

  • 这是基于todoMVC模板的一个记录待办事项页面,模板地址。
  • 该模板需要自行通过npm下载初始化页面样式,而且没有各种交互的逻辑代码。
  • 该项目的交互代码是采用原生js结合es6的数组方法实现。
  • 虽然项目要实现的功能多,但实现起来都比较简单,非常适合练手。

实现的功能

  • 待办事项的完成、添加、删除、全选和反选所有待办等功能,单击重新编辑待办功能、实时显示剩余未完成待办的功能、三个按钮的事项筛选功能、将所有事项存储到客户端中、一键清除所有已完成事项功能;
  • 利用单一数据流的特点对项目进行开发。

展示

具体实现逻辑

  • 事项的渲染(事项的刷新)

    • 这里需要实现事项的初始化渲染和事项的刷新

      • 在li里自定义一个属性来记录当前渲染的这条数据的id,即data-id=${randerData[i].id}

      • li标签里的completed类名是用于控制待办事项是否完成的样式的,如果添加completed类名,那么该li标签就有待办事项完成的css样式,该类名用数据的done属性进行控制,该属性也用于控制input单选框是否选中

      • 初始化渲染只要对数据遍历,然后将拼接的字符串插入页面,

      • 刷新事项就需要每次置空标签和字符串。

// 数据渲染函数function rander(data) {if (data.length == 0) {return;}let str = '';let count = 0;let randerData = null;oTodoList.innerHTML = '';// 对数据进行筛选switch (location.hash) {// 待办case '#/active':randerData = data.filter(function (value, index) {return !value.done;})break;// 已完成case '#/completed':randerData = data.filter(function (value, index) {return value.done;})break;default:randerData = data;}for (let i = 0; i < randerData.length; i++) {str += `<li data-id=${randerData[i].id} class=${randerData[i].done ? "completed" : ""} ><div class="view"><input class="toggle" type="checkbox" ${randerData[i].done ? "checked" : ''}><label>${randerData[i].todo}</label><button class="destroy"></button></div><input class="edit" value="${randerData[i].todo}"></li>`}// 展示剩余未完成待办数量的功能randerData.forEach(value => {if (value.done == false) {count++;}})oTodoCount.innerHTML = count;oTodoList.innerHTML = str;}
  • 利用事件冒泡原理给事项的父级ul绑定点击事件,然后根据事件对象里记录DOM不同的class类值来实现待办事项的完成、删除、单击编辑待办功能

    • 待办事项的点击完成功能(点击对象为CheckBox单选框)(即事件源对象里记录的DOM的class类为toggle)

      • 待办事项的选中和不选中的切换功能:获取事件源DOM对象的li父级上记录的id值,根据这个id值找到data里的数据,将这条数据的done赋值为事件源DOM对象的checked的值,然后重新渲染修改后data。(checked的值用于控制li标签的class类名completed的有无,class类名值为completed就会让li标签具有待办已完成的样式)然后重新渲染data。
 // 记录当前点击到的li的索引let id = e.target.parentElement.parentElement.getAttribute('data-id');let temp = data.find(value => value.id == id);// 只将完成待办的复选框点击事件冒泡执行if (e.target.className == 'toggle') {// 待办事项完成,即将点击到的复选框选中,然后再重新渲染temp.done = e.target.checked;rander(data);}
  • 待办事项的点击删除功能(点击对象为删除按钮)(即事件源对象里记录的DOM的class类为destroy)

    • 获取事件源DOM对象的li父级上记录的id值,根据这个id值找到data里的数据,利用indexOf获取到这条数据在data里的索引,然后利用数组方法splice剪切掉这条数据,再重新渲染修改后data。
     // 删除待办功能if (e.target.className == 'destroy') {data.splice(data.indexOf(temp), 1);rander(data);}
  • 待办事项的点击重新编辑功能(点击对象为label标签)(即事件源对象里记录的DOM的标签名为LABEL)

    • 给当前点击到的label标签的父级li标签加上editing的class类名(以拼接字符串的方式)。
// 单击可以编辑待办
if (e.target.tagName == 'LABEL') {// 待办编辑框展示e.target.parentElement.parentElement.className += ' editing';// 让文本框自动选中let oInput = e.target.parentElement.parentElement.getElementsByClassName('edit')[0];oInput.focus();// 调整输入框的光标到文字最右边oInput.setSelectionRange(-1, -1);
}
- 当修改完成后就需要将这个修改后的值赋值到data数组的todo里:首先需要给class类名为todo-list绑定一个回车事件,再获取到事件源对象上的DOM对象,然后去找父级的li利用getAttridute()来获取到保存的id值,再在data数组里找到这条数据,并将它的todo值,当前这个input框的value值,然后重新渲染data。
oTodoList.addEventListener('keyup', (e) => {// 获取当前选中的重新编辑输入框的索引let id = e.target.parentElement.getAttribute('data-id');// 根据索引找到待办项lilet temp = data.find(function (value) {return value.id == id;})// 将重新编辑过的待办项进行赋值if (e.target.className == 'edit' && e.keyCode == 13) {temp.todo = e.target.value;rander(data);}})
  • 待办事项的全选和反选功能

    • 给toggle-all的label标签绑定change事件,该事件是用于监听input框聚焦和失焦的变化。

    • 只需要将与label绑定的input标签的checked值赋值到data里的每条数据的done的值中,然后重新渲染data,就可以根据一个按钮实现一键全选和反选的功能。

// 实现待办的全选和反选功能oToggleAll.addEventListener('change', () => {data.forEach(value => value.done = oToggleAll.checked);rander(data)})
  • 因为每完成一个待完成都有可能触发全选功能或全不选功能,所以就需要在每次完成一项待办任务时都要对data里的每条数据的done值进行检测,如果所有的事项全部完成就要让input上的checked为true,否则就为false。
// 当先代办完成后判断是否触发全选按钮
if (data.every(value => value.done) == true) {oToggleAll.checked = true;} else {oToggleAll.checked = false;}
  • 添加待办功能

    • 给class类名为new-todo的input标签绑定回车事件,然后在data的最后一位添加一个新数据,todo值为输入的input的value值,然后重新渲染data,创建完毕后需要清空input的value值。
 // 添加待办功能oNewTodo.addEventListener('keyup', e => {if (e.key == 'Enter') {let obj = {id: data.length == 0 ? 1 : data[data.length - 1].id + 1,todo: e.target.value,done: false,}data.push(obj);rander(data);// 待办创建完毕清除输入框e.target.value = '';}})
  • 3个按钮的筛选功能

    • 3个按钮的样式改变

      • 在这个下载的模板里这几个按钮的功能都是a标签以hash跳转的方式来写的,所以给window绑定监听页面以hash跳转就触发的hashchange事件。
      • 遍历这3个a标签的hash值是否与当前显示页面的hash值相同,相同就要给这个a标签加上一个值为selected的class类名,该类名是用于控制选中状态样式
// 三个按钮的筛选功能window.onhashchange = function () {// a标签样式class类名的修改Array.from(oFiltersList).forEach(function (value, index) {if (value.hash == location.hash) {document.getElementsByClassName('selected')[0].className = '';oFiltersList[index].className = 'selected';}})// 重新渲染数组rander(data);}
  • 筛选功能的实现

    • 该功能不需要再去写方法去实现,只需要在事项渲染阶段时就对事项进行根据当前打开页面的hash值来判断需要展示什么类型的数据,然后用数组的filter方法对数组进行筛选。
     // 对数据进行筛选switch (location.hash) {// 待办case '#/active':randerData = data.filter(function (value, index) {return !value.done;})break;// 已完成case '#/completed':randerData = data.filter(function (value, index) {return value.done;})break;default:randerData = data;}
  • 一键清除完成事项功能

    • 只需要将data里done状态为false的都筛选出来,然后把这些重新筛选出来的数据进行渲染。
 // 一键清除完成事项功能oClearCompleted.onclick = function (e) {data = data.filter(function (value) {return !value.done;})rander(data);}
  • 将数据存储到客户端功能

    • 以localStorage.setItem(‘todoList’, JSON.stringify(data))的形式将数据存储到localstorage中,并且在每次重新渲染data时都要存储一次
  • 给修改待办的input框绑定一个失焦事件

    • 该事件绑定在class类名为todo-list的标签上,所以不能绑定blur事件,但可用focuout代替。当失焦时将当前li上editing的类名删掉。
// 取消重新编辑待办项的聚焦事件,blur失焦事件无法冒泡,可用focusout代替
oTodoList.addEventListener('focusout', function (e) {if (e.target.className == 'edit') {// 将li上editing的类名删掉let str = e.target.parentElement.getAttribute('class')e.target.parentElement.className = str.replace(' editing', '')}
})rander(data);
}

html部分:

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Template • TodoMVC</title><link rel="stylesheet" href="node_modules/todomvc-common/base.css"><link rel="stylesheet" href="node_modules/todomvc-app-css/index.css"><link rel="stylesheet" href="css/app.css"></head><body><section class="todoapp"><header class="header"><h1>todos</h1><input class="new-todo" placeholder="创建一个待办事项" autofocus></header><section class="main"><input id="toggle-all" class="toggle-all" type="checkbox"><label for="toggle-all">Mark all as complete</label><ul class="todo-list"></ul></section><footer class="footer"><span class="todo-count"><strong> 0 </strong> 件未完成</span><ul class="filters"><li><a class="selected" href="#/">所有事项</a></li><li><a href="#/active">待办事项</a></li><li><a href="#/completed">完成事项</a></li></ul><button class="clear-completed">清除完成事项</button></footer></section><script src="js/app.js"></script></body>
</html>

javaScript部分

window.onload = () => {let data;let dataStr = localStorage.getItem('todoList');// 获取要渲染的数据if (dataStr) {data = JSON.parse(dataStr);} else {data = [];}let oTodoList = document.getElementsByClassName('todo-list')[0];let oToggleAll = document.getElementsByClassName('toggle-all')[0];let oNewTodo = document.getElementsByClassName('new-todo')[0];let oTodoCount = document.getElementsByClassName('todo-count')[0].getElementsByTagName('strong')[0];let oFiltersList = document.getElementsByClassName('filters')[0].getElementsByTagName('a');let oClearCompleted = document.getElementsByClassName('clear-completed')[0];// 数据渲染函数function rander(data) {if (data.length == 0) {return;}let str = '';let count = 0;let randerData = null;oTodoList.innerHTML = '';// 对数据进行筛选switch (location.hash) {// 待办case '#/active':randerData = data.filter(function (value, index) {return !value.done;})break;// 已完成case '#/completed':randerData = data.filter(function (value, index) {return value.done;})break;default:randerData = data;}for (let i = 0; i < randerData.length; i++) {str += `<li data-id=${randerData[i].id} class=${randerData[i].done ? "completed" : ""} ><div class="view"><input class="toggle" type="checkbox" ${randerData[i].done ? "checked" : ''}><label>${randerData[i].todo}</label><button class="destroy"></button></div><input class="edit" value="${randerData[i].todo}"></li>`}// 展示剩余未完成待办数量的功能randerData.forEach(value => {if (value.done == false) {count++;}})oTodoCount.innerHTML = count;oTodoList.innerHTML = str;}// 给ul绑定事件,将li里的事件冒泡执行oTodoList.addEventListener('click', (e) => {// 记录当前点击到的li的索引let id = e.target.parentElement.parentElement.getAttribute('data-id');let temp = data.find(value => value.id == id);// 只将完成待办的复选框点击事件冒泡执行if (e.target.className == 'toggle') {// 待办事项完成,即将点击到的复选框选中,然后再重新渲染temp.done = e.target.checked;// 当先代办完成后判断是否触发全选按钮if (data.every(value => value.done) == true) {oToggleAll.checked = true;} else {oToggleAll.checked = false;}// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data);}// 删除待办功能if (e.target.className == 'destroy') {data.splice(data.indexOf(temp), 1);// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data);}// 单击可以编辑待办if (e.target.tagName == 'LABEL') {// 待办编辑框展示e.target.parentElement.parentElement.className += ' editing';// 让文本框自动选中let oInput = e.target.parentElement.parentElement.getElementsByClassName('edit')[0];oInput.focus();// 调整输入框的光标到文字最右边oInput.setSelectionRange(-1, -1);}})oTodoList.addEventListener('keyup', (e) => {// 获取当前选中的重新编辑输入框的索引let id = e.target.parentElement.getAttribute('data-id');// 根据索引找到待办项lilet temp = data.find(function (value) {return value.id == id;})// 将重新编辑过的待办项进行赋值if (e.target.className == 'edit' && e.keyCode == 13) {temp.todo = e.target.value;// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data);}})// 实现待办的全选和反选功能oToggleAll.addEventListener('change', () => {data.forEach(value => value.done = oToggleAll.checked);// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data)})// 添加待办功能oNewTodo.addEventListener('keyup', e => {if (e.key == 'Enter') {let obj = {id: data.length == 0 ? 1 : data[data.length - 1].id + 1,todo: e.target.value,done: false,}data.push(obj);// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data);// 待办创建完毕清除输入框e.target.value = '';}})// 三个按钮的筛选功能window.onhashchange = function () {// a标签样式class类名的修改Array.from(oFiltersList).forEach(function (value, index) {if (value.hash == location.hash) {document.getElementsByClassName('selected')[0].className = '';oFiltersList[index].className = 'selected';}})// 重新渲染数组rander(data);}// 一键清除完成事项功能oClearCompleted.onclick = function (e) {data = data.filter(function (value) {return !value.done;})// 当数据发生修改就要把数据存储到localStorage中localStorage.setItem('todoList', JSON.stringify(data));rander(data);}// 取消重新编辑待办项的聚焦事件,blur失焦事件无法冒泡,可用focusout代替
oTodoList.addEventListener('focusout', function (e) {if (e.target.className == 'edit') {// 将li上editing的类名删掉let str = e.target.parentElement.getAttribute('class')e.target.parentElement.className = str.replace(' editing', '')}
})rander(data);
}

TodoMVC模板的原生js待办事项卡片相关推荐

  1. 如何使用Things3创建重复的待办事项?

    Things3是Mac平台上一款非常优秀的任务管理软件,严格按照GTD流程来规划人们的任务安排,可帮助您输入,组织和处理待办事项列表中的项目.那我们如何让Things提醒您每隔几天,几周或几个月需要做 ...

  2. 适用于Android的最佳免费待办事项列表应用程序以及如何使自己成才

    如果您没有组织任务,那么跟踪任务可能会很麻烦. 这就是待办事项清单的帮助. 在这篇文章中,我将向您展示一些适用于Android的最佳免费待办事项列表应用程序. 然后,我将为您提供一些有关如何创建自己的 ...

  3. 原生vue.js实现待办事项清单,支持增删改查

    源码部分: <!DOCTYPE html> <html lang="en"> <head><!--设置页面的utf-8编码格式--> ...

  4. 鸿蒙开发-从搭建todolist待办事项来学习组件与js之间的交互

    场景 鸿蒙开发-实现页面跳转与页面返回: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/118383025 在上面实现从主页面跳转 ...

  5. 从零入门 HTML、CSS、JS、React,构建 ToDo 待办事项管理项目!

    在今天,前端工程师已经成为研发体系中的重要岗位之一.可是与此相对的是,极少大学的计算机专业愿意开设前端课程,大部分前端工程师的知识,也都是在实践和工作中不断学习的. 最近收到很多同学的后台留言,说希望 ...

  6. 原生JS实现简易模板

    原生JS实现简易模版 String.prototype.renderTpl = function (obj) {var myself = this;var render = '';var source ...

  7. hyperapp 共享_使用Hyperapp(1KB JS微框架)构建待办事项列表

    hyperapp 共享 在本教程中,我们将使用Hyperapp构建待办事项列表应用程序. 如果您想学习函数式编程原理,但又不想陷入细节,请继续阅读. Hyperapp现在很热门. 它最近在GitHub ...

  8. Mars3D项目模板:基础项目 原生JS版 (widget方式)介绍

    项目介绍 Mars3D基础项目 是基于Mars3D平台 做的一个应用系统,提供的一个基础项目模版,包含常用基础地图功能,可在该基础项目上快速开发搭建新项目.方便快速搭建三维地图产品,敏捷开发,可复用, ...

  9. todomvc html css模板,通过todoMVC来学vue.js的使用

    这是vue官网的一个例子,挺适合作为vue应用的入门的. 通过这个应用,我们能学到vue的[双向绑定],[v-for][事件],[计算],[指令]等的应用. 应用预览 这个应用开头是这样的 初始状态. ...

最新文章

  1. 迁移学习之ResNet50和ResNet101(图像识别)
  2. WebConfig的经典使用,大家一起交流一下吧!!!
  3. android studio 3.0新功能介绍
  4. 三个activity之间跳转 数据传递_第二百四十二回:Android中Fragment之间的数据传递概述...
  5. Week1 Team Homework #2 Introduction of team member with photos
  6. 纳尼???我JVM优化过头了,直接把异常信息优化没了?怎么办
  7. “速课小龙”项目冲刺3
  8. 求字符串全排列的递归算法
  9. SysTick_Handler cortex-m0 LPC1114
  10. linux脚本grep,linux shell 脚本之深入浅出的grep的用法
  11. 遥感图像——多波段数据存储的方式
  12. 阿里巴巴Java开发文档2020版学习-常量定义
  13. Microsoft Office 不同电脑不同电脑登录用户的数据同步
  14. 如何用MATLAB加速,使用MATLAB加速C/C++算法开发
  15. 不怕被群主踢,安心分享小游戏续命,上分好办法!
  16. 把图标变成圆形的html_css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局...
  17. win10计算机系统盘不足,Win10系统C盘空间不足?简单5招,教你安全清理C盘垃圾!...
  18. Ubuntu18.04 鼠标键盘失灵
  19. 抖音为什么这么火?抖音用户暴涨的秘密在哪?
  20. C select 函数

热门文章

  1. 京东拼购启动2019年首轮招商 六大流量入口+社交玩法助力商家成长
  2. 绝地求生北美服务器延迟过高,《绝地求生》匹配系统出错延迟过高 官方正加紧修正...
  3. Weex框架开发 基于windows系统的环境搭建
  4. 2023年吉林建筑大学成人高考报名条件
  5. 网站标题被修改的解决办法
  6. 【西电-网信院】数据结构与算法分析2022期末考试
  7. 获取网址的ico/favicon的两种方法
  8. 小水滴2.0网站导航网模板
  9. Java时间戳转字符串
  10. systemverilog中隐式转换和显式转换(静态转换和动态转换)