1 分钟

一步步构建简单待办应用(通俗易懂)

一份清晰的逐步指南,教你构建一个小型待办应用:规划功能、创建界面、添加逻辑、保存数据、测试并发布。

一步步构建简单待办应用(通俗易懂)

我们要做什么(以及你会学到什么)

在本指南里,当我说“应用”时,我指的是一个小型的网页应用:在浏览器中打开的单页,能响应你的点击和输入。不需要安装、不需要账户、也不需要复杂的设置——只是一个能在本地运行的小项目。

目标(完成后的功能)

到最后,你会得到一个待办应用,它可以:

  • 添加任务:从输入框添加任务
  • 标记任务为已完成(这样你可以看出哪些已完成)
  • 删除任务:移除不需要的任务
  • 在刷新后保留任务,使用 localStorage(这样关闭标签页不会丢失内容)

它不会很完美或“企业级”,这正是目的:这是为初学者设计的项目,教你基础知识而不会丢给你太多工具。

你会学到什么(通俗说明)

你将一步一步构建这个应用,并掌握前端网页应用的核心部分:

  • HTML:创建页面结构(输入、按钮、列表)
  • CSS:让界面可读且好用
  • JavaScript:处理点击、创建列表项并更新页面
  • localStorage:保存并加载任务,让它们持久化
  • 基本的测试与调试(发现小错误并修复)

开始前你需要准备的东西

保持简单。你只需要:

  • 一台电脑(Windows、Mac 或 Linux)
  • 一个现代浏览器(Chrome、Firefox、Safari 或 Edge)
  • 一个代码编辑器(VS Code 很受欢迎,但任何编辑器都可以)

如果你能创建文件夹并编辑几个文件,就可以开始了。

第 1 步:确定应用目标与功能

在写任何代码之前,先决定什么才算“成功”。本教程只做一个小应用、一个明确的工作:帮你跟踪你想做的任务。

选定应用的主要目标

写一句话的目标,构建时随手可见:

“这个应用让我把任务添加到列表里,以免忘记。”

就是这样。如果你想加日历、提醒、标签或账户功能,把它们先放到以后再做。

必须有 vs 可选功能

快速列两份清单:

必须有(本项目)

  • 添加任务(输入文字,点击按钮)
  • 在列表中查看任务
  • 标记任务为已完成
  • 删除任务
  • 在刷新后保留任务(稍后我们会用 localStorage

可选(今天不需要):到期日、优先级、分类、搜索、拖放、云同步。

把“必须有”控制在小范围内能帮助你真正完成项目。

用通俗的话描述界面

这个应用可以是单页,包含:

  • 一个文本输入(“添加任务…”)和一个“添加”按钮
  • 下方的任务列表
  • 每个任务有一个“完成”切换和一个“删除”按钮

定义“已完成”是什么意思

明确一点,避免卡住:

  • “已完成” = 任务看起来被划掉(或变淡),并且在被删除之前它仍保留在列表中。

决定后,就可以设置项目文件了。

第 2 步:搭建项目文件

在写任何代码之前,先为应用创建一个干净的小“家”。从一开始就整理好文件会让后面的步骤更顺利。

1)创建项目文件夹

在电脑上新建一个文件夹,取名如 todo-app。这个文件夹会存放项目的所有内容。

在该文件夹中创建三个文件

  • index.html(页面结构)
  • styles.css(外观与布局)
  • app.js(行为与交互)

如果你的系统隐藏文件扩展名(比如“.html”),确认你真的是创建了 index.html,常见的初学者错误是最后变成了 index.html.txt

2)在编辑器和浏览器中打开文件夹

在代码编辑器(VS Code、Sublime Text 等)中打开 todo-app 文件夹,然后在浏览器中打开 index.html

此时页面可能是空白的——没关系,下一步我们会添加内容。

3)如何查看修改效果

编辑文件时,浏览器不会自动更新(除非你使用自动刷新工具)。

基本流程是:

  1. 在编辑器保存文件
  2. 切到浏览器页签
  3. 刷新页面查看更新

如果出现“没反应”的情况,先尝试刷新。

可选:使用本地服务器(推荐但非必须)

你也可以通过本地服务器打开页面,这能避免一些后续的奇怪问题(尤其是开始保存数据或加载文件时)。

适合初学者的选项:

  • 在 VS Code 安装 Live Server,然后点击 “Go Live”
  • 如果电脑上已安装 Python,在项目文件夹中运行:
python -m http.server

