CONFIG-05 · 原创网页练习

公开配置校验与导出

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

已提供依据
公开配置校验与导出字段说明
本站编号 CONFIG-05 仅用于组织原创练习,与9.1产品版本无对应关系。
源文件 config-boundary.html
结构与行为 JavaScript增强
练习系列 本页状态练习
读者路径 首次制作网页
运行条件 现代浏览器支持JSON、Blob及对象URL下载。 仅操作展示设置,导出文件写入用户选定的下载位置。
数据范围 虚构练习数据;当前页面内存 不联网提交,不包含账号、API或真实预约。
源码许可 MIT 复制或修改源码时保留版权与许可文字。

打开、阅读与下载

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

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

这份练习适合谁

适合

  • 适合学习JSON结构、字段白名单和范围判断。
  • 适合区分公开展示参数与后端凭据。

不适合

  • 不支持生产环境的权限或秘密管理。
  • 不将用户配置发送到任何API。

运行前要知道什么

  • 现代浏览器支持JSON、Blob及对象URL下载。
  • 仅操作展示设置,导出文件写入用户选定的下载位置。

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

这次练习观察三件事

  1. 先用默认输入生成JSON,阅读四个字段。
  2. 尝试空标题、超出1–50范围或含小数的页长。
  3. 修改一个输入,观察旧导出失效,重新校验后再下载。

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

完整原创源文件

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

