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. python3 线性同余发生器 ( random 随机数生成器 ) 伪随机数产生周期的一些探究

    import random x=[str(random.randint(0, 5)) for i in range(10)] x_str=''.join(x) y=[str(random.randin ...

  2. test20180922 世界第一的猛汉王

    题意 分析 由于异色点必有连边,所以一个点的covered减去两个点共有的covered就是可存在的环数,十分巧妙. 代码 #include <bits/stdc++.h> using L ...

  3. PDF文本框更改字体大小

    在Adobe Acrobat  Professional 7.0版本后里单击所插入的文本框,会出现文本框属性,此时只能改文本框的属性,不能修改文本框内的字体大小 要改字体很简单,左键选中要改的文本,按 ...

  4. struts神马的不过是对servlet、filter的封装而已,hibernate神马的也不过是对jdbc的封装而已,他们只是把一些常见的操作流程化了,如果不懂servlet、filter,不懂jdbc,使用struts和hibernate出问题了都不知道是怎么回事。

    struts神马的不过是对servlet.filter的封装而已,hibernate神马的也不过是对jdbc的封装而已,他们只是把一些常见的操作流程化了,如果不懂servlet.filter,不懂jd ...

  5. Mysql ON子句和USING子句

    Mysql ON子句和USING子句   Mysql 中联接SQL语句中,ON子句的语法格式为:table1.column_name = table2.column_name. 当模式设计对联接表的列 ...

  6. 用vbs打开文件

    set ws=createobject("wscript.shell")ws.run"打开文件所使用的程序+空格+程序路径",x,ture Run方法有三个参数 ...

  7. 标 题: 有什么办法快速把pc上的网址发送到手机上

    标  题: 有什么办法快速把pc上的网址发送到手机上 transfer2u, pushbullet都可以实现你说的功能,还可以把图片或者选中内容/剪贴板内容发送到手机.后者功能更强,还支持在电脑之间发 ...

  8. 从操作系统rm数据文件后,利用句柄与rman恢复的过程。(已验证)

    以下操作代码的流程是配的,但是相应的文件名,啥的   必须改动. 故障现象 数据文件被误删除 具体情况 接到反馈说,数据文件data20120512.dbf被误删除,需要恢复 数据库提示 ERROR ...

  9. Grid中添加链接,打开选项卡页面

    如何在grid中点击,添加一个选项卡并打开页面        function addeditnew(id, title)         {                     var node ...

  10. POJ1135 Domino Effect

    题目:http://poj.org/problem?id=1135 只是求以1为起点的最短路罢了.稍稍判断一下在边上的情况. 多亏提醒:毒数据——n==1!一定要dis [ k ] >= ans ...