然后在浏览器中打开它打印的地址(通常是 http://localhost:8000)。

第 3 步:构建页面结构(HTML)

现在我们来做一个干净的骨架。这个 HTML 不会让页面变得“可交互”(交互是下一步的事),但它会为你的 JavaScript 提供清晰的读写位置。

我们需要的最小布局

包括:

  • 一个标题,让页面一目了然
  • 一个输入框用于任务文本
  • 一个添加按钮用以提交任务
  • 一个列表区域,用来显示新增任务

使用简单且可读的名称。良好的 id/class 会让后面 JavaScript 通过名称获取元素时更方便。

将下面内容粘到你的 index.html

\u003c!doctype html\u003e
\u003chtml lang=\"en\"\u003e
  \u003chead\u003e
    \u003cmeta charset=\"utf-8\" /\u003e
    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e
    \u003ctitle\u003eTo‑Do App\u003c/title\u003e
    \u003clink rel=\"stylesheet\" href=\"styles.css\" /\u003e
  \u003c/head\u003e
  \u003cbody\u003e
    \u003cmain class=\"app\" id=\"app\"\u003e
      \u003ch1 class=\"app__title\" id=\"appTitle\"\u003eMy To‑Do List\u003c/h1\u003e

      \u003cform class=\"task-form\" id=\"taskForm\"\u003e
        \u003clabel class=\"task-form__label\" for=\"taskInput\"\u003eNew task\u003c/label\u003e
        \u003cdiv class=\"task-form__row\"\u003e
          \u003cinput
            id=\"taskInput\"
            class=\"task-form__input\"
            type=\"text\"
            placeholder=\"e.g., Buy milk\"
            autocomplete=\"off\"
          /\u003e
          \u003cbutton id=\"addButton\" class=\"task-form__button\" type=\"submit\"\u003e
            Add
          \u003c/button\u003e
        \u003c/div\u003e
      \u003c/form\u003e

      \u003cul class=\"task-list\" id=\"taskList\" aria-label=\"Task list\"\u003e\u003c/ul\u003e
    \u003c/main\u003e

    \u003cscript src=\"app.js\"\u003e\u003c/script\u003e
  \u003c/body\u003e
\u003c/html\u003e

结构就这些。注意我们用了 id="taskInput"id="taskList"——这两个元素将会在 JavaScript 中频繁使用。

第 4 步:做点样式让界面更清爽(CSS)

现在页面存在了,但可能看起来像一份普通文档。简单的 CSS 会让它更好用:间距更清晰、文字可读、按钮更像可点击的元素。

从一个居中的容器开始

居中的框能让应用更集中,防止内容在宽屏上拉得太开。

/* Basic page setup */
body {
  font-family: Arial, sans-serif;
  background: #f6f7fb;
  margin: 0;
  padding: 24px;
}

/* Centered app container */
.container {
  max-width: 520px;
  margin: 0 auto;
  background: #ffffff;
  padding: 16px;
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}

让任务便于扫视

每个任务应该看起来像独立的一行,间距舒适。

ul { list-style: none; padding: 0; margin: 16px 0 0; }

li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid #e7e7ee;
  border-radius: 8px;
  margin-bottom: 10px;
}

.task-text { flex: 1; }

添加清晰的“已完成”样式

当任务完成时,应有明显的视觉变化,便于一眼识别。

.done .task-text {
  text-decoration: line-through;
  color: #777;
  opacity: 0.85;
}

按钮风格一致

保持按钮尺寸与样式一致,会让它们看起来像同一个应用的一部分。

button {
  border: none;
  border-radius: 8px;
  padding: 8px 10px;
  cursor: pointer;
}

button:hover { filter: brightness(0.95); }

这就足够让 UI 干净友好了——不需要花哨技巧。接下来我们用 JavaScript 接上交互。

第 5 步:添加任务(JavaScript 基础)

规划下一版本
在生成代码前规划编辑、筛选、到期等功能。

既然页面上有输入框、按钮和列表,我们就需要让它们“动”起来。目标很简单:当有人输入任务并点击 Add(或按 Enter)时,列表里出现新项。

绑定按钮和 Enter 键

在你的 JavaScript 文件中,先获取需要的元素,然后监听两种动作:按钮点击和输入框内的 Enter。

const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');

function addTask() {
  const text = taskInput.value.trim();

  // Block empty tasks (including ones that are just spaces)
  if (text === '') return;

  const li = document.createElement('li');
  li.textContent = text;
  taskList.appendChild(li);

  // Clear the input and put the cursor back
  taskInput.value = '';
  taskInput.focus();
}

