STATE-04 · 原创网页练习

练习清单的状态反馈

添加一个虚构练习、标记完成,再重置初始清单。示例把状态变化写成可见文字并用温和通知区域辅助动态阅读。数据只存在当前页面内存里,刷新会回到初始状态。

已提供依据
练习清单的状态反馈字段说明
本站编号 STATE-04 仅用于组织原创练习,与9.1产品版本无对应关系。
源文件 status-feedback.html
结构与行为 JavaScript增强
练习系列 本页状态练习
读者路径 键盘学习路径、首次制作网页
运行条件 JavaScript增强添加、完成和重置。 当前页内存保存状态;不使用跨设备账号或后台存储。
数据范围 虚构练习数据;当前页面内存 不联网提交,不包含账号、API或真实预约。
源码许可 MIT 复制或修改源码时保留版权与许可文字。

打开、阅读与下载

本站原创教学源码;练习中的昵称、任务和数据为虚构。编号用于本站组织内容,与9.1产品版本没有对应关系。

“9.1免费版代码”是未经产品身份确认的检索线索。这份练习来自本站,不是任何9.1产品的官方源码。线上运行页属于本站;下载包核心代码无需第三方库或网络。

这份练习适合谁

适合

  • 适合学习事件、数组与DOM呈现。
  • 适合把“按钮已点击”改成有内容的结果反馈。

不适合

  • 不适合需要永久保存的真实任务管理。
  • 不能当作实时多人协作应用。

运行前要知道什么

  • JavaScript增强添加、完成和重置。
  • 当前页内存保存状态;不使用跨设备账号或后台存储。

先解压ZIP并保留README和LICENSE,再用浏览器打开HTML。准备一个未修改的副本;每次只改一个位置,保存后重新载入。接入模块、接口或长期存储属于后续扩展,需要重新说明运行条件。

这次练习观察三件事

  1. 空任务名应得到可理解提示。
  2. 完成后同时观察计数与按钮文字。
  3. 重置应恢复初始数据并有结果说明。

这些观察点供你读源码和练习使用,没有宣称所有浏览器、设备或辅助技术已经实测。

完整原创源文件

下面的源代码与下载的纯文本文件一致。HTML正文、CSS和必要的JavaScript在同一文件中,方便对照各层职责。

