使用js实现一个任务管理列表|青训营

使用js写了一个任务管理列表,数据存储在本地,可以实现:自动计算任务完成时间、标记完成、录入、删除任务。主要是练习和熟悉一些DOM操作~而且日常记录任务也可以使用这个自己设计的网页,成就感upup!

实现效果如下图所示~

接下来就来跟我一起定制自己专属的任务列表吧~

一、代码结构

1.html部分

用html搭建一个简单的骨架,包括标题、实时时间显示模块、任务录入模块、任务清单的标题部分。

2.css部分

用css实现两个大盒子在屏幕上的布局,再设置相应的字体、边框等样式。

3.js部分

前面两个部分的代码都非常简单。js才是实现本项目的重头戏。js部分包括以下几个模块: (1)任务清单渲染模块

(2)任务完成勾选模块

(3)实时日期渲染模块

(4)表单录入模块

(5)删除操作模块

接下来跟着我完善各部分代码吧~

二、代码实现

1.html部分

分为两个大盒子:plus盒子用于新增任务,show盒子用于展示清单。

按照预想的架构,把标题和文本框,下拉表单,按钮,任务列表的骨架一个个摆上去就好了。

完成后的效果如图:

以下为源码:

xml 复制代码
<div class="plus">
        <h1>新增任务</h1>

        <p class="myDate">今天是:2023/8/10 </p>

        <form class="info" autocomplete="off">
            任务:<input type="text" class="uname" name="uname">
            完成期限:<select name="udate" class="udate">
                <option value="六小时">六小时</option>
                <option value="一天">一天</option>
                <option value="两天">两天</option>
                <option value="三天">三天</option>
                <option value="一星期">一星期</option>
            </select>
            <button class="add">录入</button>

        </form>
    </div>

    <div class="show">
        <h1>任务清单</h1>
        <table>
            <thead>
                <tr>
                    <th>全选<input type="checkbox" id="checkAll"></th>
                    <th>任务</th>
                    <th>完成期限</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <!-- <tr>
                    <td><input type="checkbox" class="ck"></td>
                    <td>看仁医</td>
                    <td>今天</td>
                    <td>删除</td>
                </tr> -->
            </tbody>
        </table>
    </div> `

2.css部分

实现思路是: 1.首先用标准流布局加和浮动布局,将plus和show两个大盒子的宽度设置好并摆放好。

根据预期效果,plus盒子容纳的内容较少,只包含时间实时显示模块,标题,以及录入模块,因此宽度设置为400px;而show盒子容纳的清单大小会根据用户录入的量增加而增加,包含的内容较多,因此宽度设置为900px,高度不予设置,但设置了底部内边距padding-bottom为40px,来保证盒子的高度会根据需要自动伸缩,并且使内边距较为美观。

布局部分的代码如下:

css 复制代码
* {
            margin: 0;
            padding: 0;
        }

        .plus {
            margin: 30px 10px;
            float: left;
            width: 400px;
            background-color: #e9f1f7;
            border: 2px dotted #a5b9d2;
        }

        .show {
            margin: 30px 10px;
            float: left;
            width: 900px;
            padding-bottom: 40px;
            background-color: #e9f1f7;
            border: 2px dotted #a5b9d2;
        }

2.用css选择器一个个选中其中的标题、表单等元素设置对应的样式即可。 这部分没什么严格要求,根据自己的喜好设置即可。注意各种基础选择器和复合选择器的配合使用哦~

实现代码如下:

css 复制代码
.plus h1 {
            text-align: center;
            color: #333;
            margin: 20px 0;
            font-size: 20px;
        }

        .show h1 {
            text-align: center;
            color: #333;
            margin: 20px 0;

        }

        table {
            margin: 0 auto;
            width: 850px;
            border-collapse: collapse;
            color: #004085;
        }

        th {
            padding: 10px;
            background: #cfe5ff;

            font-size: 20px;
            font-weight: 400;
        }

        td,
        th {
            border: 1px solid #b8daff;
        }

        td {
            padding: 10px;
            color: #666;
            text-align: center;
            font-size: 16px;
        }

        tbody tr {
            background: #fff;
        }

        tbody tr:hover {
            background: #e1ecf8;
        }

        .info {
            width: 400px;
            margin: 30px auto;
            text-align: center;
        }

        .info input,
        .info select {
            width: 80px;
            height: 27px;
            outline: none;
            border-radius: 5px;
            border: 1px solid #b8daff;
            padding-left: 5px;
            box-sizing: border-box;
            margin-right: 15px;
        }

        .info button {
            width: 60px;
            height: 27px;
            background-color: #004085;
            outline: none;
            border: 0;
            color: #fff;
            cursor: pointer;
            border-radius: 5px;
        }

        .plus p {
            text-align: center;
        }

        a {
            text-decoration: none;
            color: #004085;
            font-weight: 600;
        }

        .myDate {
            color: #004085;
        }

完成css部分后,页面效果如图,是不是变得美观了很多呢~

完成html和css代码后,我们得到了一个(比较)漂亮的空壳子。为了让这个空壳子具有实际的功能,我们还需要完成js代码~

3.js部分

重头戏来咯~让我们来一起分析各个模块的需求及实现思路。

本项目是将数据作为对象,存储在数组arr中,并及时在该数组发生变化时,更新到本地仓库localStorage中。arr数组是一个全局变量,我们在增、删、标记数据时,都是对arr数组中的数据进行处理。处理过后,根据数组中数据,实时渲染到网页中。

数组中的数据有三条属性值:uname(任务名),udate(截止时间),state(该任务是否被勾选完成?是的话为true,否则为false)。

arduino 复制代码
const obj = {
                uname: uname.value,//表单录入的任务名 
                udate: ddl,//根据udate.value计算得出ddl
                state: false//该任务是否被勾选
            }

(1)任务清单渲染模块:

什么时候需要渲染任务清单呢?当然是任务清单上的条目发生变化时咯~因此我们需要封装一个渲染函数render(),以便在页面刚加载完成时新增任务条目删除任务条目时调用。

渲染思路:

  • 1.获取本地数据,转换为对象数组,存入arr。之后的渲染都是从arr中读取对象。
  • 2.函数最开始需要清空页面以前渲染的内容,避免一条数据重复打印
  • 3.利用循环,逐条生成tr,用模版字符串填入内容,再追加到tbody中
  • 4.渲染完一条数据后,查看其state属性。如果为true,则用js将对应的字体属性修改为"划去"
  • 5.所有数据渲染完成后,判断是否每个对象的state都为true。如果是,将'全选'勾上。
  • 6.函数封装完成后要调用一次。这样每次页面重新加载后都会显示本地存储的数据了。
  • 代码如下:
ini 复制代码
         //获取元素
        const p = document.querySelector('p')
        const tbody = document.querySelector('tbody')
        const uname = document.querySelector('.uname')
        const udate = document.querySelector('.udate')
        // console.log(uname);
        // console.log(udate);
        const items = document.querySelectorAll('[name]')
        //读取本地数据
        const data = localStorage.getItem('myData')
        //存入arr数组中,如果没有本地数据则为空数组
        const arr = data ? JSON.parse(data) : []
        const info = document.querySelector('.info')

        //封装渲染函数 
        function render() {
            //清空以前的避免重复打印
            tbody.innerHTML = ''
  
            let flag = true//代表全选被勾选
            
            for (let i = 0; i < arr.length; i++) {
                //生成tr
                const tr = document.createElement('tr')
                //用模版字符串填充
                tr.innerHTML = `
                 <td><input type="checkbox" class="ck"></td>
                    <td>${arr[i].uname}</td>
                    <td>${arr[i].udate}</td>
                    <td>
            <a href="javascript:" data-id=${i}>删除</a>
          </td>
                `

                //追加到页面中
                tbody.appendChild(tr)
                //每一个删除键都设置了对应的data-id,以便后期点击时找到对应数据删除
                const a = tr.querySelector('a')
                console.log(a.dataset.id);
                
                //根据state修改勾选框的状态
                const input = tr.querySelector('input')
                input.checked = arr[i].state
                 //根据state修改字体样式
                if (arr[i].state) {
                    const td = tr.querySelector('td:nth-of-type(2)')
                    // console.log(td);
                    td.style.textDecoration = 'line-through'
                    const td2 = td.nextElementSibling
                    td2.style.textDecoration = 'line-through'
                    const td3 = td2.nextElementSibling
                    td3.style.textDecoration = 'line-through'

                }
                else {
                    flag = false//任意一个对象state=false则全选标记置为false
                }

            }
            //修改全选框
            document.querySelector('#checkAll').checked = flag



        }

        //别忘了调用
        render()

(2)任务完成勾选模块

该模块由两个函数组成:

  • 1、revise函数:在复选框发生点击事件时调用,可以根据任务清单中对应的复选框勾选情况,来修改arr[i]的state值,并更新到本地存储里。同时更改字体样式:勾选则划掉文字,未勾选则更改为不划掉。
ini 复制代码
//修改属性函数
        function revise(i, cks) {

            //更改arr的state属性
            arr[i].state = cks[i].checked ? true : false

            // console.log(arr[i]);
            //更新本地存储
            localStorage.setItem('myData', JSON.stringify(arr))
            // console.log(arr[i].state);
            //修改样式:划掉还是不划掉?
            const trs = document.querySelectorAll('tr')
            console.log(trs[1]);
            const td = trs[i + 1].querySelector('td:nth-of-type(2)')
            if (arr[i].state) {
                // console.log('我要划掉了');
                // console.log(td);

                td.style.textDecoration = 'line-through'
                const td2 = td.nextElementSibling
                td2.style.textDecoration = 'line-through'
                const td3 = td2.nextElementSibling
                td3.style.textDecoration = 'line-through'

            }
            else {
 
                td.style.textDecoration = 'none'
                const td2 = td.nextElementSibling
                td2.style.textDecoration = 'none'
                const td3 = td2.nextElementSibling
                td3.style.textDecoration = 'none'
            }

        }
  • 2.checkbox函数:用于在复选框发生点击事件时调用。如果点击了全选框,就将每个小复选框的状态更新为全选框的状态,并调用revise来更改样式和存储数据;如果点击了其他普通复选框,就调用revise来更改样式和存储数据,同时检查是否需要更新全选框勾选状态。
  • 因为checkbox函数给复选框和全选框注册了点击事件,所以也要调用一下。在全局范围内调用一次就可以把需要的点击事件都注册上了~代码如下:
ini 复制代码
function checkbox() {

            const checkAll = document.querySelector('#checkAll')

            const cks = document.querySelectorAll('.ck')
            // console.log(checkAll);
            // console.log(cks);
            checkAll.addEventListener('click', function () {
                for (let i = 0; i < cks.length; i++) {
                    cks[i].checked = this.checked
                    //修改属性
                    // console.log(cks[i].checked);
                    revise(i, cks)

                }
            })


            for (let i = 0; i < cks.length; i++) {
                cks[i].addEventListener('click', function () {
                    revise(i, cks)
                    checkAll.checked = document.querySelectorAll('.ck:checked').length === cks.length

                })
            }

        }

        checkbox()
         

(3)实时日期渲染模块

这部分调用了定时函数,来实时更新时间显示模块中的文字内容,比较简单,只要熟悉日期对象Date和定时函数setInterval就可以啦~

javascript 复制代码
function getWeek(a) {
            const arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
            return arr[a % 7]
        }

        setInterval(function () {
            const date = new Date()
            p.innerHTML = ` ${getWeek(date.getDay())}  ${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}  ${date.getHours()}:${date.getMinutes()}`
        }, 1000)

(4)表单录入模块

该模块负责在点击"录入"按钮时,将表单中的内容经过处理后存储到本地,并调用render()重新渲染任务清单。

  • 1.录入的时候会根据下拉表单选择的时间来计算ddl。这里封装了一个计算时间的函数。该函数可以传入当前的时间戳以及要经过的时间(ms),计算出截止时间。如下所示:
vbscript 复制代码
 function culTime(now, period) {
            const ddl = now + period
            const date = new Date(ddl)
            return ` ${getWeek(date.getDay())}  ${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}  ${date.getHours()}点`
        }
        // const now = +new Date()
        // console.log(culTime(now, 21600000));
  • 2.注册表单提交事件:首先要阻止提交后自动跳转的默认事件,再读取表单中的数据,处理后写入一个新对象里,再把该对象push进arr数组,再存入localStorage就可以了。
  • 注意创建对象前要验证表单内容是否为空,空的话要return。
  • 注意提交后要清空表单内容
  • 注意要调用checkbox()函数。因为增加了新的复选框,这些新的复选框也需要用checkbox()注册点击事件,不然会出bug的!
javascript 复制代码
//录入模块
        info.addEventListener('submit', function (e) {
        //阻止默认跳转
            e.preventDefault()
            cks = document.querySelectorAll('.ck')
         
            //表单验证
            for (let i = 0; i < items.length; i++) {
                if (items[i].value == '') {
                    return alert('输入内容不能为空')
                }
            }
            
            //获取当前时间戳
            const now = +new Date()
            let ddl = '' 
            
            //调用时间计算函数来计算ddl的值
            switch (udate.value) {
                case '六小时'://21600000
                    ddl = culTime(now, 21600000)
                    break

                case '一天'://86400000
                    ddl = culTime(now, 86400000)
                    break

                case '两天'://172800000
                    ddl = culTime(now, 172800000)
                    break

                case '三天'://259200000
                    ddl = culTime(now, 259200000)
                    break

                case '一星期':// 604800000
                    ddl = culTime(now, 604800000)
                    break

            }
            console.log(ddl);


            //存入arr和本地
             const obj = {
                uname: uname.value,
                udate: ddl,
                state: false
            }
            //console.log(obj);
            arr.push(obj)
            localStorage.setItem('myData', JSON.stringify(arr))
            //console.log(JSON.parse(localStorage.getItem('myData')));
            //清空表单中内容
            this.reset()
            //渲染
            render()
            //重新注册一下事件
            checkbox()

        })

(5)删除操作模块

采取事件委托,给删除的'父亲'tbody注册点击事件。通过data-id属性能拿到相应的数据在arr中的下标号,将该条数据删除并更新到本地仓库即可。

  • 其中也要调用checkbox()函数,因为删除时复选框的序列号发生变化,需要更新状态。
scss 复制代码
//删除操作和勾选操作
        tbody.addEventListener('click', function (e) {
            console.log(e.target);
            if (e.target.tagName == 'A') {

                arr.splice(e.target.dataset.id, 1)
                localStorage.setItem('myData', JSON.stringify(arr))
                render()
                checkbox()
            }

        })

三、总结

综上,将html,css,js代码放入他们该在的位置,我们的代码就全部完成了~获得了一个可以在日常生活中使用的任务列表,且数据存储在本地不会丢失(除非你手动删了)。笔者是个前端小白,独立完成此项目后感觉对代码的理解能力上升了,也注意到了一些以前没发现的点:

  1. 项目中要实时关注全局变量的状态,例如arr和document.ququerySelectorAll拿到的对象。假如我一开始将所有复选框存入了ck变量中,但是当数据新增或删除后,复选框就发生了改变,需要更新ck!不然就会出现奇怪的bug半天无法解决.....
  2. 本项目代码量差不多四百行,写的时候要思路清晰,先分析需求,写好大纲!不要写一布看一步,不然效率会很低下.....
  3. 代码永远都有可以简化的空间,继续学习更多的知识,回头再来看今天的任务,一定可以写出更高效简洁的代码的!
相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记