addBtn.addEventListener('click', addTask);

taskInput.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    addTask();
  }
});

这段代码做了什么(通俗说明)

  • 读取输入框里的文本并用 trim() 去掉首尾多余空格。
  • 如果是空文本就阻止添加,避免出现空项。
  • 创建一个新的 <li>,设置它的文本并加入列表中。
  • 清空输入框并把焦点放回输入框,方便快速继续输入下一个任务。

如果没有反应,先检查 HTML 中的元素 id 是否与 JavaScript 里的选择器完全对应(这是初学者常踩的坑)。

第 6 步:标记完成与删除任务

现在你可以添加任务了,接下来让任务可操作:可以标记为已完成,也可以删除它们。

将任务存成简单的数据结构

不要只把任务当字符串保存,用对象更灵活。这样每个任务都有稳定的身份和“已完成”状态:

  • text:任务内容
  • donetruefalse
  • id:唯一编号,用来定位或删除特定任务

示例:

let tasks = [
  { id: 1, text: "Buy milk", done: false },
  { id: 2, text: "Email Sam", done: true }
];

添加“完成”控件和删除按钮

渲染每个任务时,包含一个复选框或“完成”按钮,以及一个“删除”按钮。

事件监听器(event listener)就是用来响应点击的。你把监听器挂到按钮(或整个列表容器)上,当用户点击时,对应的代码就会运行。

对初学者友好的做法是使用事件委托:在任务列表容器上放一个点击监听器,然后根据被点击的元素判断操作。

function toggleDone(id) {
  tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
  renderTasks();
}

function deleteTask(id) {
  tasks = tasks.filter(t => t.id !== id);
  renderTasks();
}

document.querySelector("#taskList").addEventListener("click", (e) => {
  const id = Number(e.target.dataset.id);
  if (e.target.matches(".toggle")) toggleDone(id);
  if (e.target.matches(".delete")) deleteTask(id);
});

在你的 renderTasks() 函数中:

  • 给每个按钮加上 data-id="${task.id}"
  • .done 类来在视觉上标注已完成的任务。

第 7 步:保存任务以便持久化(localStorage)

现在你的待办清单有个烦恼:刷新页面或关闭标签页后,所有内容都消失了。

原因是任务只存在于 JavaScript 的内存里,页面重载后内存会被清空。

localStorage = 浏览器内一个小“储物箱”

localStorage 内置于浏览器。把它想象成一个可以按键(key)存放文本的小盒子。它适合初学者项目,因为不需要服务器或账号系统。

我们会把整个任务数组转为 JSON 文本保存,然后在页面打开时再读回来。

1)每次变更后保存

每当你添加、标记完成或删除任务时,都调用 saveTasks()

const STORAGE_KEY = "todo.tasks";

function saveTasks(tasks) {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}

无论何处更新了 tasks 数组,紧接着执行:

saveTasks(tasks);
renderTasks(tasks);

2)页面启动时加载并渲染

页面加载时读取保存的值。如果没有保存过,就回退到空数组。

function loadTasks() {
  const saved = localStorage.getItem(STORAGE_KEY);
  return saved ? JSON.parse(saved) : [];
}

let tasks = loadTasks();
renderTasks(tasks);

就这样:你的应用现在能在刷新后记住任务。

提示:localStorage 只能存字符串,所以用 JSON.stringify() 把数组变成文本,用 JSON.parse() 在读取时再把文本变回数组。

第 8 步:测试并修复常见错误

保留完整所有权
获取源代码,便于查看、修改或迁移到任意地点。

测试听起来无聊,但它是把你的待办应用从“在我机器上能用”变成“在任何时候都能用”的最快方法。在每次小改动后做一次快速检查。

快速测试流程(60 秒)

按下面顺序试一遍主要流程:

  • 添加一个任务
  • 标记为完成
  • 删除它
  • 刷新页面(确认 UI 行为仍正确)

如果某一步出问题,先修复再做新功能。把问题堆在一起会让小应用变得难以维护。

常见会搞坏初学者应用的边界情况

边界情况是你没有特意设计但用户会做的操作:

  • 很长的文本(会不会溢出或把按钮挤走?)
  • 重复任务(你是否删除了正确的那个?)
  • 空输入(会不会添加空任务?)

常见的解决是阻止空任务:

const text = input.value.trim();
addButton.disabled = text === "";

