当前位置: 首页 > news >正文

你的第一个Todo List项目藏着这些坑:HTML+CSS+JS新手避雷指南

你的第一个Todo List项目藏着这些坑:HTML+CSS+JS新手避雷指南

刚学完前端三件套的新手,第一个综合练习项目往往选择Todo List——它看似简单,却暗藏无数陷阱。我曾见过太多初学者在实现基础功能后,代码变成难以维护的"意大利面条",或是遭遇性能瓶颈、安全漏洞却不自知。本文将揭示那些教程里不会告诉你的10个典型错误,从事件绑定到数据持久化,帮你避开这些坑。

1. 事件监听的性能陷阱

新手最常见的错误是在循环内绑定事件监听器。比如动态生成待办事项时,为每个删除按钮单独绑定点击事件:

// 错误示范:每次添加都重新绑定所有按钮 function addTodo() { const deleteButtons = document.querySelectorAll('.delete-btn'); deleteButtons.forEach(btn => { btn.addEventListener('click', handleDelete); }); }

这种写法会导致:

  • 内存泄漏:每次添加新事项都会创建新的事件监听器
  • 性能下降:列表较长时重复绑定消耗资源

正确做法:使用事件委托(Event Delegation)

// 最佳实践:在父元素上统一监听 document.getElementById('todo-list').addEventListener('click', (e) => { if (e.target.classList.contains('delete-btn')) { handleDelete(e); } });

提示:事件委托利用了事件冒泡机制,无论新增多少子元素都无需重新绑定

2. XSS安全漏洞

直接使用innerHTML插入用户输入内容极其危险:

// 危险代码:用户输入可能包含恶意脚本 todoList.innerHTML += `<li>${userInput}</li>`;

攻击者可输入:

<script>alert('XSS攻击')</script> <img src="x" onerror="恶意代码">

防御方案

  • 使用textContent代替innerHTML
  • 对特殊字符进行转义
  • 使用DOMPurify等库过滤
// 安全写法 const li = document.createElement('li'); li.textContent = userInput; todoList.appendChild(li);

3. CSS选择器权重混乱

新手常犯的选择器错误:

/* 问题代码:选择器过于具体且重复 */ div#container ul.todo-list li.item span.text { color: blue; } /* 后面想修改却失效 */ .todo-list .text { color: red !important; /* 被迫使用!important */ }

CSS权重管理原则

  1. 避免过度限定选择器(如div > ul > li)
  2. 优先使用class选择器
  3. 保持选择器简单可覆盖
/* 优化后 */ .todo-item { color: blue; } /* 需要修改时 */ .todo-list .todo-item { color: red; /* 自然覆盖 */ }

4. 状态管理缺失

许多新手Todo项目存在"状态同步"问题:

问题表现后果解决方案
DOM直接存储状态操作后状态丢失使用数据模型
分散的状态变量难以维护集中状态管理
无唯一标识无法精准操作为每个todo添加id

推荐状态结构

const state = { todos: [ { id: 1, text: '学习React', completed: false }, { id: 2, text: '部署项目', completed: true } ], filter: 'all' };

5. 缺乏数据持久化

页面刷新后数据消失?你需要:

  1. 本地存储方案对比
方案容量有效期适用场景
localStorage5MB永久Todo列表数据
sessionStorage5MB会话期间临时数据
IndexedDB永久复杂结构化数据
  1. 实现示例
// 保存到localStorage function saveTodos() { localStorage.setItem('todos', JSON.stringify(state.todos)); } // 初始化时读取 function loadTodos() { const saved = localStorage.getItem('todos'); if (saved) state.todos = JSON.parse(saved); }

6. 无防抖处理的用户输入

连续快速点击"添加"按钮会导致重复提交:

// 问题代码:快速点击会创建多个todo addButton.addEventListener('click', addTodo);

防抖(debounce)实现

function debounce(fn, delay) { let timer; return function() { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, arguments), delay); }; } addButton.addEventListener('click', debounce(addTodo, 300));

7. 可访问性(A11Y)忽视

合格的Todo List应该:

  • 支持键盘操作(Enter添加,空格标记完成)
  • 为视觉障碍者提供ARIA标签
  • 足够的颜色对比度

改进示例

<input type="text" aria-label="新增待办事项" placeholder="输入后按Enter添加" @keyup.enter="addTodo"> <button aria-label="标记完成" @click="toggleComplete"> <span class="visually-hidden">完成</span> <svg>...</svg> </button>

