1.需求:

实现一个如下页面:

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

2.实现代码:

HTML:

 <!-- author: wyb -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>js todo</title>
<style>
*{
margin: 0;
padding: 0;
}
.container{
width: 60%;
margin: 0 auto;
}
.button{
margin-right: 5px;
}
.complete{
color: red;
text-decoration: line-through;
}
.pub-time{
margin-left: 15px;
}
</style>
</head>
<body> <div class="container">
<!-- todo输入框 -->
<div class="todo-form">
<input type="text" id="input">
<button id="button-add">add</button>
</div>
<!-- todo列表 -->
<div id="todo-list">
<!-- 示例todo -->
<div class="todo-cell">
</div>
</div>
</div> <script src="todo.js"></script>
</body>
</html>

JavaScript:

 // 封装输出
var log = function() {
console.log.apply(console, arguments)
}; // 字符串处理:
var todoTemplate = function (todo) {
// 下面是JavaScript中的字符串替换:
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>`;
return t;
}; // 插入新元素
var insertTodo = function (todo) {
// 获得todo-cell的HTML字符串:
var todoItem = todoTemplate(todo); var todoList = document.querySelector("#todo-list");
todoList.insertAdjacentHTML('beforeend', todoItem);
}; // 开关一个元素的某个class
var toggleClass = function(element, className) {
if (element.classList.contains(className)) {
element.classList.remove(className)
} else {
element.classList.add(className)
}
}; // 得到当前时间
var currentTime = function () {
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;
var day = d.getDate();
var hour = d.getHours();
var minute = d.getMinutes();
var second = d.getSeconds();
// 时间格式处理
if(minute <= 9){
minute = "0" +minute
}
if(second <= 9){
second = "0" +second
} var timeString = `${year}/${month}/${day} ${hour}:${minute}:${second}`;
log("now time is: ", timeString);
return timeString
}; // 返回自己在父元素中的下标
var indexOfElement = function(element) {
var parent = element.parentElement;
for (var i = 0; i < parent.children.length; i++) {
var e = parent.children[i];
if (e === element) {
return i
}
}
}; // 保存 todoList
var saveTodos = function() {
var s = JSON.stringify(todoArray);
localStorage.todoArray = s;
}; var loadTodos = function() {
var s = localStorage.todoArray;
return JSON.parse(s);
}; // 事件处理相关:
// 响应事件函数:
var bindEventAdd = function () {
var buttonAdd = document.getElementById("button-add");
buttonAdd.addEventListener('click', function () {
log("button-add click"); // 获得todo的值:
var task = document.getElementById("input").value;
// log(task);
// 获得todo对象
var todo = {
'task': task,
'time': currentTime(),
};
// 将数据存入数组中
todoArray = loadTodos();
todoArray.push(todo);
saveTodos(); // 插入todo-list:
insertTodo(todo)
});
}; var bindEventEnter = function(){
var todoList = document.querySelector("#todo-list");
todoList.addEventListener('keydown', function (event) {
log('todo keydown: ', event, event.target);
var target = event.target;
if(event.key === 'Enter') {
log('按了回车');
// 失去焦点
target.blur();
// 阻止默认行为的发生, 也就是不插入回车
event.preventDefault();
// 更新todo
var index = indexOfElement(target.parentElement);
log('update index: ', index);
// 把元素在 todoList 中更新
todoArray = loadTodos();
todoArray[index-1].task = target.innerText;
saveTodos();
}
});
}; var bindEventButton = function () {
// bindEventButton -> 复制todo所在div中的3个按钮的响应
var todoList = document.querySelector("#todo-list");
todoList.addEventListener('click', function (event) {
log('click: ', event, event.target);
// 获得点击对象和其父元素(todo的div)
var target = event.target;
var todoDiv = target.parentElement; // complete和delete和update的具体操作:
if(target.classList.contains('button-complete')) {
// 给 todo的div 开关一个状态 class
toggleClass(todoDiv, 'complete')
} else if (target.classList.contains('button-delete')) {
log('delete');
var index = indexOfElement(todoDiv) - 1;
log(index);
// 删除父节点
todoDiv.remove();
// 把元素从 todoArray 删除:
// delete todoArray[index] -> 不是完全删除,删除的数据变成了undefined依然留着数组中
todoArray = loadTodos();
log("delete: ", todoArray[index]);
todoArray.splice(index, 1);
log(todoArray);
saveTodos();
}
else if (target.classList.contains('button-update')) {
log('update');
var cell = target.parentElement;
var span = cell.children[3];
log("span is: ", span);
span.setAttribute("contenteditable", true);
// span.contentEditable = true // 同理
span.focus();
}
});
}; var bindEventBlur = function() {
var todoList = document.querySelector('#todo-list');
todoList.addEventListener('blur', function(event){
log('todo blur: ', event, event.target);
var target = event.target;
if (target.classList.contains('todo-label')) {
log('update and save');
// 让 span 不可编辑
target.setAttribute('contenteditable', 'false');
// 更新todo
var index = indexOfElement(target.parentElement);
log('update index: ', index);
// 把元素在 todoList 中更新
todoArray = loadTodos();
todoArray[index-1].task = target.innerText;
saveTodos()
}
}, true)
}; // 绑定事件:
var bindEvents = function () {
// 添加todo
bindEventAdd();
// 文本框输入todo 按回车保存
bindEventEnter();
// 完成按钮和删除按钮和编辑按钮
bindEventButton();
// 文本框失去焦点后保存todo
bindEventBlur()
}; // 初始化todo:
var initTodos = function () {
var todoArray = loadTodos();
for (var i = 0; i < todoArray.length; i++) {
var todo = todoArray[i];
insertTodo(todo);
}
}; // 存储数据
var todoArray = [];
// 程序主入口
var __main = function (){
// 绑定事件:
bindEvents(); // 程序加载后, 加载 todoArray 并且添加到页面中
initTodos(); }; __main(); // 一些说明:
// 事件委托相关概念
// ===
//
// 问题在于, todo都是运行的时候才添加的元素
// 对于这样的元素, 我们没办法实现绑定事件
// 我们可以把 click 事件绑定在事先存在的父元素上
// 通过父元素响应click事件 调用相应的事件响应函数
// 而事件响应函数会被传入一个参数, 就是事件本身
// 然后在运行的时候通过 event.target 属性(发起事件的元素,例如某个按钮)
// 来检查被点击的对象是否是需要的对象, 这个概念就是事件委托 // 与存储相关的问题:
// ===
// localStorage 可以用来存储字符串数据, 在浏览器关闭后依然存在
// 存储方法如下:
// localStorage.name = 'wyb';
// 关闭浏览器, 注释上一句代码
// 再次用同一个浏览器打开该项目, 仍然能获取到这个值
// log('关闭浏览器后: ', localStorage.name);
// localStorage删除数据:
// localStorage.removeItem("name");
// 注意:
// 利用 localStorage 就可以 存储todo
// 但是 todo存在于array中
// 而 localStorage 只能存储 string 数据
// 所以没办法直接存储todo数据
//
// 可行的办法如下:
// 存储的时候把 array 转换为字符串 读取的时候把字符串转成 array
// 这个过程通常被称之为 序列化 和 反序列化
// 在 js 中, 序列化使用 JSON 格式
//
// var s = JSON.stringify([1, 2, 3, 4]);
// log('序列化后的字符串', typeof s, s);
// var a = JSON.parse(s);
// log('反序列化后的数组', typeof a, a);
// 输出结果:
// 序列化后的字符串 string [1,2,3,4]
// 反序列化后的数组 object Array(4)
//
// 使用 JSON 序列化后, 就可以把 todo存入浏览器的 localStorage 了
//
// 与时间相关的问题: JavaScript中的时间对象 -> Date对象
// ===
// 常用用法如下:
/*
var d = new Date()
d.getFullYear()
年份, 2016
d.getMonth()
月份, 0-11
d.getDate()
日期, 1-31
d.getHours()
小时, 0-23
d.getMinutes()
分钟, 0-59
d.getSeconds()
秒数, 0-59
d.getMilliseconds()
毫秒, 0-999
d.getDay()
星期几, 0-6
*/

3.实现效果:

(1)最开始界面

(2)输入信息点击add

(3)点击完成

(4)点击删除

 

(5)点击编辑 

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

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

DOM实战-js todo的更多相关文章

  1. 《Cocos2d-x实战 JS卷 Cocos2d-JS开发》上线了

    感谢大家一直以来的支持! 各大商店均开始销售:京东:http://item.jd.com/11659698.html当当:http://product.dangdang.com/23659808.ht ...

  2. how to get iframe dom in js

    how to get iframe dom in js https://stackoverflow.com/questions/3999101/get-iframes-document-from-ja ...

  3. HTML(.js) – 最简单的方式操作 DOM 的 JS 库

    HTML(.js) 是一个轻量的(压缩后~2kb) JavaScript 库,简化了与 DOM 交互的方法. 这个 JavaScript 库的方法可读性很好,并具有搜索和遍历 DOM 的方法.相比 j ...

  4. 开发成功-cpu-mem监控动态折线图--dom esayui js java

    jsp ------------------------------------------------------------------------------------------- ---- ...

  5. JavaScript DOM实战:创建和克隆元素

    DOM来创建和克隆元素. createElement()和createTextNode() createElement()和createTextNode()做的事情正如它们的名字所说的那样.最常见的J ...

  6. JS操作DOM对象——JS基础知识(四)

    一.JavaScript的三个重要组成部分 (1)ECMAScript(欧洲计算机制造商协会) 制定JS的规范 (2)DOM(文档对象模型)重点学习对象 处理网页内容的方法和接口 (3)BOM(浏览器 ...

  7. JS中的函数、Bom、DOM及JS事件

    本期博主给大家带来JS的函数.Bom.DOM操作,以及JS各种常用的数据类型的相关知识,同时,这也是JavaScript极其重要的部分,博主将详细介绍各种属性的用法和方法. 一.JS中的函数 [函数的 ...

  8. DOM 以及JS中的事件

    [DOM树节点] DOM节点分为三大节点:元素节点,文本节点,属性节点. 文本节点,属性节点为元素节点的两个子节点通过getElment系列方法,可以去到元素节点 [查看节点] 1 document. ...

  9. 从零开始的JS生活(二)——BOM、DOM与JS中的事件

    上回书说道,JS中变量.运算符.分支结构.循环和嵌套循环等内容.本回就由本K给大伙唠唠JS中的BOM.DOM和事件. 一."花心大萝卜"--BOM 1.震惊,FFF团为何对BOM举 ...

随机推荐

  1. 本地和服务器(ubuntu)文件同步

    秘钥登录远端服务器 rsync -avze 'ssh -i ./id_rsa' root@remoteIp:/xx/remotefile.txt ./localpath (./id_rsa为本地秘钥路 ...

  2. 51Nod:独木舟问题(贪心)

    n个人,已知每个人体重,独木舟承重固定,每只独木舟最多坐两个人,可以坐一个人或者两个人.显然要求总重量不超过独木舟承重,假设每个人体重也不超过独木舟承重,问最少需要几只独木舟? 输入 第一行包含两个正 ...

  3. php-fpm简介

    What is PHP-FPM? PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation with ...

  4. 2018-2019-2 网络对抗技术 20165212 Exp4 恶意代码分析

    2018-2019-2 网络对抗技术 20165212 Exp4 恶意代码分析 原理与实践说明 1.实践目标 监控你自己系统的运行状态,看有没有可疑的程序在运行. 分析一个恶意软件,就分析Exp2或E ...

  5. hdu1428 记忆化搜索(BFS预处理最短路径和+DP+DFS)

    题意:有一块 n * n 大小的方形区域,要从左上角 (1,1)走到右下角(n,n),每个格子都有通过所需的时间,并且每次所走的下一格到终点的最短时间必须比当前格子走到重点的最短时间短,问一共有多少种 ...

  6. sys目录下常用文件汇总

    1. /sys/kernel/debug/gpio 可以实时反馈系统中感兴趣的gpio引脚的状态 root@g6s:/sys/kernel/debug# cat gpio gpiochip7: GPI ...

  7. opencart安装和使用PHPMailer

    一.安装PHPMailer 1)先给opencart项目安装vqmod 下载最新版本: http://code.google.com/p/vqmod (目前最新版本是vqmod-2.5.1-stand ...

  8. Openssl将crt证书和key私钥合成pfx证书

    下载OpenSSL地址:http://slproweb.com/products/Win32OpenSSL.html 下载安装openssl 选择对应OpenSSL版本进行下载下载. 运行安装程序Wi ...

  9. FastAdmin 教程草稿大纲

    FastAdmin 教程草稿大纲 计划 FastAdmin 教程大纲 FastAdmin 环境搭建 phpStudy 2018 安装 一键 CRUD 教程 环境变量配置 环境安装 命令行安装 列出所需 ...

  10. 使用 mysqldump 备份时的一些参数

    因为还没有用到 ThinkPHP 的迁移组件,暂时使用 mysqldump 来备份,并版本控制. 有几个参数需要用到. --skip-dump-date 不要完成时间. --skip-extended ...