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

news2025/1/26 15:43:34

目录

一、功能需求

二、 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.value
    console.log(text)
    // 创建元素td1 td2
    var tr = document.createElement('tr')
    var td1 = document.createElement('td')
    td1.innerText = text
    var 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.value
    if(text.length!= 0)
    {
        console.log(text)
        // 创建元素td1 td2
        var tr = document.createElement('tr')
        var td1 = document.createElement('td')
        td1.innerText = text
        var 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 td2
        var tr = document.createElement('tr')
        var td1 = document.createElement('td')
        td1.innerText = text
        var 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.previousElementSibling
            target.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.previousElementSibling
            if(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--删除tr
            var target = this.parentNode.parentNode
            tbody.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--删除tr
                if(confirm("确定要删除吗?")){    //用户点击确定时就会返回true 
                    var target = this.parentNode.parentNode
                    tbody.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--》td
           var target = this.parentNode.previousElementSibling
           if(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 td2
        var tr = document.createElement('tr')
        var td1 = document.createElement('td')
        td1.setAttribute("index",flag)
        flag++
        td1.innerText = text
        var 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 td2
        var tr = document.createElement('tr')
        var td1 = document.createElement('td')
        td1.setAttribute("index",flag)
        flag++
        td1.innerText = text
        var 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.previousElementSibling
            if(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--删除tr
                if(confirm("确定要删除吗?")){    //用户点击确定时就会返回true 
                    var target = this.parentNode.parentNode
                    tbody.removeChild(target)
                }
            }else{
                alert("努力完成吧ヾ(◍°∇°◍)ノ゙")
            }
            
        }
    }


    /*
        获取修改按钮--回写:让内容重新回到输入框,进行修改
     */
    var update = document.getElementsByClassName('update')
    //循环绑事件
    for(var i=0;i<update.length;i++)
    {
        update[i].onclick = function(){
            //找到td--》td
           var target = this.parentNode.previousElementSibling
           if(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.coloradmin.cn/o/2283517.html

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

相关文章

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<1>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 这一节我们来学习指针的相关知识&#xff0c;学习内存和地址&#xff0c;指针变量和地址&#xff0c;包…

積分方程與簡單的泛函分析6.有連續對稱核的弗雷德霍姆積分算子的特徵值

1)def弗雷德霍姆算子的核函數定義 定义: 设 是定义在矩形区域 上的函数。 若满足以下条件,则称 为弗雷德霍姆算子的核函数: 实值性: 是实值函数,即对于任意的 ,。 这是因为在许多实际的物理和数学问题中,所涉及的量往往是实数值,例如在积分方程描述的物理模型中,…

AI编程工具使用技巧:在Visual Studio Code中高效利用阿里云通义灵码

AI编程工具使用技巧&#xff1a;在Visual Studio Code中高效利用阿里云通义灵码 前言一、通义灵码介绍1.1 通义灵码简介1.2 主要功能1.3 版本选择1.4 支持环境 二、Visual Studio Code介绍1.1 VS Code简介1.2 主要特点 三、安装VsCode3.1下载VsCode3.2.安装VsCode3.3 打开VsCod…

kettle与Springboot的集成方法,完整支持大数据组件

目录 概要整体架构流程技术名词解释技术细节小结 概要 在现代数据处理和ETL&#xff08;提取、转换、加载&#xff09;流程中&#xff0c;Kettle&#xff08;Pentaho Data Integration, PDI&#xff09;作为一种强大的开源ETL工具&#xff0c;被广泛应用于各种数据处理场景。…

GitHub Actions 使用需谨慎:深度剖析其痛点与替代方案

在持续集成与持续部署&#xff08;CI/CD&#xff09;领域&#xff0c;GitHub Actions 曾是众多开发者的热门选择&#xff0c;但如今&#xff0c;其弊端逐渐显现&#xff0c;让不少人在使用前不得不深思熟虑。 团队由大约 15 名工程师组成&#xff0c;采用基于主干的开发方式&am…

<iframe>标签和定时调用函数setInterval

iframe 标签和定时调用函数 setInterval 问题描述&#xff1a;解决方法&#xff1a; 问题描述&#xff1a; 今天遇到一个前端问题&#xff0c;在浏览器页面上传Excel文件后&#xff0c;然后点击导入按钮&#xff0c;经后端Java类读取文件内容校验后&#xff0c;将校验结果返回…

Qt——界面优化

一.QSS 1.背景 在网页前端开发领域中&#xff0c; CSS 是⼀个至关重要的部分。 描述了⼀个网页的 "样式"。 从而起到对网页美化的作用。 所谓样式&#xff0c;包括不限于大小&#xff0c;位置&#xff0c;颜色&#xff0c;背景&#xff0c;间距&#xff0c;字体等等…

java基础学习——jdbc基础知识详细介绍

引言 数据的存储 我们在开发 java 程序时&#xff0c;数据都是存储在内存中的&#xff0c;属于临时存储&#xff0c;当程序停止或重启时&#xff0c;内存中的数据就会丢失&#xff0c;我们为了解决数据的长期存储问题&#xff0c;有以下解决方案&#xff1a; 通过 IO流书记&…

CukeTest使用 | 1 CukeTest是什么?如何下载安装?

CukeTest使用 | 1 CukeTest是什么&#xff1f;如何下载安装&#xff1f; 1 CukeTest是什么&#xff1f;2 关于开发者3 CukeTest有哪些特性&#xff1f;4 都支持哪些自动化技术类型&#xff1f;5 版本区别6 下载安装 特殊说明&#xff1a;学习内容主要来自官网的教程、以及网上公…

An OpenGL Toolbox

3.An OpenGL Toolbox 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 3.1 Vertex Arrays and Their Drawing Commands 顶点数组及其绘制命令&#xff1a;将几何数据存储在一个位置&#xff0c…

R语言学习笔记之高效数据操作

一、概要 数据操作是R语言的一大优势&#xff0c;用户可以利用基本包或者拓展包在R语言中进行复杂的数据操作&#xff0c;包括排序、更新、分组汇总等。R数据操作包&#xff1a;data.table和tidyfst两个扩展包。 data.table是当前R中处理数据最快的工具&#xff0c;可以实现快…

Linux的权限和一些shell原理

目录 shell的原理 Linux权限 sudo命令提权 权限 文件的属性 ⽂件类型&#xff1a; 基本权限&#xff1a; chmod改权限 umask chown 该拥有者 chgrp 改所属组 最后&#xff1a; 目录权限 粘滞位 shell的原理 我们广义上的Linux系统 Linux内核Linux外壳 Linux严格…

LearnOpenGL——光照

教程地址&#xff1a;简介 - LearnOpenGL CN 前言 这篇开始光照的学习。 颜色 原文链接&#xff1a; 颜色 - LearnOpenGL CN总结&#xff1a; 重新搭建了一个简单场景&#xff0c;为后面的学习做准备。 现实世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我…

步入响应式编程篇(二)之Reactor API

步入响应式编程篇&#xff08;二&#xff09;之Reactor API 前言回顾响应式编程Reactor API的使用Stream引入依赖Reactor API的使用流源头的创建 reactor api的背压模式发布者与订阅者使用的线程查看弹珠图查看形成新流的日志 前言 对于响应式编程的基于概念&#xff0c;以及J…

利用Redis实现数据缓存

目录 1 为啥要缓存捏&#xff1f; 2 基本流程&#xff08;以查询商铺信息为例&#xff09; 3 实现数据库与缓存双写一致 3.1 内存淘汰 3.2 超时剔除&#xff08;半自动&#xff09; 3.3 主动更新&#xff08;手动&#xff09; 3.3.1 双写方案 3.3.2 读写穿透方案 3.3.…

【动态规划】--- 斐波那契数模型

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey &#x1f3e0; 第N个泰波那契数模型 &#x1f4cc; 题目解析 第N个泰波那契数 题目要求的是泰波那契数&#xff0c;并非斐波那契数。 &…

php-phar打包避坑指南2025

有很多php脚本工具都是打包成phar形式&#xff0c;使用起来就很方便&#xff0c;那么如何自己做一个呢&#xff1f;也找了很多文档&#xff0c;也遇到很多坑&#xff0c;这里就来总结一下 phar安装 现在直接装yum php-cli包就有phar文件&#xff0c;很方便 可通过phar help查看…

java提取系统应用的日志中的sql获取表之间的关系

为了获取到对应的sql数据&#xff0c;分了三步骤 第一步&#xff0c;获取日志文件&#xff0c;解析日志文件中的查询sql&#xff0c;递归解析sql&#xff0c;获取表关系集合 递归解析sql&#xff0c;获取表与表之间的关系 输出得到的对应关联关系数据 第二步&#xff0c;根据获…

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)

医疗 MLLM 框架编程实现 本医疗 MLLM 框架结合 Python 与 PyQt6 构建,旨在实现多模态医疗数据融合分析并提供可视化界面。下面从数据预处理、模型构建与训练、可视化界面开发、模型 - 界面通信与部署这几个关键部分详细介绍编程实现。 6.1 数据预处理 在医疗 MLLM 框架中,多…

IMX6ull项目环境配置

文件解压缩&#xff1a; .tar.gz 格式解压为 tar -zxvf .tar.bz2 格式解压为 tar -jxvf 2.4版本后的U-boot.bin移植进SD卡后&#xff0c;通过串口启动配置开发板和虚拟机网络。 setenv ipaddr 192.168.2.230 setenv ethaddr 00:04:9f:…