前端【8】HTML+CSS+javascript实战项目----实现一个简单的待办事项列表 (To-Do List)

目录

一、功能需求

二、 HTML

三、CSS

四、js

1、绑定事件与初始设置

2.、绑定事项 

(1)添加操作:

(2)完成操作

(3)删除操作

(4)修改操作

3、完整js代码

总结与感悟


        实现了一个包括 添加、完成、删除、修改 等操作的 To-Do List,涵盖了常见的 DOM 操作及事件绑定。目的为了掌握 JavaScript 操作 DOM 的技巧,项目还可用于实际开发中的简单任务管理工具。

一、功能需求

  1. 添加待办事项

    • 用户输入内容后,点击 "添加" 按钮,将事项添加到列表中。
    • 如果输入为空,提示用户输入内容。
  2. 标记完成

    • 点击 "完成" 按钮,可以标记当前事项为完成或取消完成。
  3. 删除事项

    • 仅当事项已标记完成时才能删除。
  4. 修改事项

    • 点击 "修改" 按钮,将事项内容回填到输入框,用户可以修改内容并保存。

二、 HTML

HTML 文件定义了一个简单的页面布局,包括输入框、按钮和一个表格来显示待办事项。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>待办事项列表</title><link rel="stylesheet" href="css/todolist.css"><script src="js/todolist.js" defer></script>
</head><body><div class="container"><div class="top"><input type="text" class="content"><input type="button" value="添加" class="btn"></div><table border="1"><thead><tr><th>内容</th><th>操作</th></tr></thead><tbody><!-- 动态生成内容 --></tbody></table></div>
</body></html>

三、CSS

通过简单的 CSS,提升待办事项列表的外观效果。

.container {width: 600px;margin: 0 auto;text-align: center;
}.top {margin-bottom: 20px;
}.content {width: 400px;padding: 5px;
}.btn {padding: 5px 10px;background-color: #007BFF;color: #fff;border: none;cursor: pointer;
}.btn:hover {background-color: #0056b3;
}table {width: 100%;border-collapse: collapse;
}td {padding: 10px;text-align: center;
}

最终: 

四、js

1、绑定事件与初始设置

首先获取页面中的按钮、输入框和表格的 tbody 元素

// 获取添加按钮
var btn = document.querySelector('.btn')
// 获取输入框
var content = document.querySelector('.content')
// 获取tbody
var tbody = document.querySelector('tbody')

2.、绑定事项 

(1)添加操作:

用户输入内容后,点击 "添加" 按钮,内容将被添加到表格中。

第一版:

// 给添加按钮绑事件
btn.onclick = function() {var text = content.valueconsole.log(text)// 创建元素td1 td2var tr = document.createElement('tr')var td1 = document.createElement('td')td1.innerText = textvar td2 = document.createElement('td')td2.innerHTML = '<input type="button" value="完成" class="finish"><input type="button" value="删除" class="delete"><input type="button" value="修改" class="update"></input>'tr.append(td1)tr.append(td2)tbody.append(tr)}

 第二版:校验输入值,如果输入框为空,仍会生成一个空白行,不是预期的行为。添加条件判断语句

// 给添加按钮绑事件
btn.onclick = function() {var text = content.valueif(text.length!= 0){console.log(text)// 创建元素td1 td2var tr = document.createElement('tr')var td1 = document.createElement('td')td1.innerText = textvar td2 = document.createElement('td')td2.innerHTML = '<input type="button" value="完成" class="finish"><input type="button" value="删除" class="delete"><input type="button" value="修改" class="update"></input>'tr.append(td1)tr.append(td2)tbody.append(tr)// console.log('td1')// console.log('td2')// console.log('tr')}else{alert("请输入信息!!!")}
}

                    

第三版:但是!!这样你输入空格的时候也会产生空白行,于是添加

解决方法:var text = content.value.trim(); // 去掉输入值的前后空格

        并且优化每次输入完成自动去掉输入框中的值:在最后 补上content.value=''

        

/*添加按钮绑事件*/var text = content.value.trim(); // 去掉输入值的前后空格if(text.length!= 0){console.log(text)// 创建元素td1 td2var tr = document.createElement('tr')var td1 = document.createElement('td')td1.innerText = textvar td2 = document.createElement('td')td2.innerHTML = '<input type="button" value="完成" class="finish"><input type="button" value="删除" class="delete"><input type="button" value="修改" class="update"></input>'tr.append(td1)tr.append(td2)tbody.append(tr)content.value=''// console.log('td1')// console.log('td2')// console.log('tr')}else{alert("请输入信息!!!")}

                        

(2)完成操作

点击 "完成" 按钮,可以标记事项完成或取消完成。 

第一版:

   /*给完成按钮绑事件,这里注意要写到添加按钮里面,可以实时获取最新的事件*/var finish = document.getElementsByClassName('finish')//循环给每一个按钮绑事件for(var i = 0;i<finish.length;i++){finish[i].onclick = function() { //触发事件时才执行var target = this.parentNode.previousElementSiblingtarget.style.textDecoration = 'line-through'}}

第二版:完成后只是简单的给内容添加了划线,在这里优化添加恢复操作【主要是判断语句来实现】和一些其他样式

/*给完成按钮绑事件,这里注意要写到添加按钮里面,可以实时获取最新的事件,同理其他按钮也是这样*/var finish = document.getElementsByClassName('finish')//循环给每一个按钮绑事件for(var i = 0;i<finish.length;i++){finish[i].onclick = function() { //触发事件时才执行var target = this.parentNode.previousElementSiblingif(target.style.textDecoration == 'line-through'){target.style.textDecoration = 'none'target.style.color= '#000'this.value = "完成"this.style.borderColor = '#910000'this.style.color='#910000'}else{target.style.textDecoration = 'line-through'target.style.color= '#888'this.value = "恢复"this.style.borderColor = '#888'this.style.color='#888'}}}

 

(3)删除操作

 第一版:基本的删除操作【关键代码】

    /*获取删除按钮 */var deleteBtn = document.getElementsByClassName('delete')//循环绑事件for(var i=0;i<deleteBtn.length;i++){deleteBtn[i].onclick.function () {// 删除整行,找到tbody--删除trvar target = this.parentNode.parentNodetbody.removeChild(target)}}

第二版:修改细节,必须完成了才能删除【要结合完成操作】

/*获取删除按钮*/var deleteBtn = document.getElementsByClassName('delete')//循环绑事件for(var i = 0;i < deleteBtn.length;i++){deleteBtn[i].onclick =function(){if(this.parentNode.previousElementSibling.style.textDecoration=="line-through"){// 删除整行,找到tbody--删除trif(confirm("确定要删除吗?")){    //用户点击确定时就会返回true var target = this.parentNode.parentNodetbody.removeChild(target)}}else{alert("努力完成吧ヾ(◍°∇°◍)ノ゙")}}}

(4)修改操作

点击 "修改" 按钮,将事项内容回填到输入框,用户可以编辑后保存。

        这里稍微复杂一点,需要注意对添加操作的重写,全局变量 flag 、targetFlag的使用,从而获得需要修改的是存储的是哪个信息

    /*获取修改按钮--回写:让内容重新回到输入框,进行修改*/var update = document.getElementsByClassName('update')//循环绑事件for(var i=0;i<update.length;i++){update[i].onclick = function(){//找到td--》tdvar target = this.parentNode.previousElementSiblingif(target.style.textDecoration=="line-through"){//事项已经完成无需修改alert("已经完成啦无需修改~~")btn.value="添加" //这里需要在最外层循环进行判断,否则会是无脑的执行添加操作}else{content.value = target.innerText    btn.value="修改"targetFlag = target.getAttribute("index")}}}

添加全局变量来获取要修改哪条信息

//定义标识
var flag = 1
//存储修改的是哪条信息
var targetFlag = 0

重新添加操作:加上 td1.setAttribute("index",flag)   flag++来存储信息索引。

/*添加按钮绑事件*/var text = content.value.trim(); // 去掉输入值的前后空格if(text.length!= 0){console.log(text)// 创建元素td1 td2var tr = document.createElement('tr')var td1 = document.createElement('td')td1.setAttribute("index",flag)flag++td1.innerText = textvar td2 = document.createElement('td')td2.innerHTML = '<input type="button" value="完成" class="finish"><input type="button" value="删除" class="delete"><input type="button" value="修改" class="update"></input>'tr.append(td1)tr.append(td2)tbody.append(tr)content.value=''// console.log('td1')// console.log('td2')// console.log('tr')}else{alert("请输入信息!!!")}

 最后在添加操作之前进行判断【return关键字的使用,判断是修改还是添加】,并完成修改的操作

if(btn.value =="修改"){ //获取所有内容 tbody--》tr-->td里面的第一个td(存储内容的,用到伪类选择器)var tds = document.querySelectorAll('tbody tr td:nth-child(1)')for(var i=0;i<tds.length;i++){if(tds[i].getAttribute('index') == targetFlag){ //与循环到的索引值相等 就修改tds[i].innerText = content.value //--->修改的关键语句//修改完成的善后处理content.value=''btn.value="添加"}}return  }

 3

3、完整js代码

// 获取添加按钮
var btn = document.querySelector('.btn')
// 获取输入框
var content = document.querySelector('.content')
// 获取tbody
var tbody = document.querySelector('tbody')
//定义标识
var flag = 1
//存储修改的是哪条信息
var targetFlag = 0/*给添加按钮绑事件*/
btn.onclick = function() {if(btn.value =="修改"){ //获取所有内容 tbody--》tr-->td里面的第一个td(存储内容的,用到伪类选择器)var tds = document.querySelectorAll('tbody tr td:nth-child(1)')for(var i=0;i<tds.length;i++){if(tds[i].getAttribute('index') == targetFlag){ //与循环到的索引值相等 就修改tds[i].innerText = content.value //--->修改的关键语句//修改完成的善后处理content.value=''btn.value="添加"}}return  }/*添加按钮绑事件*/var text = content.value.trim(); // 去掉输入值的前后空格if(text.length!= 0){console.log(text)// 创建元素td1 td2var tr = document.createElement('tr')var td1 = document.createElement('td')td1.setAttribute("index",flag)flag++td1.innerText = textvar td2 = document.createElement('td')td2.innerHTML = '<input type="button" value="完成" class="finish"><input type="button" value="删除" class="delete"><input type="button" value="修改" class="update"></input>'tr.append(td1)tr.append(td2)tbody.append(tr)content.value=''// console.log('td1')// console.log('td2')// console.log('tr')}else{alert("请输入信息!!!")}/*给完成按钮绑事件,这里注意要写到添加按钮里面,可以实时获取最新的事件,同理其他按钮也是这样*/var finish = document.getElementsByClassName('finish')//循环给每一个按钮绑事件for(var i = 0;i<finish.length;i++){finish[i].onclick = function() { //触发事件时才执行var target = this.parentNode.previousElementSiblingif(target.style.textDecoration == 'line-through'){target.style.textDecoration = 'none'target.style.color= '#000'this.value = "完成"this.style.borderColor = '#910000'this.style.color='#910000'}else{target.style.textDecoration = 'line-through'target.style.color= '#888'this.value = "恢复"this.style.borderColor = '#888'this.style.color='#888'}}}/*获取删除按钮*/var deleteBtn = document.getElementsByClassName('delete')//循环绑事件for(var i = 0;i < deleteBtn.length;i++){deleteBtn[i].onclick =function(){if(this.parentNode.previousElementSibling.style.textDecoration=="line-through"){// 删除整行,找到tbody--删除trif(confirm("确定要删除吗?")){    //用户点击确定时就会返回true var target = this.parentNode.parentNodetbody.removeChild(target)}}else{alert("努力完成吧ヾ(◍°∇°◍)ノ゙")}}}/*获取修改按钮--回写:让内容重新回到输入框,进行修改*/var update = document.getElementsByClassName('update')//循环绑事件for(var i=0;i<update.length;i++){update[i].onclick = function(){//找到td--》tdvar target = this.parentNode.previousElementSiblingif(target.style.textDecoration=="line-through"){//事项已经完成无需修改alert("已经完成啦无需修改~~")btn.value="添加" //这里需要在最外层循环进行判断,否则会是无脑的执行添加操作}else{content.value = target.innerText    btn.value="修改"targetFlag = target.getAttribute("index")}}}}
console.log(btn) 

总结与感悟

  js语法很简单,编写代码的时候最主要的还是思路。需要注意的和之前没有见过知识点如下:

1、完成、删除、修改的绑定操作都是基于添加操作的,因此他们都是在btn.onclick = function()下的,这样可以实时获取最新的事件并进行操作的绑定。

2、outline: none —— 去除外轮廓,在本demo中用于表单元素的美化,按钮的点击的时候单元格不会有轮廓的变化

3、min-width —— 最小宽度:在本demo中用于确保布局中某些元素在内容不足时也能保持稳定的宽度。比如当我们缩小windows时候表格会保持min_width不会被压缩。

4、border-radius —— 设置圆角。之前没有详细了解过,用于设置元素的边框圆角,可实现按钮、图片等元素的圆角或圆形效果。可以分别指定 左上角右上角右下角左下角 的圆角半径

5、border-collapse —— 表格单元格边框合并:指定是否将表格的单元格边框合并为一个。

  • separate:单元格的边框独立存在(默认值)。
  • collapse:单元格的边框合并为一个。

6、cursor —— 鼠标样式:设置鼠标指针悬停在元素上的样式。

  • pointer:小手样式(常用于超链接或按钮)。
  • default:默认箭头样式。
  • text:文本光标(用于文本选择)。

7、trim() 方法 —— 去掉字符串前后空格,demo中用于优化添加操作输入空格时的处理

8、confirm 弹窗 ——弹出一个确认框,提供“确定”和“取消”两个按钮,并返回布尔值:

  • 点击“确定”返回 true
  • 点击“取消”返回 false

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/67327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue事件总线(原理、优缺点)

目录 一、原理二、使用方法三、优缺点优点缺点 四、使用注意事项具体代码参考&#xff1a; 一、原理 在Vue中&#xff0c;事件总线&#xff08;Event Bus&#xff09;是一种可实现任意组件间通信的通信方式。 要实现这个功能必须满足两点要求&#xff1a; &#xff08;1&#…

图像处理之HSV颜色空间

目录 1 RGB 的局限性 2 HSV 颜色空间 3 RGB与HSV相互转换 4 HSV颜色模型对图像的色相、饱和度和明度进行调节 5 演示Demo 5.1 开发环境 5.2 功能介绍 5.3 下载地址 参考 1 RGB 的局限性 RGB 是我们接触最多的颜色空间&#xff0c;由三个通道表示一幅图像&#xff0c;分…

【C++高并发服务器WebServer】-9:多线程开发

本文目录 一、线程概述1.1 线程和进程的区别1.2 线程之间共享和非共享资源1.3 NPTL 二、线程操作2.1 pthread_create2.2 pthread_exit2.3 pthread_join2.4 pthread_detach2.5 patch_cancel2.6 pthread_attr 三、实战demo四、线程同步五、死锁六、读写锁七、生产消费者模型 一、…

14-6-1C++STL的list

(一&#xff09;list容器的基本概念 list容器简介&#xff1a; 1.list是一个双向链表容器&#xff0c;可高效地进行插入删除元素 2.list不可以随机存取元素&#xff0c;所以不支持at.(pos)函数与[ ]操作符 &#xff08;二&#xff09;list容器头部和尾部的操作 list对象的默…

21.Word:小赵-毕业论文排版❗【39】

目录 题目​ NO1.2 NO3.4 NO5.6 NO7.8.9 NO10.11.12 题目 NO1.2 自己的论文当中接收老师的修改&#xff1a;审阅→比较→源文档&#xff1a;考生文件夹&#xff1a;Word.docx→修订的文档&#xff1a;考生文件夹&#xff1a;教师修改→确定→接收→接收所有修订将合并之…

深度学习 DAY3:NLP发展史及早期的前馈神经网络(ANN)及多任务学习

NLP发展史 NLP发展脉络简要梳理如下&#xff1a; 2001 - Neural language models&#xff08;神经语言模型&#xff09; 2008 - Multi-task learning&#xff08;多任务学习&#xff09; 2013 - Word embeddings&#xff08;词嵌入&#xff09; 2013 - Neural networks for NL…

全面了解 Web3 AIGC 和 AI Agent 的创新先锋 MelodAI

不管是在传统领域还是 Crypto&#xff0c;AI 都是公认的最有前景的赛道。随着数字内容需求的爆炸式增长和技术的快速迭代&#xff0c;Web3 AIGC&#xff08;AI生成内容&#xff09;和 AI Agent&#xff08;人工智能代理&#xff09;正成为两大关键赛道。 AIGC 通过 AI 技术生成…

54.数字翻译成字符串的可能性|Marscode AI刷题

1.题目 问题描述 小M获得了一个任务&#xff0c;需要将数字翻译成字符串。翻译规则是&#xff1a;0对应"a"&#xff0c;1对应"b"&#xff0c;依此类推直到25对应"z"。一个数字可能有多种翻译方法。小M需要一个程序来计算一个数字有多少种不同的…

RabbitMQ5-死信队列

目录 死信的概念 死信的来源 死信实战 死信之TTl 死信之最大长度 死信之消息被拒 死信的概念 死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;一般来说&#xff0c;producer 将消息投递到 broker 或直接到queue 里了&#xff0c;consumer 从 queue 取出消息进…

10JavaWeb——SpringBootWeb案例01

前面我们已经讲解了Web前端开发的基础知识&#xff0c;也讲解了Web后端开发的基础(HTTP协议、请求响应)&#xff0c;并且也讲解了数据库MySQL&#xff0c;以及通过Mybatis框架如何来完成数据库的基本操作。 那接下来&#xff0c;我们就通过一个案例&#xff0c;来将前端开发、后…

JAVA 接口、抽象类的关系和用处 详细解析

接口 - Java教程 - 廖雪峰的官方网站 一个 抽象类 如果实现了一个接口&#xff0c;可以只选择实现接口中的 部分方法&#xff08;所有的方法都要有&#xff0c;可以一部分已经写具体&#xff0c;另一部分继续保留抽象&#xff09;&#xff0c;原因在于&#xff1a; 抽象类本身…

ResNeSt: Split-Attention Networks论文学习笔记

这张图展示了一个名为“Split-Attention”的神经网络结构&#xff0c;该结构在一个基数组&#xff08;cardinal group&#xff09;内进行操作。基数组通常指的是在神经网络中处理的一组特征或通道。图中展示了如何通过一系列操作来实现对输入特征的注意力机制。 以下是图中各部…

设计模式Python版 原型模式

文章目录 前言一、原型模式二、原型模式示例三、原型管理器 前言 GOF设计模式分三大类&#xff1a; 创建型模式&#xff1a;关注对象的创建过程&#xff0c;包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式&#xff1a;关注类和对…

神经网络的通俗介绍

人工神经网络&#xff0c;是一种模仿人类大脑工作原理的数学模型。人类的大脑是由无数的小“工作站”组成的&#xff0c;每个工作站叫做“神经元”。这些神经元通过“电线”互相连接&#xff0c;负责接收、处理和传递信息。 一、人类大脑神经网络 人类大脑的神经网络大概长这…

OpenEuler学习笔记(八):安装OpenEuler

在VMware Workstation中安装OpenEuler 准备工作 下载并安装VMware Workstation虚拟机软件。前往OpenEuler官网下载OpenEuler系统镜像文件。 创建虚拟机 打开VMware Workstation&#xff0c;点击“创建新的虚拟机”&#xff0c;选择“自定义”&#xff0c;点击“下一步”。选择…

Leetcode::119. 杨辉三角 II

119. 杨辉三角 II 已解答 简单 相关标签 相关企业 给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowIndex 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: rowIndex 3 输出: [1,3,3,1]示例 2: 输入: rowIndex 0…

让Android adb支持互联网调试脱离局域网

某些特殊场景下由于不方便&#xff0c;手机不在身边&#xff0c;但需要进行adb调试。 首先可以先开启adb的无线调试模式&#xff0c;我使用的是第二种方式。 在Android手机上安装一个终端模拟器&#xff0c;并赋予root权限&#xff0c;随后执行&#xff1a; setprop service.…

Dest1ny漏洞库:用友 U8-CRM 系统 ajaxgetborrowdata.php 存在 SQL 注入漏洞

用友U8-CRM系统ajaxgetborrowdata.php存在SQL注入漏洞&#xff0c;文件多个方法存在SQL注入漏洞&#xff0c;未经身份验证的攻击者通过漏洞执行任意SQL语句&#xff0c;调用xp_cmdshell写入后门文件&#xff0c;执行任意代码&#xff0c;从而获取到服务器权限。 hunter app.n…

能说说MyBatis的工作原理吗?

大家好&#xff0c;我是锋哥。今天分享关于【Redis为什么这么快?】面试题。希望对大家有帮助&#xff1b; 能说说MyBatis的工作原理吗&#xff1f; MyBatis 是一款流行的持久层框架&#xff0c;它通过简化数据库操作&#xff0c;帮助开发者更高效地与数据库进行交互。MyBatis…

DeepSeek崛起:中国AI新星如何撼动全球资本市场格局

引言 近期&#xff0c;中国人工智能实验室DeepSeek发布的两款开源模型——DeepSeek V3和DeepSeek R1——以其优异的性能和低廉的成本迅速爆火&#xff0c;引发了全球资本市场的震动&#xff0c;尤其对美国资本市场产生了显著影响。DeepSeek R1更是能够在数学、代码和推理任务上…