展开 config-boundary.html 完整源码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>公开配置检验与 JSON 导出|9.1免费版代码</title>
<meta name="description" content="9.1免费版代码:原创单文件示例检验学习看板的公开配置并导出 JSON,说明前端秘密边界,不连接 API,不读取密钥,MIT 源码可下载。">
<meta name="keywords" content="9.1免费版代码,原创网页示例,HTML,CSS,JavaScript">
<link rel="canonical" href="https://91-web.cn/examples/config-boundary.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>公开配置检验与 JSON 导出</h1>
<p class="lead">配置文件应把“可公开的界面参数”和“需要保密的服务凭据”分开。此例只接受看板标题、页容量和默认筛选,检验后生成 JSON。</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>
<p class="notice">不要在本页输入 API 密钥、密码、令牌或真实服务凭据。浏览器可以读取前端下载到的源码与公开配置,隐藏输入框或压缩代码不构成秘密存储。</p>
<fieldset id="config-controls" disabled><legend>公开的学习看板参数</legend>
<div class="field"><label for="board-title">看板标题</label><input id="board-title" value="网页练习看板" maxlength="40" aria-describedby="title-help"><p id="title-help" class="help">1–40 个字符,仅作为界面文字。</p></div>
<div class="grid"><div class="field"><label for="page-size">每页任务数</label><input id="page-size" type="number" min="1" max="50" step="1" value="10"><p class="help">1–50 的整数,不代表后端限制。</p></div><div class="field"><label for="default-filter">默认筛选</label><select id="default-filter"><option value="all">全部</option><option value="pending">待完成</option><option value="done">已完成</option></select></div></div>
<div class="row"><button id="validate-config" type="button">检验并生成 JSON</button><button id="export-config" type="button" class="secondary" disabled>导出已检验 JSON</button></div></fieldset>
<div id="config-errors" class="summary" tabindex="-1" hidden><h3>配置尚未通过检验</h3><ul id="config-error-list"></ul></div>
<p id="config-status" class="status" role="status" aria-live="polite" aria-atomic="true"></p>
<h3>输出预览</h3><pre><code id="json-preview">尚未生成配置;请先检验公开参数。</code></pre>
</section>
<section id="learn" aria-labelledby="learn-heading">
<h2 id="learn-heading">这段代码解决什么问题</h2>
<p>这份 JSON 只是学习看板的公开界面配置,固定包含 <code>schemaVersion</code>、<code>boardTitle</code>、<code>pageSize</code>、<code>defaultFilter</code>。其中 schemaVersion 的 1 是本站数据结构示例编号,与检索词的“9.1”没有产品对应关系。</p><p>输入变化会立即让旧导出失效,避免把已经改过的字段误当成已检验结果。通过后,脚本使用 Blob 和对象 URL 将 JSON 交给浏览器下载;导出过程中没有发送到服务器。</p><ul><li>范围检验只说明本例允许的界面参数,不验证 API 存活、用户权限或远端配置。</li><li>公开环境变量、前端构建输出、URL 参数都可能被用户读取;需要保密的服务凭据应放在合适的服务端环境。</li><li>如果以后增加接口地址,客户端输入检验仍不能替代服务端授权和访问控制。</li><li>下载文件是否弹出或直接保存由浏览器设置决定;对象 URL 在使用后释放。</li></ul>
<h3>关键片段</h3>
<pre><code>const config = {
  schemaVersion: 1,
  boardTitle: titleInput.value.trim(),
  pageSize: Number(sizeInput.value),
  defaultFilter: filterInput.value
};
const json = JSON.stringify(config, null, 2);
const blob = new Blob([json + &quot;\n&quot;], { type: &quot;application/json;charset=utf-8&quot; });
// 生成本地对象 URL,不发出 API 请求
const url = URL.createObjectURL(blob);</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>config-boundary.html</code>。无需安装依赖、启动构建工具或连接服务;下载包中的文件没有外部字体、样式、图片、模块导入或网络请求。在线参考链接只有点击后才访问对应网站。</p>
<p>关闭 JavaScript 仍可阅读本文和初始示例内容;动态操作按钮保留禁用状态或静态内容,不表示已运行交互。 本例没有做浏览器兼容性认证或辅助技术实测,学习者可按自己的设备验证键盘操作和朗读结果。</p>
<div class="downloads"><a class="button" href="../downloads/config-boundary.zip" download>下载本例 ZIP</a><a class="button secondary" href="../downloads/config-boundary-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/config-boundary-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/API/Blob">MDN:Blob 文件数据对象</a>(在线参考,访问需网络)</li><li><a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static">MDN:URL.createObjectURL</a>(在线参考,访问需网络)</li><li><a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Website_security">MDN:网站安全与客户端边界</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 controls = document.getElementById('config-controls');
const titleInput = document.getElementById('board-title');
const sizeInput = document.getElementById('page-size');
const filterInput = document.getElementById('default-filter');
const validateButton = document.getElementById('validate-config');
const exportButton = document.getElementById('export-config');
const errorsBox = document.getElementById('config-errors');
const errorsList = document.getElementById('config-error-list');
const status = document.getElementById('config-status');
const preview = document.getElementById('json-preview');
let validatedJson = null;
controls.disabled = false;
function invalidate() {
  validatedJson = null; exportButton.disabled = true;
  errorsBox.hidden = true; errorsList.replaceChildren();
  [titleInput, sizeInput, filterInput].forEach(field => field.removeAttribute('aria-invalid'));
  preview.textContent = '参数已变化;请重新检验配置。';
  status.textContent = '旧导出已失效。';
}
[titleInput, sizeInput, filterInput].forEach(field => field.addEventListener('input', invalidate));
filterInput.addEventListener('change', invalidate);
validateButton.addEventListener('click', () => {
  errorsList.replaceChildren(); errorsBox.hidden = true;
  [titleInput, sizeInput, filterInput].forEach(field => field.removeAttribute('aria-invalid'));
  const title = titleInput.value.trim();
  const size = Number(sizeInput.value);
  const filter = filterInput.value;
  const errors = [];
  if (Array.from(title).length < 1 || Array.from(title).length > 40) errors.push([titleInput, '看板标题需为 1–40 个字符。']);
  if (sizeInput.value.trim() === '' || !Number.isInteger(size) || size < 1 || size > 50) errors.push([sizeInput, '每页任务数需为 1–50 的整数。']);
  if (!['all', 'pending', 'done'].includes(filter)) errors.push([filterInput, '默认筛选需为 all、pending 或 done。']);
  if (errors.length) {
    validatedJson = null; exportButton.disabled = true;
    for (const [field, message] of errors) {
      field.setAttribute('aria-invalid', 'true');
      const li = document.createElement('li'); const link = document.createElement('a');
      link.href = '#' + field.id; link.textContent = message;
      link.addEventListener('click', event => { event.preventDefault(); field.focus(); });
      li.append(link); errorsList.append(li);
    }
    errorsBox.hidden = false; errorsBox.focus();
    preview.textContent = '配置检验未通过,尚无可导出的 JSON。';
    status.textContent = '有 ' + errors.length + ' 项配置需要修正。'; return;
  }
  validatedJson = JSON.stringify({schemaVersion: 1, boardTitle: title, pageSize: size, defaultFilter: filter}, null, 2);
  preview.textContent = validatedJson; exportButton.disabled = false;
  status.textContent = '公开配置已通过本例检验,可以导出;没有连接 API。';
});
exportButton.addEventListener('click', () => {
  if (validatedJson === null) return;
  const blob = new Blob([validatedJson + '\n'], {type: 'application/json;charset=utf-8'});
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url; link.download = 'learning-board-config.json';
  document.body.append(link); link.click(); link.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
  status.textContent = '已交给浏览器下载 JSON;没有发送到服务器。';
});
</script>
</body>
</html>

对应源码指南

指南

9.1免费版代码本地运行

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

包含 6 个可复现步骤

阅读指南
指南

9.1免费版代码配置边界

本例检验三个公开字段:看板标题1–40字符、每页任务数1–50整数、默认筛选all、pending或done;通过后生成含schemaVersion: 1的JSON。9.1免费版代码是检索线索,修改这些参数不会接通API或取得产品权限。

包含 6 个可复现步骤

阅读指南

相关实现比较

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

结构图与下载材料

已提供依据 2 个能力分类

课程页的语义导航

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

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

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

已提供依据 2 个能力分类

学习计划表单

用昵称、学习主题和时间选择做一份本地学习计划。label明确关联控件,错误说明邻接输入,JavaScript增强生成结果;不会预约、保存到服务器或要求提供真实联系方式。

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

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

来源、许可与运行问答

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

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

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

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

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

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

数据透明度

来源与核验状态

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