从零开始:用HTML+CSS+JS手搓一个待办事项网页(含可访问性优化)

从零构建一个高可访问性的待办事项网页:前端初学者的深度实践指南

很多前端开发者入门时,都会选择“待办事项”这个经典项目来练手。它看似简单,却涵盖了现代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-labelledbyaria-describedby
    • aria-labelledby=”add-todo-heading” 将区域(<section>)与一个标题(<h2>)关联起来,明确区域的用途。
    • aria-describedby=”input-hint” 为输入框关联了一段补充说明,屏幕阅读器会朗读这段文字。
  • aria-live=”polite”:这个属性加在动态更新的列表容器 <ul> 上。当列表内容发生变化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值