目录
实现了一个包括 添加、完成、删除、修改 等操作的 To-Do List,涵盖了常见的 DOM 操作及事件绑定。目的为了掌握 JavaScript 操作 DOM 的技巧,项目还可用于实际开发中的简单任务管理工具。
一、功能需求
-
添加待办事项
- 用户输入内容后,点击 "添加" 按钮,将事项添加到列表中。
- 如果输入为空,提示用户输入内容。
-
标记完成
- 点击 "完成" 按钮,可以标记当前事项为完成或取消完成。
-
删除事项
- 仅当事项已标记完成时才能删除。
-
修改事项
- 点击 "修改" 按钮,将事项内容回填到输入框,用户可以修改内容并保存。
二、 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
。