可以用js代码添加li的内容吗_原生JS写一个ToDo Demo

d7f65013be9cfca79790513807306d04.png

周六加班没什么事,于是乎。。。上班摸鱼,用原生JS写了一个ToDo Demo,废话少叙,直接看效果:

a4a947ee1ceaed884d34076bcc7ce94b.gif

如图,实现了以下需求(以下的具体实现我会一一道来):

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.io

demo 里面我就简单的写了这几行代码~封装成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
fda276e81785ef20708aa26873fd36e2.png

使用本地存储的时候也需要注意几点:

(1)只要dataList发生改变就需要将数据重新存储;

(2)localStorage 只能保存字符串,如果直接setItem 一个对象,则会执行Object 的toString() 方法,保存其结果,你可能会得到“[object Object]”的结果。由于dataList的子项均为对象,因此可以借助JSON.stringify()方法将dataList转为一个字符串。同时还要注意,localStorage.getItem()得到的是一个字符串,需要借助JSON.parse()将其转为对象。

最后附上我的源码,需要的朋友可以去我的github上clone~

https://github.com/ZhipengYang0605/todo-demo​github.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值