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举 ...
随机推荐
- [LeetCode&Python] Problem 821. Shortest Distance to a Character
Given a string S and a character C, return an array of integers representing the shortest distance f ...
- .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
林德熙 小伙伴希望保存一个文件,并且希望如果出错了也要不断地重试.然而我认为如果一直错误则应该对外抛出异常让调用者知道为什么会一直错误. 这似乎是一个矛盾的要求.然而最终我想到了一个办法:让重试一直进 ...
- 前端学习——jquery操作例子
一.jquery和DOM函数的转换 . jquery转换成dom $(] . dom转换成jquery var i1=documen.getElementById('#i1')---------> ...
- 【正则表达式】java应用正则表达式
一:简单应用 /** * * ' * & * ' * & * & * ' * ' * ' * sources=sdcg'hde&xyz'dfa&&ad' ...
- 在Spark上通过BulkLoad快速将海量数据导入到Hbase
我们在<通过BulkLoad快速将海量数据导入到Hbase[Hadoop篇]>文中介绍了一种快速将海量数据导入Hbase的一种方法,而本文将介绍如何在Spark上使用Scala编写快速导入 ...
- ballerina 学习二十六 项目docker 部署&& 运行(二)
ballerina 从发布,到现在官方文档的更新也是很给力的,同时也有好多改进,越来越好用了 可以参考官方文档 https://ballerina.io/learn/by-guide/restful- ...
- apache flink kubernetes 运行试用
类似docker-compose 运行模式,使用的是deploy 的模式 deploy yaml 文件 deploy-k8s-yaml apiVersion: extensions/v1beta1 k ...
- http-equiv 了解
META标签是HTML语言HEAD区的一个辅助性标签,它位于HTML文档头部的<HEAD>标记和<TITLE>标记之间,它提供用户不可见的信息.meta标签通常用来为搜索引擎r ...
- Effective C++:条款23:宁以non-member、non-friend替换member函数
(一) 有个class来表示网页浏览器: class WebBrowser { public: void clearChache(); void clearHistory(); void remove ...
- MySQL中drop,truncate 和delete的区别
注意:这里说的delete是指不带where子句的delete语句 相同点: truncate和不带where子句的delete, 以及drop都会删除表内的数据 不同点: truncate和 del ...