周六加班没什么事,于是乎。。。上班摸鱼,用原生JS写了一个ToDo Demo,废话少叙,直接看效果:
如图,实现了以下需求(以下的具体实现我会一一道来):
0.页面的基本布局;
1.rem布局。在不同移动端模拟器下,布局也不会乱,实现了响应式布局;
2.在输入框内敲击回车键,添加代办事项;
3.待办事项<===>完成事项之间的转化;
4.删除待办事项 / 完成事项;
5.本地数据存储;
===================================================
于我来说,这个小demo,我最大的收获就是知道“事件委托”如何使用,以前仅仅停留在概念层,现在对其应用层也算是有了一定的理解。以前处理需要循环遍历的元素,例如每个li元素需要绑定点击事件,可能我的第一反应就是给每个li都绑定一个点击事件,这样写往往会导致代码臃肿,而且影响性能,毕竟每个元素都需要绑定一个点击事件。如果有需求要为一万个li绑定点击事件,那该怎么办?
这时候通过事件委托就可以很好地解决这个问题的,只需要在的父元素上绑定事件,然后通过event事件对象来确定具体的触发子元素即可,非常好用方便。
如果还不是很懂事件委托的朋友可以看看下面这篇Blog,相信看完后一定对你有很大的帮助。
js中的事件委托(事件代理)详解 - lauzhishuai - 博客园www.cnblogs.com好了,现在来说说具体是怎么实现的:
0.页面布局
这个其实没什么好说的了,flex布局用的比较多。最需要提一下的就是完成事项灰色效果的实现了:
/* 修改所有图片的颜色为黑白 (100% 灰度) */
-webkit-filter: grayscale(100%);
/* Chrome, Safari, Opera */
filter: grayscale(100%);
PS:源码我贴在文章的末尾,有需要的可以自行下载~
1.rem布局
我的Blog里面有讲,直通车------->
https://zhipengyang0605.github.io/%E5%93%8D%E5%BA%94%E5%BC%8F%E5%B8%83%E5%B1%80/2020/07/19/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E9%A1%B9%E7%9B%AE%E4%B8%AD%E4%BD%BF%E7%94%A8rem/#zhipengyang0605.github.iodemo 里面我就简单的写了这几行代码~封装成rem.js文件了
function resize () {
// 1rem = 100px
var designWidth = 750 // 设计稿的宽度
var rootFontSize = 100 // 将html root的font-size设置为100px
document.documentElement.style.fontSize = rootFontSize + 'px'
var width = document.documentElement.clientWidth // 屏幕尺寸
var fontSize;
fontSize = (width / designWidth) * rootFontSize // 模拟器上的实际字体大小
}
window.onresize = resize // 屏幕尺寸大小变化时触发
document.addEventListener('DOMContentLoaded', resize, false) // 当浏览器解析完文档触发该事件
这里我默认以iPhone6 为标准,设计稿的宽度为750px,设置网页根元素的font-size等于100px,iPhone6模拟器的宽高为375x667,因此根据这个公式
fontSize =(width / designWidth)* rootFontSize
不难得出1rem = 100px了,因此接下来的所有布局都只是用rem不再使用px。
2.在输入框内敲击回车键,添加代办事项
主要通过监听keydown事件,然后通过事件对象e的key属性的值是否为‘Enter’,若为Enter则执行render函数,render函数的作用是将数据渲染到页面上。这里的数据为dataList,初始化为空数组,每当敲击回车键后将自定义的对象push到该数组中去。定义的对象结构为:
var obj = {
isChecked: false, // checkbox的状态是否选中
content: value, // input的输入内容
isDone: false // 是否完成
}
以下是实现的主要代码:
// 鼠标按下事件
input.addEventListener('keydown', handleInput)
// input事件
function handleInput (e) {
if (e.key === 'Enter') { // 敲回车执行事件
var value = e.target.value
var obj = {
isChecked: false,
content: value,
isDone: false
}
dataList.push(obj)
// 将数据存储到本地
localStorage.setItem('toDoList', JSON.stringify(dataList));
// 在这里渲染
render()
// 输入完毕后清除输入框的内容
e.target.value = ''
}
}
// 处理渲染页面函数
function render () {
// 渲染之前清空内容
doingbox.innerHTML = ''
donebox.innerHTML = ''
// 判断是doing渲染还是done渲染
dataList.forEach(function (item, index) {
if (!item.isChecked) {
// 创建一个元素
var li = document.createElement('li')
li.innerHTML = `
<div class="item-left">
<input type="checkbox" class="checkbox" data-index = "${index}">
<span>${item.content}</span>
</div>
<button class="del" data-index = "${index}">删除</button>
`
// 插入一个元素
doingbox.appendChild(li)
li.className = 'doing-item'
} else {
// 创建一个元素
var li = document.createElement('li')
li.innerHTML = `
<div class="item-left">
<input type="checkbox" class="checkbox" checked="${item.isChecked}" data-index = "${index}">
<span>${item.content}</span>
</div>
<button class="del" data-index = "${index}">删除</button>
`
// 插入一个元素
donebox.appendChild(li)
li.className = 'done-item'
}
})
}
render函数这里有几点要特别注意:
(1).每次插入之前需要把上一次的innerHTML清空,不然会重复渲染;
(2).需要绑定data-index,方便后续删除/事项切换获取下标;
3.待办事项<===>完成事项之间的转化
思路主要是监听checkbox的change事件,通过改变checkbox的checked属性,重新设置dataList中每一项中isChecked和isDone这两个属性,然后重新调用render函数(只要dataList被操作了就需要重新调用render函数)
// checkbox改变触发
todobox.addEventListener('change', handleChangeEvent)
// 处理change事件
function handleChangeEvent (e) {
var currentindex = e.target.dataset.index
dataList[currentindex].isChecked = 'checked'
// render()
dataList[currentindex].isDone = !dataList[currentindex].isDone
if (dataList[currentindex].isDone) { //已完成
render()
} else { // 未完成
dataList[currentindex].isDone = false
// 去除checked状态
dataList[currentindex].isChecked = false
render()
}
// 将数据存储到本地
localStorage.setItem('toDoList', JSON.stringify(dataList));
}
4.删除待办事项 / 完成事项
要实现删除功能其实很简单,为每个todo/done元素绑定click事件,点击获取对应的index,然后再todoList中删除该项,然后重新调用render函数即可。绑定事件可以不用手动为每个li绑定事件,可以使用事件委托,为整个页面的某个dox绑定点击事件,结合事件对象e.target.nodeName,精确点击的元素即可。
// 点击事件
todobox.addEventListener('click', handleDel)
// 处理删除功能
function handleDel (e) {
// 只有当元素为button时才执行删除操作
if (e.target.nodeName.toLowerCase() === 'button') {
// 删除提示
var result = window.confirm('删除要三思啊~~~')
if (result) {
// 获取要删除元素的下标
var activeIndex = e.target.dataset.index
// 删除对应元素
dataList.splice(activeIndex, 1)
// 重新将数组保存到本地
localStorage.setItem('toDoList', JSON.stringify(dataList));
// 重新渲染页面
render()
}
}
}
我这里使用了window.confirm来提示是否删除,接受一个返回值,当点击确定的时候会返回true,反之为false。
5.本地数据存储
本地数据存储HTML5提供了一个新的API:localStorage,它可以实现将数据保存到浏览器本地,并且没有时间限制,除非手动删除。
有不明白的小伙伴可以看看菜鸟教程,简单易懂;
Window localStorage 属性www.runoob.com使用本地存储的时候也需要注意几点:
(1)只要dataList发生改变就需要将数据重新存储;
(2)localStorage 只能保存字符串,如果直接setItem 一个对象,则会执行Object 的toString() 方法,保存其结果,你可能会得到“[object Object]”的结果。由于dataList的子项均为对象,因此可以借助JSON.stringify()方法将dataList转为一个字符串。同时还要注意,localStorage.getItem()得到的是一个字符串,需要借助JSON.parse()将其转为对象。
最后附上我的源码,需要的朋友可以去我的github上clone~
https://github.com/ZhipengYang0605/todo-demogithub.com