从零构建一个高可访问性的待办事项网页:前端初学者的深度实践指南
很多前端开发者入门时,都会选择“待办事项”这个经典项目来练手。它看似简单,却涵盖了现代Web开发的核心要素:结构、样式、交互,以及常常被忽视但至关重要的可访问性。今天,我们不只满足于实现功能,而是要打造一个从零开始、移动优先、对所有人(包括使用辅助技术的用户)都友好的待办事项应用。这不仅仅是一个教程,更是一次对高质量前端开发实践的深度探索。
无论你是刚刚接触HTML、CSS和JavaScript的新手,还是希望巩固基础、提升代码质量的开发者,这个项目都将带你走完一个完整的前端开发流程。我们会从最基础的语义化HTML结构开始,逐步添加现代化的CSS布局和响应式设计,然后用JavaScript实现核心交互逻辑,并最终将重点放在可访问性优化上。你会发现,编写“好”的代码,与编写“能用”的代码,中间隔着对细节的执着追求。
1. 项目规划与语义化HTML结构搭建
在动手写第一行代码之前,明确我们要构建什么至关重要。一个基础的待办事项应用需要具备以下核心功能:允许用户输入新的待办事项、将其添加到列表中、标记事项为已完成,以及删除不再需要的事项。同时,我们还需要一个区域来显示统计信息,比如总任务数和已完成数。
1.1 为什么语义化HTML是基石
语义化HTML意味着使用最恰当的HTML元素来表达内容的含义,而不仅仅是视觉呈现。例如,用 <header> 表示页眉,用 <main> 表示主要内容区,用 <button> 表示可点击的按钮。这样做的好处是多方面的:
- 对辅助技术友好:屏幕阅读器等工具可以准确理解页面结构,为用户提供清晰的导航。
- SEO优化:搜索引擎更容易理解页面内容,有助于提升排名。
- 代码可维护性:结构清晰的代码更易于开发者阅读和维护。
- 未来兼容性:语义化标签定义了标准行为,在不同浏览器和设备上表现更一致。
1.2 构建HTML骨架
让我们从创建 index.html 文件开始。我们将构建一个包含以下部分的页面结构:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的待办事项 | 一个高可访问性的实践项目</title>
<link rel="stylesheet" href="styles.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- 后续我们会引入字体和图标 -->
</head>
<body>
<div class="app-container">
<!-- 页头区域 -->
<header class="app-header" role="banner">
<h1>📝 我的待办清单</h1>
<p class="tagline">保持专注,今日事今日毕。</p>
</header>
<!-- 核心应用区域 -->
<main class="app-main" role="main">
<!-- 新增待办事项的表单 -->
<section class="add-todo-section" aria-labelledby="add-todo-heading">
<h2 id="add-todo-heading" class="visually-hidden">添加新任务</h2>
<form id="todo-form" class="todo-form">
<div class="input-group">
<label for="new-todo-input" class="visually-hidden">任务描述</label>
<input type="text"
id="new-todo-input"
class="todo-input"
placeholder="例如:学习CSS Grid布局..."
aria-describedby="input-hint"
required>
<p id="input-hint" class="input-hint">输入后按“添加”或回车键确认</p>
</div>
<button type="submit" class="btn btn-primary" aria-label="添加新任务">
<span aria-hidden="true">➕</span> 添加任务
</button>
</form>
</section>
<!-- 待办事项列表区域 -->
<section class="todo-list-section" aria-labelledby="todo-list-heading">
<div class="section-header">
<h2 id="todo-list-heading">任务列表</h2>
<div class="list-controls">
<button id="filter-all" class="btn-filter active" aria-pressed="true">全部</button>
<button id="filter-active" class="btn-filter" aria-pressed="false">未完成</button>
<button id="filter-completed" class="btn-filter" aria-pressed="false">已完成</button>
</div>
</div>
<!-- 列表容器,我们将用JS动态填充 -->
<div class="list-container">
<ul id="todo-list" class="todo-list" aria-live="polite" aria-relevant="additions removals">
<!-- 初始为空,JS动态生成列表项 -->
</ul>
<p id="empty-list-message" class="empty-message">列表空空如也,添加你的第一个任务吧!</p>
</div>
</section>
<!-- 统计信息面板 -->
<aside class="stats-panel" aria-labelledby="stats-heading" role="region">
<h3 id="stats-heading">📊 任务统计</h3>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-value" id="total-count">0</span>
<span class="stat-label">总任务</span>
</div>
<div class="stat-item">
<span class="stat-value" id="pending-count">0</span>
<span class="stat-label">待完成</span>
</div>
<div class="stat-item">
<span class="stat-value" id="completed-count">0</span>
<span class="stat-label">已完成</span>
</div>
</div>
<div class="action-area">
<button id="clear-completed" class="btn btn-secondary" disabled>
🗑️ 清除已完成
</button>
<p class="storage-hint" id="storage-status">数据已自动保存至本地。</p>
</div>
</aside>
</main>
<!-- 页脚 -->
<footer class="app-footer" role="contentinfo">
<p>© <span id="current-year">2025</span> - 这是一个专注于可访问性的前端学习项目。</p>
<p>使用键盘的 <kbd>Tab</kbd> 键导航,<kbd>Enter</kbd> 或 <kbd>Space</kbd> 键进行操作。</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>
提示:注意代码中使用的
aria-*属性(如aria-labelledby,aria-live,aria-describedby)和role属性。它们是实现可访问性的关键,我们会在后续章节详细解释其作用。
1.3 关键HTML元素与属性解析
让我们拆解上面代码中的一些重要部分:
<form>与<input>:将输入和按钮包裹在<form>中,可以利用原生的表单行为(如按回车键提交),并更好地组织相关元素。aria-labelledby与aria-describedby:aria-labelledby=”add-todo-heading”将区域(<section>)与一个标题(<h2>)关联起来,明确区域的用途。aria-describedby=”input-hint”为输入框关联了一段补充说明,屏幕阅读器会朗读这段文字。
aria-live=”polite”:这个属性加在动态更新的列表容器<ul>上。当列表内容发生变化

&spm=1001.2101.3001.5002&articleId=154174866&d=1&t=3&u=32172512ff864013b37667f440d4f15e)

被折叠的 条评论
为什么被折叠?