(可以在每次 input 事件时执行,也可以在添加前再次检查。)

快速排查“没有反应”的清单

当点击没有反应,通常是因为:

  • 选择器不匹配(HTML 的 id/class 与 JS 不一致)
  • 脚本未加载(文件名/路径错误,或 app.js 找不到)
  • 有 JavaScript 错误阻止了后面的代码执行

用控制台查看实际发生了什么

当问题看起来随机时,打日志:

console.log("Adding:", text);
console.log("Current tasks:", tasks);

在浏览器的 Console 查看错误(红色文字)。问题解决后记得删掉这些日志,避免把项目搞得杂乱。

第 9 步:提升可用性(无障碍与移动端)

当一个待办应用“完成”的标准之一是:在手机上、用键盘以及使用无障碍工具(如屏幕朗读器)时都让人感觉舒适。

让移动端易于点击

在小屏幕上,太小的按钮会让人很烦。确保可点击元素有足够空间:

  • 按钮使用更大的内边距(高度目标约 44px)
  • 完成删除 按钮之间留出间距,避免误触
  • 在移动端让输入框占满宽度,打字更自然

在 CSS 中,增加 paddingfont-sizegap 经常能带来明显改善。

添加标签与 ARIA(是什么与为什么)

屏幕朗读器需要清晰的控件名字:

  • 文本输入应有真实的 <label>(这是最佳做法)。如果你不想显示它,可以用 CSS 将其视觉隐藏,但保留在 HTML 中。
  • 仅靠图标的按钮(比如垃圾桶图标)应包含 aria-label="Delete task",这样屏幕朗读器不会只读出“按钮”而没有上下文。

这能帮助使用辅助工具的人理解每个控件的作用。

键盘支持:Tab + Enter

确保可以不靠鼠标也能操作应用:

  • 使用 Tab 能切到输入框和按钮
  • 在输入框按 Enter 应该能添加任务(使用 <form> 可以让 Enter 自然工作)
  • 添加任务后考虑把焦点返回到输入框,方便连续添加

可读性:对比度与字体大小

使用可读的字体大小(16px 是个好基线)和充足的颜色对比(深色文字配浅背景或反之)。不要只用颜色来表示“已完成”这一状态——加上线穿或其他明确样式更好。

第 10 步:整理与优化代码

边构建边赚取
制作关于 Koder.ai 的内容或邀请他人,获得构建积分。

功能都实现后,花 10–15 分钟把代码整理一下,这会让未来的修复更容易,也让你下次回来时更容易理解自己的项目。

简单的文件结构

保持小而可预测:

  • /index.html — 页面结构(输入、按钮、列表)
  • /styles.css — 界面样式(间距、字体、已完成样式)
  • /app.js — 行为(添加、切换完成、删除、保存/加载)
  • /README.md — 给“未来的你”的快速说明

如果你喜欢子文件夹,也可以:

  • /css/styles.css
  • /js/app.js

只要确保你的 <link><script> 路径一致。

让 JavaScript 更易读

几个快速改进:

  • 用清晰的名字:taskInputtaskListsaveTasks()
  • 保持函数短小:每个函数只做一件事
  • 仅在代码不明显时添加简短注释(避免每行都注释)

例如,以下一目了然:

  • renderTasks(tasks)
  • addTask(text)
  • toggleTask(id)
  • deleteTask(id)

添加简短的 README