8. 移动端适配缺失

常见移动端问题及解决方案:

  • 触摸目标太小:按钮至少48x48px
  • 输入法遮挡:滚动到可视区域
  • 滑动删除:添加touch事件支持
/* 触摸友好设计 */ .todo-item { padding: 12px; min-height: 48px; } .delete-btn { width: 48px; height: 48px; }

9. 无错误边界处理

健壮的代码应该处理:

// 数据加载失败 try { const data = JSON.parse(localStorage.getItem('todos')); } catch (e) { console.error('解析失败', e); state.todos = []; } // 网络请求失败 fetch('/api/todos') .then(response => { if (!response.ok) throw new Error('请求失败'); return response.json(); }) .catch(error => { showToast(error.message); });

10. 项目结构混乱

典型的新手文件结构:

project/ index.html script.js style.css images/

优化后的结构

todo-app/ src/ components/ TodoItem/ index.js style.css stores/ todos.js utils/ storage.js api.js public/ index.html assets/ package.json

实现第一个Todo List只是起点,真正的价值在于理解这些避坑原则后,你能写出更健壮、可维护的前端代码。当我在团队代码审查中看到类似问题时,总会想起自己当年踩过的这些坑——现在你可以轻松跨过它们了。

http://www.jsqmd.com/news/598466/

相关文章:

  • 告别ifconfig手忙脚乱:MobaXterm一键SSH连接VMware Ubuntu的保姆级图解
  • LangChain 官方出手了:这个 Agent 框架自带规划、文件系统和子 Agent 派发
  • StructBERT情感分析镜像部署实录:解决WebUI打不开/超时常见问题
  • 市政规划许可场景钓鱼攻击机理与闭环防御研究
  • 告别网络抽风!Ubuntu 22.04下Intel I219-V网卡设置固定IP与禁用IPv6的保姆级教程
  • 电子取证必备:手把手教你用ADB命令提取手机APK(含避坑指南)
  • Java、从零开始学异常
  • FinalBurn Neo终极指南:3步快速开启你的复古街机游戏之旅
  • PHP防止Shell命令注入的有效方法
  • 无片外电容LDO电路设计 完整IP现成电路,具有过温保护和过流保护,带隙,BUFFER都有 性...
  • 告别手动编译!用SDKManager一键为Jetson Orin NX刷入JetPack 6.2.1并开启实时内核
  • 【实战指南】迪文屏开发全流程解析与优化技巧
  • Florence-2 视觉语言模型适配 Neuron SDK 全记录:Stage-wise 编译与 Bucket 策略实战
  • 终极方案:如何用代码替代拖拽,高效绘制专业架构图与流程图
  • 告别固定阈值!用DBnet做文本检测,手把手教你搞定自适应二值化(附PyTorch代码)
  • 如何快速配置虚拟手柄驱动:面向游戏玩家的完整教程
  • 低代码技术如何重构钣金工厂的数字化生产链路
  • 深入MAX30102传感器:从光电信号到心率血氧值的完整数据处理流程解析
  • 智慧机场三维空间智能中枢系统白皮书——构建“全域感知 × 空间认知 × 智能调度”的下一代机场操作平台
  • 新手必看:5分钟搞定Linux服务器基础命令行操作(含常见问题解决)
  • 告别CSDN限制!VScode+PicGo+Github图床保姆级配置指南(支持Markdown写作)
  • Wan2.2-I2V-A14B效果实测:不同prompt下视频连贯性、画质、运动自然度分析
  • 伺服压力机与MCGS、昆仑通态触摸屏:实时曲线、历史数据存盘与完整PLC程序功能概述
  • Text-to-SQL实战:如何用RSL-SQL在5分钟内提升数据库查询准确率(附避坑指南)
  • Atcoder abc452_e 笔记
  • DCDC电源带载不稳?5个常见坑点及实测排查指南(附波形分析)
  • 从Fetch到SSE:我的大模型前端对接踩坑实录(附性能对比表格)
  • 智慧车站三维空间智能管控系统白皮书——构建“全域感知 × 连续认知 × 动态调度”的交通枢纽空间智能中枢
  • 告别启动黑屏:RK3568设备树中bootargs的PARTUUID到底该怎么写?(附完整配置流程)
  • gcc-multilib安装指南:解决Linux编译中的‘fatal error: sys/cdefs.h‘问题