DOM实战-js todo
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的更多相关文章
- 《Cocos2d-x实战 JS卷 Cocos2d-JS开发》上线了
感谢大家一直以来的支持! 各大商店均开始销售:京东:http://item.jd.com/11659698.html当当:http://product.dangdang.com/23659808.ht ...
- how to get iframe dom in js
how to get iframe dom in js https://stackoverflow.com/questions/3999101/get-iframes-document-from-ja ...
- HTML(.js) – 最简单的方式操作 DOM 的 JS 库
HTML(.js) 是一个轻量的(压缩后~2kb) JavaScript 库,简化了与 DOM 交互的方法. 这个 JavaScript 库的方法可读性很好,并具有搜索和遍历 DOM 的方法.相比 j ...
- 开发成功-cpu-mem监控动态折线图--dom esayui js java
jsp ------------------------------------------------------------------------------------------- ---- ...
- JavaScript DOM实战:创建和克隆元素
DOM来创建和克隆元素. createElement()和createTextNode() createElement()和createTextNode()做的事情正如它们的名字所说的那样.最常见的J ...
- JS操作DOM对象——JS基础知识(四)
一.JavaScript的三个重要组成部分 (1)ECMAScript(欧洲计算机制造商协会) 制定JS的规范 (2)DOM(文档对象模型)重点学习对象 处理网页内容的方法和接口 (3)BOM(浏览器 ...
- JS中的函数、Bom、DOM及JS事件
本期博主给大家带来JS的函数.Bom.DOM操作,以及JS各种常用的数据类型的相关知识,同时,这也是JavaScript极其重要的部分,博主将详细介绍各种属性的用法和方法. 一.JS中的函数 [函数的 ...
- DOM 以及JS中的事件
[DOM树节点] DOM节点分为三大节点:元素节点,文本节点,属性节点. 文本节点,属性节点为元素节点的两个子节点通过getElment系列方法,可以去到元素节点 [查看节点] 1 document. ...
- 从零开始的JS生活(二)——BOM、DOM与JS中的事件
上回书说道,JS中变量.运算符.分支结构.循环和嵌套循环等内容.本回就由本K给大伙唠唠JS中的BOM.DOM和事件. 一."花心大萝卜"--BOM 1.震惊,FFF团为何对BOM举 ...
随机推荐
- 【机器学习算法】AdaBoost自适应提升算法
前言 AdaBoost的算法步骤比较容易理解,可以参考李航老师的<统计学习方法>和July的blog. 对博主而言,最主要的是迭代部分的第二步骤是如何如何确定阈值呢,也就是说有一个特征就有 ...
- 一致性哈希算法(Consistent Hashing Algorithm)
一致性哈希算法(Consistent Hashing Algorithm) 浅谈一致性Hash原理及应用 在讲一致性Hash之前我们先来讨论一个问题. 问题:现在有亿级用户,每日产生千万级订单,如 ...
- 【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.4 LHS简介&Pattern
LHS简介 在规则文件组成章节,我们已经了解了LHS的基本使用说明.LHS是规则条件部分的统称,由0个或多个条件元素组成.前面我们已经提到,如果没有条件元素那么默认就是true. 没有条件元素,官方示 ...
- 类名.fromObject(obj)静态方法
- PHP的extension_dir设置问题
PHP安装时,extension_dir的路径要设成绝对路径:extension_dir = "D:/Tools/php-7.0.5/ext", 不然如果设成extension_d ...
- Jenkins进阶-Gitlab使用Webhook实现Push代码自动部署(3)
1.Jenkins 安装完成以后,首先我们在Jenkins中需要安装一下,Gitlab Hook Plugin 插件: 2.插件安装完成我们创建任务,在任务重构建触发器下获取回调URL: 注意: 注意 ...
- Linq快速入门——Lambda表达式的前世今生
Linq快速入门——Lambda表达式的前世今生 Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托. 何为委托 ...
- github for windows 使用
先在github上申请账号,已有略过. 下载github for windows安装,可以提前安装.NET FRAMEWORK 4.0,否则它会在线下载安装.NET 4. 安装后登录账号,不要急着CL ...
- gcc gdb调试 (二)
GDB的命令概貌——————— 启动gdb后,就你被带入gdb的调试环境中,就可以使用gdb的命令开始调试程序了,gdb的命令可以使用help命令来查看,如下所示: /home/hchen> g ...
- Qt QTableWidget用法总结
转载:李宏兵 QTableWidget是QT程序中常用的显示数据表格的空间,很类似于VC.C#中的DataGrid.说到QTableWidget,就必须讲一下它跟QTabelView的区别了. QTa ...