你的 README.md 可以很简单:

  • 功能说明(一句:一个会保存任务的待办列表)
  • 如何运行(在浏览器中打开 index.html
  • 可选:已知的限制

备份你的项目

至少在完成一个里程碑后压缩备份一次(例如“localStorage 生效”时)。如果想要版本历史,Git 很好用,但不是必须。哪怕一份备份也能防止误删造成的损失。

第 11 步:发布并分享你的应用

发布就是把应用文件(HTML、CSS、JavaScript)放到互联网上,让别人打开链接就能使用。由于这个待办应用是静态站点(在浏览器运行,不需要服务器),你可以免费托管在多个服务上。

选项 1:GitHub Pages(适合静态站点)

大致步骤:

  1. 注册 GitHub(如果还没有)
  2. 新建仓库并上传文件(或用 Git 推送)
  3. 在仓库的 Settings → Pages 中设置
  4. 选择要发布的分支(通常是 main)和文件夹(通常是根目录 /
  5. 保存后,GitHub 会给你一个公开的 URL

如果有多个文件,记得检查文件名与引用是否完全一致(例如 styles.cssstyle.css 的区别)。

选项 2:Netlify 或 Vercel(拖拽式更简单)

如果你想最省事的“上传并使用”方式:

  1. 在 Netlify 或 Vercel 注册账号
  2. 找到 Deploy / Add New Project
  3. 把项目文件夹拖放上去(或连接 GitHub 仓库)
  4. 等待上传/构建完成
  5. 打开生成的 URL 并测试

分享前的快速检查清单

  • 刷新页面:任务仍然保留(localStorage 生效)
  • 添加、完成与删除任务:没有坏掉的按钮
  • 在手机上打开(或缩小浏览器窗口):没有溢出的内容

通过这些检查后,把链接发给朋友,让他们试一试——新鲜的眼睛常能发现问题。

后续可尝试的简单升级

你已经做出了一个可用的待办应用。如果想在不跳跃到巨型项目的情况下继续学习,这些升级既实用又能教到额外的模式。

1)编辑任务文本

给每个任务加一个“编辑”按钮。点击时把任务标签替换成一个小的输入框(预填当前文本),并显示“保存/取消”按钮。

提示:保持任务数据为带 idtext 的对象。编辑时就是按 id 找到任务、更新 text、重新渲染并保存。

2)筛选:全部 / 未完成 / 已完成

在顶部加三个按钮:AllActiveDone

把当前过滤器存在变量里,例如 currentFilter = 'all'。渲染时根据过滤器显示:

  • All:所有任务
  • Active:未完成任务
  • Done:已完成任务

3)到期日或简单分类

保持轻量:

  • 加一个可选的到期日(格式 YYYY-MM-DD),并显示在任务旁边
  • 或用下拉菜单加一个“Work / Home / School”之类的分类

哪怕只加一个字段,也能教你如何同时更新数据模型和界面。

4)进阶:从 localStorage 迁移到简单后端

当你准备好了,核心思路是:不再把任务保存到 localStorage,而是通过 fetch() 发送到 API(服务器)。服务器把它存到数据库,这样不同设备间就能同步任务。

如果你想在不重写全部代码的情况下尝试这个跳跃,一些生成/原型平台可以帮你快速试错并导出代码。

5)下一个适合初学者的项目

试试做一个笔记应用(带搜索)或一个习惯追踪器(每日打卡)。它们复用相同技能:列表渲染、编辑、保存与基础 UI 设计。

常见问题

构建这个待办应用需要什么?

你只需要一台电脑、现代浏览器和代码编辑器。创建一个文件夹,包含 index.htmlstyles.cssapp.js,然后在浏览器中打开 HTML 文件。

适合初学者的待办应用应包含哪些功能?

先实现四个操作:添加任务、在列表中查看任务、将任务标记为完成,以及删除任务。截止日期、分类和账户等附加功能可以以后再加。

项目中的每个文件分别做什么?

HTML 用于创建输入框、按钮和任务列表。CSS 控制间距和外观。JavaScript 读取用户操作、更新列表并保存任务数据。

如何防止添加空白任务?

使用包含文本输入框和提交按钮的表单。JavaScript 应去除输入值两端的空白,拒绝空结果,创建列表项,然后清空输入框并让它重新获得焦点。

为什么任务应使用对象而不是纯文本?

使用包含 idtextdone 值的任务对象。即使两个任务的文本相同,ID 也能让代码更新或删除准确的那个任务。

如何将任务标记为已完成?

为每个任务添加复选框或“完成”按钮。用户点击后,修改该任务的 done 值,并添加一个 CSS 类来应用删除线或淡化样式。

应用如何在刷新后记住任务?

使用 JSON.stringify() 将任务数组存储在 localStorage 的一个名称下,并在页面启动时用 JSON.parse() 读取。添加、完成、编辑或删除任务后,都要再次保存。

为什么我的“添加”按钮没有任何反应?

先刷新页面,并打开浏览器控制台查看红色错误信息。然后检查 HTML ID 是否与 JavaScript 选择器匹配,以及脚本文件名是否与 index.html 中的路径一致。

如何让应用在手机上和使用键盘时更易用?

为任务输入框使用真正的标签,确保控件足够大,方便点击,并用 Tab 和 Enter 测试应用。已完成的任务不应只靠颜色区分,还应使用删除线和清晰的文本等提示。

发布待办应用前应测试什么?

测试添加任务、将其标记为完成、删除任务以及刷新页面。分享应用前,还要尝试空输入、较长的任务名称和重复的任务文本。

Related posts