揭秘 To-Do List:一个前端小玩具的"幕后玩家"
嘿,各位掘金的大佬们,前端小白们,以及那些在代码的海洋里摸爬滚打的"程序猿"们!今天,咱们不聊高深莫测的框架,不谈性能优化到极致的骚操作,咱们来点轻松愉快的------一个看似简单,实则蕴含着前端"朴实无华"智慧的To-Do List小应用!
你可能会说:"To-Do List?这玩意儿我闭着眼睛都能写出来!" 没错,它确实是前端入门的"Hello World"级别应用。但正是这些看似简单的东西,才能让我们更好地理解前端的本质,体会到代码的乐趣。今天,就让我带着大家,一起扒一扒这个To-Do List的"底裤",看看它到底藏着哪些不为人知的秘密(其实就是HTML、CSS和JavaScript的那些事儿)。

第一章:HTML------骨架的艺术,你值得拥有!
首先,我们来看看这个To-Do List的"骨架"------HTML。它就像是盖房子前的地基和钢筋,虽然不 flashy,但却是整个应用的基石。没有它,再炫酷的CSS和JavaScript都无从谈起。
xml
<!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="./style.css">
</head>
<body>
<div class="container">
<div class="heading">
<h1 class="heading_title">To-D0 List</h1>
</div>
<form class="form">
<div>
<label for="todo" class="form_lable">~ Today I Want To Do ~</label>
<input
type="text"
class="form_input"
id="todo"
size="30"
required
>
<button class="botton">
<span>Submit</span>
</button>
</div>
</form>
<div class="todo-list"></div>
</div>
<script src="./index.js"></script>
</body>
</html>
从这段代码中,我们可以看到几个关键的"零部件":
<head>
里的"内务管理" :charset
、viewport
这些都是老生常谈了,确保你的页面在各种设备上都能"长得周正"。title
嘛,就是浏览器标签页上那几个字,别小看它,它可是你应用的"门面"!link
标签:CSS的"红娘" :link rel="stylesheet" href="./style.css"
,这行代码就像是给HTML找了个"对象"------CSS。没有它,你的页面可能就只是一堆光秃秃的文字,毫无美感可言。body
里的"肉体" :div.container
是整个应用的"大容器",所有的内容都装在里面。h1.heading_title
就是那个大大的"To-Do List"标题,一看就知道这是个干啥的。form
表单:用户的"许愿池" :form
标签是用户输入待办事项的地方。label
是输入框的"说明书",input
就是那个让你输入内容的神奇盒子,button
嘛,就是你"提交愿望"的按钮。div.todo-list
:待办事项的"展示柜" :这个div
一开始是空的,但当用户输入待办事项并提交后,JavaScript就会把这些事项"摆"到这里来。script
标签:JavaScript的"引路人" :script src="./index.js"
,这行代码是整个应用"灵魂"的入口。没有它,你的To-Do List就只是个静态页面,啥也干不了。
总结一下,HTML就是我们应用的"骨架",它定义了页面的结构和内容。虽然它看起来很"朴素",但却是不可或缺的基石。就像一个人的骨架,没有它,再好看的皮囊也撑不起来!
第二章:CSS------给骨架穿上"皇帝的新衣"
有了骨架,我们还得给它穿上"衣服"啊!不然光秃秃的,多不好意思见人。这时候,CSS就闪亮登场了!它负责给我们的To-Do List"化妆",让它变得美观大方,甚至有点"小清新"。
css
@import url(https://fonts.googleapis.com/css?family=Gochi+Hand);
body{
background-color: #def0d4;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Gochi Hand', cursive;
color: #494a4b;
text-align: center;
font-size: 130%;
}
.container{
width: 100%;
min-width: 250px;
max-width: 500px;
min-height: 500px;
background-color: #f1f5f8;
box-shadow: 4px 2px 7px 2px #000;
border-radius: 20px;
padding: 1rem;
box-sizing: border-box;
background-image: radial-gradient(#bfc0c1 7.2%, transparent 0);
background-size: 25px 25px;
}
.heading_title{
padding: 0.2rem 1.2rem;
background-color: lightpink;
border-radius: 20% 5% 20% 5%/5% 20% 25% 20%;
transform: rotate(3deg);
display: inline-block;
}
.form_input{
box-sizing: border-box;
background-color: transparent;
padding: 0.7rem;
border: 3px solid transparent;
border-bottom-right-radius: 15px 3px;
border-bottom-left-radius: 3px 15px;
border-bottom: dashed 3px #95a9ea;
font-family: 'Gochi Hand', cursive;
font-size: 1rem;
color: rgba(63, 62, 65, 0.7);
width: 70%;
margin-bottom: 20px;
}
.form_input:focus{
outline: none;
border: 3px solid #95a9ea;
}
.botton{
padding: 0;
border: none;
font-family: 'Gochi Hand', cursive;
padding-bottom: 3px;
background-image: url('data:image/gif;base64,R0lGODlhBAAEAIABAAAAAAAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSJvu78iIGlkPSJXNU0wTXBDZWhpSHpydVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjEgNjQuMTQwOTQ5LCAyMDEwLzEyLzA3LTEwOjU3OjAxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlL2NvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgV2luZG93cyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5NUY1OENCRDdDMDYxMUUyOTEzMEE1MEM5QzM0NDVBMyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo5NUY1OENCRTdDMDYxMUUyOTEzMEE1MEM5QzM0NDVBMyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk1RjU4Q0JCN0MwNjExRTI5MTMwQTUwQzlDMzQ0NUEzIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjk1RjU4Q0JDN0MwNjExRTI5MTMwQTUwQzlDMzQ0NUEzIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAAQABAAAAgYEEpdoeQUAOw==');
border-radius: 5px;
box-shadow: 0 2px 0 #494a4b;
background-color: rgba(0, 119, 255, 0.7);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transform: rotate(4deg);
}
.botton span{
background-color: #f1f5f8;
display: block;
padding: 0.5rem 1rem;
border-radius: 5px;
border: 2px solid #494a4b;
}
.botton:active, .botton:focus{
padding-bottom: 0;
outline: 0;
transform: rotate(0deg);
}
.todo-list{
text-align: left;
}
.todo-list li{
padding: 0.5rem;
}
.todo-list li:hover{
text-decoration: line-through wavy yellow;
}
CSS就像是给你的应用穿上了一件"皇帝的新衣",让它看起来光彩照人。这里面有很多有趣的细节:
body
的"整体造型" :background-color
、min-height
、display: flex
这些属性,让整个页面看起来居中、舒适,并且背景色也挺"小清新"的。font-family
引入了Gochi Hand
字体,让整个应用看起来手写感十足,充满了"便签"的韵味。.container
的"盒子模型" :width
、max-width
、min-height
、padding
、box-sizing
这些属性,定义了整个To-Do List的"身材"和"内边距"。box-shadow
给它加了个"阴影",看起来更有立体感。最有趣的是background-image: radial-gradient
,它给背景加了一个"点阵"效果,模拟了便签纸的纹理,细节控狂喜!.heading_title
的"标题特效" :background-color: lightpink
让标题有了个可爱的背景色。border-radius
和transform: rotate(3deg)
让标题看起来像是手写上去的,并且有点倾斜,活泼有趣。.form_input
的"输入框美化" :border-bottom: dashed 3px #95a9ea
给输入框加了一个虚线边框,看起来像是在纸上写字一样。focus
时的边框变化,也增加了用户体验。.botton
的"按钮动画" :这个按钮的CSS是整个应用里最"骚"的地方!background-image
用了一个base64编码的GIF图片作为背景,这操作简直是"神来之笔"!transition
和transform
的组合,让按钮在点击时有一个"下沉"和"旋转"的动画效果,非常生动。active
和focus
伪类也让按钮的交互更加流畅。.todo-list li
的"删除线特效" :text-decoration: line-through wavy yellow
,当鼠标悬停在待办事项上时,会出现一个波浪形的黄色删除线,这简直是"艺术品"!它不仅提示用户可以删除,还让删除操作变得有趣起来。
CSS就像是给你的应用穿上了一件"皇帝的新衣",让它看起来光彩照人。它不仅能让页面好看,还能通过各种小细节,提升用户体验,让你的应用"活"起来!
第三章:JavaScript------灵魂的舞者,让应用"动"起来!
有了骨架和衣服,我们的To-Do List还差一个"灵魂"------JavaScript。它负责让我们的应用"动"起来,实现各种交互功能,比如添加待办事项、删除待办事项等等。没有JavaScript,你的To-Do List就只是个"花瓶",中看不中用。
javascript
// 当用户点击提交按钮时,执行以下操作
// 1. 获取用户输入内容
// 2. 将内容添加到 ul 列表中
// 监听提交按钮点击事件
// 获取 form 表单元素
const form = document.querySelector(".form");
const input = document.querySelector(".form_input");
const ul = document.querySelector(".todo-list");
const toDoListArray = []; // 存储待办事项的数组
form.addEventListener("submit", function (e) { // 用户点击提交
e.preventDefault(); // 阻止表单默认提交行为
let itemId = String(Date.now()); // 生成一个唯一的 id
let toDoItem = input.value; // 获取用户输入的内容
// 有一个函数,可以帮我把内容调价进数组
// 再有一个函数,将数组进行渲染
addItemToArray(itemId, toDoItem);
addItemToDom(itemId, toDoItem);
})
function addItemToArray (id, item) {
toDoListArray.push({
itemId: id,
todoItem: item
})
}
function addItemToDom (id, item) {
const li = document.createElement("li");
li.textContent = item;
li.setAttribute("data-id", id);
ul.appendChild(li);
}
// 移除待办事项
ul.addEventListener("click", function(e) {
console.log(e.target.getAttribute("data-id"));
// 获取到被点击的 li 元素,并且读取它的 data-id 属性
// 根据这个 id 从数组中删除对应的元素
// 从页面中删除对应的 li 元素
const itemId = e.target.getAttribute("data-id");
removeItemFromArray(itemId);
removeItemFromDom(itemId);
})
function removeItemFromArray (id) {
for (let i = 0; i < toDoListArray.length; i++) {
if (toDoListArray[i].itemId === id) {
toDoListArray.splice(i, 1);
break;
}
}
}
function removeItemFromDom (id) {
// 根据 data-id 属性找到对应的 li 元素
const itemToRemove = document.querySelector(`li[data-id="${id}"]`);
// 如果找到了元素,就从DOM中删除它
if (itemToRemove) {
itemToRemove.remove();
}
}
JavaScript是整个应用的"大脑",它负责处理用户的交互,并动态地更新页面内容。我们来逐一分析一下它的"骚操作":
-
DOM元素的"捕获" :
document.querySelector
就像是JavaScript的"千里眼",能够精准地找到页面上的form
、input
和ul
元素,为后续的操作做好准备。 -
toDoListArray
:待办事项的"记忆库" :这是一个空数组,用来存储所有的待办事项。虽然这个简单的To-Do List没有做持久化存储(比如LocalStorage),但这个数组是实现添加和删除功能的核心。 -
form.addEventListener('submit', ...)
:提交事件的"监听者" :当用户点击提交按钮时,这个事件监听器就会被触发。e.preventDefault()
是关键,它阻止了表单的默认提交行为(刷新页面),这样我们才能通过JavaScript来控制页面的更新。 -
itemId = String(Date.now())
:生成唯一的"身份证号" :Date.now()
会返回当前时间戳,把它转换成字符串作为每个待办事项的唯一ID,简单粗暴又有效! -
addItemToArray
和addItemToDom
:添加事项的"双保险" :addItemToArray
负责把新的待办事项添加到toDoListArray
数组中,这是数据层面的操作。addItemToDom
则负责把新的待办事项动态地添加到页面的ul
列表中,这是UI层面的操作。它通过document.createElement('li')
创建新的li
元素,设置textContent
,并用setAttribute('data-id', id)
给li
元素添加一个data-id
属性,这个属性就是我们前面生成的唯一ID,它将成为后续删除操作的"凭证"。最后,ul.appendChild(li)
把新创建的li
元素添加到ul
中,页面就更新了!
-
ul.addEventListener('click', ...)
:删除事件的"监听者" :当用户点击ul
列表中的任何一个li
元素时,这个事件监听器就会被触发。e.target.getAttribute('data-id')
是这里的"魔法",它能够获取到被点击的li
元素的data-id
属性,从而知道用户想删除哪个待办事项。 -
removeItemFromArray
和removeItemFromDom
:删除事项的"双管齐下" :removeItemFromArray
负责从toDoListArray
数组中删除对应的待办事项。它通过遍历数组,找到匹配的itemId
,然后使用splice
方法删除元素。这里虽然用的是循环,但对于小规模数据来说,性能影响可以忽略不计。removeItemFromDom
则负责从页面中删除对应的li
元素。它通过document.querySelector("li[data-id="${id}"]")
再次利用data-id
属性找到对应的li
元素,然后使用itemToRemove.remove()
将其从DOM中移除。页面瞬间"清爽"了!
JavaScript就像是整个应用的"灵魂舞者",它让页面从静态的展示变成了动态的交互。通过事件监听、DOM操作和数据管理,它赋予了To-Do List生命,让它能够响应用户的操作,完成各种功能。
总结:小麻雀,大智慧!
看到这里,你是不是对这个看似简单的To-Do List有了更深入的理解?它虽然小巧,但却包含了前端开发中最核心的HTML、CSS和JavaScript知识。从骨架搭建到美化包装,再到赋予灵魂,每一步都体现了前端工程师的"匠心独运"。
这个To-Do List就像是一个"小麻雀",虽然个头不大,但"五脏俱全"。它告诉我们,即使是再简单的应用,也能从中学习到很多东西。更重要的是,它提醒我们,前端开发不仅仅是堆砌代码,更是创造用户体验的艺术。用幽默风趣的语言,把技术讲得明明白白,让枯燥的代码变得生动有趣,这才是前端的魅力所在!
希望这篇博客能让你对前端开发有新的认识,也希望你能从这个小小的To-Do List中找到属于自己的乐趣。下次再看到一个简单的应用,不妨也去扒一扒它的"底裤",说不定会有意想不到的收获哦!