展开 status-feedback.html 完整源码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>任务状态与温和反馈|9.1免费版代码</title>
<meta name="description" content="9.1免费版代码:原创练习任务清单用 checkbox、可见完成数、aria-live 与安全文字插入演示状态反馈,只在内存保留,附 MIT 源码。">
<meta name="keywords" content="9.1免费版代码,原创网页示例,HTML,CSS,JavaScript">
<link rel="canonical" href="https://91-web.cn/examples/status-feedback.html">
<meta name="theme-color" content="#faf7ef">
<style>*{box-sizing:border-box}html{scroll-padding-top:1.5rem}body{margin:0;background:#faf7ef;color:#142e4c;font:16px/1.7 system-ui,-apple-system,"Segoe UI",sans-serif}a{color:#075e56;text-underline-offset:.2em}a:hover{color:#0d473f}button,input,select,textarea{font:inherit}button,.button{min-height:44px;border:1px solid #0a635b;border-radius:9px;background:#0a635b;color:#fff;padding:.65rem 1rem;cursor:pointer;font-weight:650;display:inline-block;text-decoration:none}button:hover,.button:hover{background:#084f48;color:#fff}button:disabled{cursor:not-allowed;opacity:.6}.secondary{background:#fff;color:#075e56}.secondary:hover{background:#e5f4ec;color:#075e56}input,select,textarea{width:100%;min-height:44px;padding:.65rem;border:1px solid #869993;border-radius:8px;background:#fff;color:#142e4c}input[type=checkbox]{width:auto;min-height:0;accent-color:#0a635b}label{display:block;font-weight:650;margin-bottom:.3rem}fieldset{border:1px solid #c3d4cd;border-radius:12px;padding:1rem;margin:1rem 0}legend{padding:0 .4rem;font-weight:700}h1{font-size:clamp(1.7rem,4.5vw,2.7rem);line-height:1.2;max-width:24ch}h2{font-size:1.5rem;line-height:1.35;margin-top:0}h3{font-size:1.1rem}p{margin:.7rem 0}header,main,footer{width:min(100% - 2rem,920px);margin-inline:auto}header{padding:2rem 0 1.2rem;border-bottom:1px solid #c3d4cd}.eyebrow{color:#37625b;font-size:.85rem;letter-spacing:.06em;font-weight:750}.lead{font-size:1.1rem;max-width:65ch}.notice{border-left:4px solid #d69b3b;padding:.75rem 1rem;background:#fff7df;font-size:.94rem}main{padding:1.5rem 0 3rem}section{margin:0 0 1.4rem;padding:1.3rem;border:1px solid #d2ddd3;background:#fff;border-radius:14px;box-shadow:0 5px 15px #142e4c06}.demo{background:#ecf6ee;border-color:#a6c5b9}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem}.row{display:flex;gap:.8rem;flex-wrap:wrap;align-items:center}.field{margin-bottom:1rem}.help{font-size:.9rem;color:#47635c}.error{color:#a32118;font-weight:650}input[aria-invalid=true],select[aria-invalid=true]{border:2px solid #a32118}.summary{padding:1rem;border:2px solid #a32118;border-radius:8px;background:#fff5f3}.result{padding:1rem;border:1px solid #9cbeaf;border-radius:9px;background:#fff;margin-top:1rem}.status{min-height:1.8em;color:#27554b;margin:.8rem 0}.badge{font-size:.85rem;padding:.18rem .55rem;border-radius:99px;background:#dcefe6;color:#164e43}.scroll{overflow-x:auto;border:1px solid #b9cdc2;border-radius:9px}table{width:100%;border-collapse:collapse;min-width:540px;background:#fff}caption{text-align:left;font-weight:700;padding:1rem;background:#e5f1e9}th,td{padding:.8rem;border-bottom:1px solid #ceddd3;text-align:left}thead th{background:#f1f6f0}tbody th{font-weight:650}pre{white-space:pre-wrap;overflow-wrap:anywhere;padding:1rem;background:#142e4c;color:#eaf6ed;border-radius:9px;font:14px/1.7 ui-monospace,SFMono-Regular,Consolas,monospace}code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:.93em}.downloads{display:flex;gap:.7rem;flex-wrap:wrap;margin:1rem 0}footer{border-top:1px solid #c3d4cd;padding:1.5rem 0 2rem;font-size:.9rem}.skip{position:absolute;left:1rem;top:-6rem;background:#142e4c;color:#fff;padding:.7rem;z-index:20}.skip:focus{top:.6rem}:focus-visible{outline:3px solid #b66a00;outline-offset:4px}[hidden]{display:none!important}.task-list{list-style:none;padding:0}.task-list li{padding:.8rem;border-bottom:1px solid #ceddd3;display:flex;align-items:center;gap:.8rem}.task-list label{font-weight:500;margin:0;display:flex;gap:.7rem;align-items:center;flex:1}.task-list .done .task-name{text-decoration:line-through;color:#557067}.task-list button{font-size:.9rem}.outline-mode nav,.outline-mode main,.outline-mode header,.outline-mode footer{outline:2px dashed #b66a00;outline-offset:3px}@media(max-width:640px){.grid{grid-template-columns:1fr}section{padding:1rem}header,main,footer{width:min(100% - 1.2rem,920px)}.downloads .button{width:100%;text-align:center}.task-list li{align-items:flex-start;flex-wrap:wrap}.task-list label{min-width:100%}}@media(prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}</style>
</head>
<body>
<a class="skip" href="#main">跳到主要内容</a>
<header>
<p class="eyebrow">91-WEB · 原创学习示例</p>
<p><a href="https://91-web.cn/">本站学习首页</a> · <a href="#learn">代码讲解</a> · <a href="#download">源文件与许可</a></p>
<h1>任务状态与温和反馈</h1>
<p class="lead">添加和完成任务后,页面既要改变外观,也要给出可读的文字结果。此例用短状态消息说明发生了什么,刷新后恢复初始任务。</p>
<p class="notice">围绕检索词“9.1免费版代码”提供原创练习。“9.1”在本站仅是未确认的检索或版本线索;本示例由 91-web.cn 为网页制作学习编写,不是任何 9.1 产品的官方代码,也不提供未经核验的产品安装包。</p>
</header>
<main id="main" tabindex="-1">
<section class="demo" aria-labelledby="demo-heading">
<h2 id="demo-heading">动手示例</h2>
<fieldset id="task-controls" disabled><legend>添加练习任务</legend><div class="field"><label for="new-task">任务名称</label><input id="new-task" maxlength="60" autocomplete="off" aria-describedby="new-task-help"><p id="new-task-help" class="help">1–60 个字符;最多保留 20 项,不输入隐私数据。</p></div><button id="add-task" type="button">添加任务</button></fieldset>
<p id="progress">完成 0 / 2 项</p>
<ul id="practice-tasks" class="task-list"><li><label><input type="checkbox" disabled><span class="task-name">给页面补充清晰的主标题</span></label><span class="badge task-state">待完成</span></li><li><label><input type="checkbox" disabled><span class="task-name">检查表单 label 与 id</span></label><span class="badge task-state">待完成</span></li></ul>
<p id="task-status" class="status" role="status" aria-live="polite" aria-atomic="true"></p>
<button id="reset-tasks" type="button" class="secondary" disabled>恢复两项初始任务</button>
</section>
<section id="learn" aria-labelledby="learn-heading">
<h2 id="learn-heading">这段代码解决什么问题</h2>
<p>原生复选框表示“完成”这个布尔状态,标签让操作目标明确。完成数、文字徽标和删除按钮名称随任务状态变化;颜色和删除线只是辅助提示。</p><p><code>aria-live="polite"</code> 的状态区域在初始 HTML 就存在,脚本随后更新其中的短句。它通常让辅助技术在合适的间隙报告变化,具体朗读由浏览器和辅助技术组合决定;本例不承诺每种设备都得到相同结果。</p><ul><li>添加时用 createElement 和 textContent,不把任务文字拼成 HTML。</li><li>完成操作保持焦点在复选框;删除后移到邻近任务的复选框,清空时移回输入框。</li><li>无持久存储、无账号、无同步;可用作状态练习,不是团队待办服务。</li><li>任务数量上限和长度限制只用于这个小练习,生产系统仍需相应服务端校验。</li></ul>
<h3>关键片段</h3>
<pre><code>&lt;p id=&quot;task-status&quot; role=&quot;status&quot;
  aria-live=&quot;polite&quot; aria-atomic=&quot;true&quot;&gt;&lt;/p&gt;
// 更新已有状态区域,不移动完成操作的焦点
status.textContent = &quot;已完成任务。当前完成 1 / 2 项。&quot;;
// 用户输入作为文字显示
nameNode.textContent = input.value.trim();</code></pre>
<p class="help">这里展示关键片段;完整 HTML、CSS 和 JavaScript 在下载包及源文件 TXT 中。TXT 保存为 UTF-8 的 .html 后可打开。</p>
</section>
<section id="download" aria-labelledby="download-heading">
<h2 id="download-heading">运行条件与源文件</h2>
<p>解压 ZIP 后,用支持现代 HTML、CSS、JavaScript 的浏览器直接打开 <code>status-feedback.html</code>。无需安装依赖、启动构建工具或连接服务;下载包中的文件没有外部字体、样式、图片、模块导入或网络请求。在线参考链接只有点击后才访问对应网站。</p>
<p>关闭 JavaScript 仍可阅读本文和初始示例内容;动态操作按钮保留禁用状态或静态内容,不表示已运行交互。 本例没有做浏览器兼容性认证或辅助技术实测,学习者可按自己的设备验证键盘操作和朗读结果。</p>
<div class="downloads"><a class="button" href="../downloads/status-feedback.zip" download>下载本例 ZIP</a><a class="button secondary" href="../downloads/status-feedback-source.txt" download>完整源文件 TXT</a><a class="button secondary" href="../downloads/webpage-practice-pack.zip" download>下载五例合集</a></div>
<p>本站原创示例代码使用 MIT License,允许修改和再分发,发布代码副本时须保留版权与许可文本。许可不保证代码适合任何特定生产场景;生产环境需另接合适的服务和测试。<a href="../downloads/LICENSE.txt">查看完整 MIT 许可</a> · <a href="../downloads/status-feedback-README.txt">运行说明</a></p>
</section>
<section aria-labelledby="sources-heading"><h2 id="sources-heading">进一步阅读</h2><p>以下官方资料用于核对语义和 API 行为,本站示例及文字为原创编写。</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-live">MDN:aria-live 状态更新</a>(在线参考,访问需网络)</li><li><a href="https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html">W3C:状态消息的理解</a>(在线参考,访问需网络)</li><li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent">MDN:Node.textContent</a>(在线参考,访问需网络)</li></ul></section>
</main>
<footer><p>91-web.cn 原创网页练习 · 精确检索词:9.1免费版代码</p><p>页面示例只处理当前内存中的学习数据。源文件的 MIT 许可只覆盖本站原创示例代码,不替代链接资料各自的许可。</p></footer>
<script>
// Copyright (c) 2026 91-web.cn contributors — MIT License.
'use strict';
const taskControls = document.getElementById('task-controls');
const taskInput = document.getElementById('new-task');
const addButton = document.getElementById('add-task');
const taskList = document.getElementById('practice-tasks');
const progress = document.getElementById('progress');
const status = document.getElementById('task-status');
const resetButton = document.getElementById('reset-tasks');
const initialNames = ['给页面补充清晰的主标题', '检查表单 label 与 id'];
taskControls.disabled = false; resetButton.disabled = false;
function updateProgress(message) {
  const total = taskList.children.length;
  const done = taskList.querySelectorAll('input:checked').length;
  progress.textContent = '完成 ' + done + ' / ' + total + ' 项';
  if (message) status.textContent = message + ' 当前' + progress.textContent + '。';
}
function addRow(name) {
  const li = document.createElement('li');
  const label = document.createElement('label');
  const checkbox = document.createElement('input');
  checkbox.type = 'checkbox';
  const text = document.createElement('span');
  text.className = 'task-name'; text.textContent = name;
  const state = document.createElement('span');
  state.className = 'badge task-state'; state.textContent = '待完成';
  const remove = document.createElement('button');
  remove.type = 'button'; remove.className = 'secondary'; remove.textContent = '删除';
  remove.setAttribute('aria-label', '删除任务:' + name);
  label.append(checkbox, text); li.append(label, state, remove); taskList.append(li);
  checkbox.addEventListener('change', () => {
    li.classList.toggle('done', checkbox.checked);
    state.textContent = checkbox.checked ? '已完成' : '待完成';
    updateProgress((checkbox.checked ? '已完成:' : '恢复待完成:') + name + '。');
  });
  remove.addEventListener('click', () => {
    const neighbor = li.nextElementSibling || li.previousElementSibling;
    li.remove();
    updateProgress('已删除:' + name + '。');
    if (neighbor) neighbor.querySelector('input').focus(); else taskInput.focus();
  });
}
function restoreTasks(announce) {
  taskList.replaceChildren();
  initialNames.forEach(addRow);
  taskInput.value = '';
  updateProgress(announce ? '已恢复两项初始任务。' : '');
}
function addTask() {
  const name = taskInput.value.trim();
  if (!name || Array.from(name).length > 60) {
    status.textContent = '任务名称需为 1–60 个字符。'; taskInput.focus(); return;
  }
  if (taskList.children.length >= 20) {
    status.textContent = '已达到 20 项上限,请先删除不需要的任务。'; return;
  }
  addRow(name); taskInput.value = ''; updateProgress('已添加:' + name + '。'); taskInput.focus();
}
restoreTasks(false);
addButton.addEventListener('click', addTask);
taskInput.addEventListener('keydown', event => { if (event.key === 'Enter') { event.preventDefault(); addTask(); } });
resetButton.addEventListener('click', () => { restoreTasks(true); taskInput.focus(); });
</script>
</body>
</html>

对应源码指南

指南

9.1免费版代码本地运行

准备纯文本编辑器和现代浏览器,下载本站示例ZIP并解压HTML,或把完整源文件TXT另存为UTF-8的.html文件。这里的9.1免费版代码是检索线索,示例不依赖已确认的9.1产品、账号或安装包。

包含 6 个可复现步骤

阅读指南
指南

9.1免费版代码状态反馈

任务清单用原生复选框表达完成与待完成,添加、删除和恢复时同步更新完成数与文字状态。9.1免费版代码是检索线索;本例只处理当前页面内存,没有进度计时、后台任务或持久保存。

包含 6 个可复现步骤

阅读指南

相关实现比较

网页实现与运行方式比较
比较问题 实际差异 选择建议 阅读详情
静态正文与脚本增强 先写真实HTML再加交互,逐项判断脚本在五个练习中负责什么。 先完成可阅读的静态结构,再为一个清楚的任务添加脚本。练习不需要一开始就引入框架。 查看比较
原生控件与自制交互 比较button、select和table与自制交互组件承担的职责,选择能解释清楚的实现。 用原生元素建立第一份实现,再为确实需要的行为写最小增强。自制组件的外观不能证明其键盘或辅助技术行为。 查看比较

结构图与下载材料

已提供依据 2 个能力分类

课程页的语义导航

这个原创课程介绍页用header、nav、main和footer建立区域,用普通锚点连接“学习目标、练习步骤、参考资料”。源码展示可见焦点与跳到正文链接,页面主体无需脚本即可阅读。

源码标识
NAV-01
技术
HTML / CSS
运行方式
本地浏览器
范围
独立学习示例

本站原创教学源码;练习中的昵称、任务和数据为虚构。编号用于本站组织内容,与9.1产品版本没有对应关系。

已提供依据 2 个能力分类

公开配置校验与导出

以看板标题、每页任务数和默认筛选作为公开设置,校验后生成JSON文件。固定输出四个字段,输入变化会让旧导出失效;源码没有API请求或凭据,练习重点是输入约束与公开参数的边界。

源码标识
CONFIG-05
技术
HTML / CSS / JavaScript
运行方式
本地浏览器
范围
独立学习示例

本站原创教学源码;练习中的昵称、任务和数据为虚构。编号用于本站组织内容,与9.1产品版本没有对应关系。

来源、许可与运行问答

“9.1免费版代码”对应哪个官方产品?

本站没有确认这个检索词指向的厂商或产品。这里的NAV-01、FORM-02等是本站原创网页练习编号,不能当作某个9.1发行版的源代码或官方下载。

源码真的可以免费使用吗?

本站原创练习源码以包内MIT LICENSE提供。你可以复制、修改和使用源码;应保留版权与许可文字。页面正文、第三方文档与商标不自动属于这份源码许可。

第一次如何运行这些示例?

先下载ZIP并解压,再打开其中的HTML文件。五个练习的核心逻辑不需要第三方库;浏览器应支持标准HTML、CSS和基础JavaScript。页面URL使用的站点导航与部署脚本不是离线练习依赖。

数据透明度

来源与核验状态

已提供依据
  • 本站原创HTML源文件 已提供依据
    发布方
    网页练习工坊
    访问日期
    未提供
    支持字段
    完整练习源码
    查看来源
  • MDN:ARIA live regions 已提供依据
    发布方
    MDN Web Docs
    访问日期
    未提供
    支持字段
    网页实现说明
    查看来源