
建站代码怎么写:从 HTML、CSS 到 JavaScript 搭建一个简单网页
想自己做一个网页,通常不必先安装复杂的开发环境,也不必一开始就学习大型框架。一个普通网页可以先由三种基础技术组成:HTML 描述内容和结构,CSS 负责外观与布局,JavaScript 处理交互。把它们放进几个文件,用浏览器打开,就能得到一个可运行的小项目。
本文会从零搭建一个简单的任务清单:用户可以输入任务、添加任务、删除任务,刷新页面后仍能看到已保存的内容。示例不依赖第三方库,适合学习静态网页的基本组成、熟悉浏览器调试,以及制作个人介绍页、产品说明页或轻量交互原型。它不是账号系统、多人协作工具,也不适合保存敏感信息;这些边界会在后文说明。
一、先理解三个部分各自做什么
- HTML:组织标题、段落、表单、按钮和列表等内容。它决定页面有哪些元素,以及这些元素之间的语义关系。
- CSS:控制颜色、字体、间距、宽度和响应式布局。CSS 可以改变视觉呈现,但不会自动实现数据保存或表单业务逻辑。
- JavaScript:响应点击和输入,修改页面内容,并调用浏览器提供的功能。本文用它新增、删除任务,并把数据保存在浏览器本地。
三者之间有明确的边界:HTML 提供基础结构,CSS 美化结构,JavaScript 在结构上增加行为。浏览器会把这几部分一起解析和运行,但把代码拆成文件,通常更方便阅读、修改和排错。
二、准备目录和运行方式
先创建一个文件夹,例如 my-first-site,并在其中新建三个文件:
my-first-site/
├── index.html
├── styles.css
└── app.js
可以使用任意文本编辑器编写这些文件。保存后,直接用浏览器打开 index.html,就能运行这个示例。开发时也可以使用编辑器提供的本地预览功能,或启动本地静态服务器。对于本文用到的普通 HTML、CSS 和本地存储,直接打开文件通常也能工作;但涉及模块导入、请求接口、浏览器权限等功能时,使用本地服务器往往更接近真实网站运行环境。
注意:这里的“本地运行”指页面在你自己的设备上打开,不等于网站已经发布到互联网上。想让其他人访问,需要把文件部署到静态托管服务或自己的服务器,并配置域名等相关内容。
三、编写 HTML:先搭好结构
把下面内容保存为 index.html。它提供一个页面标题、一段说明、一个输入表单和一个任务列表。label 与输入框通过 for 和 id 关联,便于屏幕阅读器识别,也能让用户点击标签时聚焦输入框。defer 会让浏览器先解析 HTML,再执行外部 JavaScript 文件。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>我的任务清单</title>
<link rel="stylesheet" href="styles.css">
<script src="app.js" defer></script>
</head>
<body>
<main class="page">
<header class="intro">
<p class="eyebrow">一个简单的网页示例</p>
<h1>我的任务清单</h1>
<p>写下接下来要做的事,完成后可以删除。</p>
</header>
<section class="panel" aria-labelledby="form-title">
<h2 id="form-title">添加任务</h2>
<form id="task-form">
<label for="task-input">任务内容</label>
<div class="form-row">
<input
id="task-input"
name="task"
type="text"
maxlength="80"
placeholder="例如:整理本周的学习计划"
autocomplete="off"
required
>
<button type="submit">添加</button>
</div>
<p class="hint">最多输入 80 个字符。</p>
</form>
</section>
<section class="panel" aria-labelledby="list-title">
<div class="list-heading">
<h2 id="list-title">待办事项</h2>
<span id="task-count" class="count">0 项</span>
</div>
<p id="empty-message" class="empty-message">还没有任务,添加一项试试。</p>
<ul id="task-list" class="task-list"></ul>
</section>
<footer class="footer">
<p>示例数据保存在当前浏览器中。</p>
</footer>
</main>
</body>
</html>
HTML 属性不是业务逻辑的替代品。这里的 required 和 maxlength 会启用浏览器原生表单校验,方便用户发现明显错误;但真实应用仍应在 JavaScript 中检查输入。若数据会发送到服务器,服务器也必须重新验证,因为客户端校验可以被绕过。
四、编写 CSS:让页面清楚易读
把下面代码保存为 styles.css。这里使用居中的内容区、卡片式面板和简单的窄屏布局。CSS 中的类名与 HTML 中的 class 对应;如果改了类名,记得同步修改相关样式。
* {
box-sizing: border-box;
}
:root {
color-scheme: light;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
color: #20252c;
background: #f3f5f8;
}
body {
margin: 0;
min-width: 320px;
}
button,
input {
font: inherit;
}
.page {
width: min(100% - 32px, 720px);
margin: 0 auto;
padding: 56px 0 32px;
}
.intro {
margin-bottom: 28px;
}
.eyebrow {
margin: 0 0 8px;
color: #52647d;
font-size: 0.9rem;
}
h1 {
margin: 0 0 12px;
font-size: clamp(2rem, 7vw, 3rem);
line-height: 1.15;
}
.intro > p:last-child {
margin: 0;
color: #56606c;
line-height: 1.7;
}
.panel {
margin: 16px 0;
padding: 24px;
border: 1px solid #e0e5ec;
border-radius: 14px;
background: #fff;
box-shadow: 0 5px 18px rgb(30 45 65 / 5%);
}
h2 {
margin: 0 0 18px;
font-size: 1.2rem;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-row {
display: flex;
gap: 10px;
}
input {
min-width: 0;
flex: 1;
padding: 11px 12px;
border: 1px solid #aeb8c5;
border-radius: 8px;
}
input:focus-visible,
button:focus-visible {
outline: 3px solid #8bb8ff;
outline-offset: 2px;
}
button {
padding: 10px 16px;
border: 0;
border-radius: 8px;
color: #fff;
background: #2459b3;
cursor: pointer;
}
button:hover {
background: #19458f;
}
.hint {
margin: 8px 0 0;
color: #657181;
font-size: 0.9rem;
}
.list-heading {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
.list-heading h2 {
margin-bottom: 8px;
}
.count {
color: #657181;
font-size: 0.9rem;
}
.empty-message {
margin: 12px 0 0;
color: #657181;
}
.task-list {
display: grid;
gap: 10px;
margin: 12px 0 0;
padding: 0;
list-style: none;
}
.task-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px;
border: 1px solid #e4e8ee;
border-radius: 8px;
overflow-wrap: anywhere;
}
.task-text {
white-space: pre-wrap;
}
.delete-button {
flex: 0 0 auto;
padding: 7px 10px;
color: #8b2635;
background: #fff0f1;
}
.delete-button:hover {
background: #ffe1e4;
}
.footer {
padding-top: 8px;
color: #657181;
font-size: 0.9rem;
}
@media (max-width: 480px) {
.page {
padding-top: 32px;
}
.panel {
padding: 18px;
}
.form-row {
align-items: stretch;
flex-direction: column;
}
}
box-sizing: border-box 让宽度计算更直观;min() 和 clamp() 是现代 CSS 函数,用来限制内容宽度和标题字号。若需要兼容较旧的浏览器,可以先用普通属性写出基础样式,再按项目目标检查是否需要降级方案。不要假设所有浏览器、设备和用户偏好都完全相同,发布前应在目标环境中实际测试。
五、编写 JavaScript:处理添加、删除和保存
把下面代码保存为 app.js。程序先读取页面上的元素,再尝试从 localStorage 恢复数据。每次添加或删除后会重新绘制列表并保存。示例通过创建文本节点来显示用户输入,不把输入拼接成 HTML。
"use strict";
const STORAGE_KEY = "simple-task-list-v1";
const MAX_TASK_LENGTH = 80;
const form = document.querySelector("#task-form");
const input = document.querySelector("#task-input");
const list = document.querySelector("#task-list");
const count = document.querySelector("#task-count");
const emptyMessage = document.querySelector("#empty-message");
let tasks = loadTasks();
function loadTasks() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved === null) {
return [];
}
const parsed = JSON.parse(saved);
if (!Array.isArray(parsed)) {
return [];
}
return parsed
.filter((item) => typeof item === "string")
.map((item) => item.trim())
.filter((item) => item.length > 0 && item.length <= MAX_TASK_LENGTH);
} catch (error) {
// 存储不可用或内容无法解析时,先以空列表继续运行。
return [];
}
}
function saveTasks() {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
} catch (error) {
// 浏览器可能禁用或限制存储;页面仍可在当前内存中使用。
console.warn("任务未能保存到本地存储。");
}
}
function renderTasks() {
list.replaceChildren();
tasks.forEach((task, index) => {
const item = document.createElement("li");
item.className = "task-item";
const text = document.createElement("span");
text.className = "task-text";
text.textContent = task;
const deleteButton = document.createElement("button");
deleteButton.className = "delete-button";
deleteButton.type = "button";
deleteButton.textContent = "删除";
deleteButton.setAttribute("aria-label", `删除任务:${task}`);
deleteButton.addEventListener("click", () => {
tasks.splice(index, 1);
saveTasks();
renderTasks();
});
item.append(text, deleteButton);
list.append(item);
});
count.textContent = `${tasks.length} 项`;
emptyMessage.hidden = tasks.length > 0;
}
form.addEventListener("submit", (event) => {
event.preventDefault();
const task = input.value.trim();
if (task.length === 0) {
input.focus();
return;
}
if (task.length > MAX_TASK_LENGTH) {
input.setCustomValidity(`任务内容不能超过 ${MAX_TASK_LENGTH} 个字符。`);
input.reportValidity();
input.setCustomValidity("");
return;
}
tasks.push(task);
saveTasks();
renderTasks();
form.reset();
input.focus();
});
renderTasks();
代码中的箭头、比较符号和逻辑运算符在网页源码中需要正确转义,因此在上面的代码展示中,诸如 =>、&& 和 <= 是 HTML 实体;从代码块复制时,若编辑器复制的是页面显示文本,它们会对应 JavaScript 中的 =>、&& 和 <= 符号。实际 app.js 文件应使用 JavaScript 原字符。完整写法如下:代码文件中把比较和箭头运算符写作 >、<、=>,逻辑运算符写作 &&;它们在 HTML 代码片段中显示为实体,是为了避免被网页解析成标签。
如果将示例直接复制到纯文本的 app.js,可将上面的 HTML 实体还原为对应字符。更稳妥的做法是根据这条规则核对:HTML 代码块里的 >、<、=>、&& 分别对应 JavaScript 的 >、<、=>、&&。这样才能保证 JavaScript 文件本身不含 HTML 转义实体。
六、输入限制、存储能力与安全边界
maxlength="80" 会限制输入框可输入的长度,JavaScript 中的 MAX_TASK_LENGTH 则在提交时再次检查。两者是对用户输入的便利限制,不是可靠的安全机制:浏览器端代码可以被修改,页面脚本也可以绕过。若应用把输入发给服务器,应由服务器按业务规则重新校验,并根据数据库和接口设计设置合理的长度、格式和速率限制。
JavaScript 字符串的 length 计算的是 UTF-16 代码单元数量,并不总等于用户感知的“字符数”。部分表情符号或组合字符可能占多个代码单元,因此这里的 80 更准确地说是代码单元上限。对普通短任务通常够用;如果产品要求精确限制可见字符数,需要采用符合该需求的 Unicode 字符分段方案,并明确前后端采用一致规则。
localStorage 属于当前浏览器环境中的客户端存储。它适合保存非敏感的偏好或小型示例数据,但不等于数据库:数据可能被用户清除,也不会自动同步到另一台设备;不同浏览器、配置文件或站点来源之间通常也不能直接共享。可用空间和行为受浏览器实现、用户设置及运行环境影响,不应把某个固定容量当作所有设备都保证支持的上限。写入失败时,示例会在控制台提示,当前页面仍可通过内存中的数组操作,但刷新后未保存的数据可能消失。
不要把密码、访问令牌、支付信息或其他敏感数据放进 localStorage。同一来源下运行的 JavaScript 通常可以读取其中的数据;如果页面出现跨站脚本漏洞,攻击代码也可能访问这些内容。浏览器本地存储不是加密保险箱,也不是身份验证或授权机制。需要账号、云端同步、权限控制、审计或可靠备份时,应设计服务端接口、认证授权与数据存储方案,而不是单靠静态页面和本地存储。
示例使用 textContent 写入用户输入,这是一个重要的安全习惯。不要把用户输入直接拼进 innerHTML,例如把输入当作 HTML 字符串插入页面;恶意内容可能被浏览器解释并执行。若确实需要展示富文本,应采用经过审查的消毒方案,并明确允许的标签和属性。仅仅做字符串替换通常不足以构成可靠的 HTML 消毒。
七、常见问题与排查顺序
- 页面没有样式:检查
styles.css是否与index.html位于同一目录,文件名和大小写是否完全一致。浏览器开发者工具的网络面板可以查看 CSS 是否加载成功。 - 按钮点击没有反应:检查
app.js路径、浏览器控制台错误,以及 HTML 中的id是否与 JavaScript 查询的选择器一致。一个拼错的task-form就会让事件绑定失效。 - 脚本报语法错误:确认 JavaScript 文件中使用的是原始运算符,而不是展示用的 HTML 实体;例如代码文件里应写
&&字符本身,而不是文本&&。 - 刷新后数据不见:确认浏览器允许该页面使用本地存储。也要注意,不同浏览器配置、浏览器隐私设置和页面来源可能对应不同的存储空间。清除浏览数据会移除本地保存的任务。
- 窄屏上内容溢出:检查输入框、按钮和长文本的宽度,避免给弹性布局中的子元素设置不合理的固定宽度。示例的
min-width: 0与overflow-wrap: anywhere用来缓解常见溢出情况,但仍应在目标设备上测试。 - 改了 HTML 后样式或功能消失:检查元素的类名、ID、标签嵌套是否仍与 CSS 和 JavaScript 相匹配。开发者工具的元素面板可以查看浏览器最终生成的 DOM。
遇到问题时,建议依次检查文件路径、浏览器控制台、元素 ID 和选择器,而不是一次改动很多地方。开发者工具通常能直接指出脚本文件加载失败、语法错误或未找到对应元素的位置。
八、可以怎样继续扩展
在这个基础上,可以尝试增加完成状态、任务筛选、清空列表或日期字段。每增加一个功能,都先明确数据结构,再决定 HTML 需要哪些控件、CSS 需要哪些状态、JavaScript 如何更新数据。若任务不再只是字符串,而是还要保存完成状态和创建时间,数据可以改为对象数组,例如每项包含 text、done 和 createdAt;同时要考虑旧版本地数据如何兼容或清理。
也可以进一步学习语义化 HTML、CSS Grid 与 Flexbox、事件处理、表单可访问性、HTTP 请求和服务端开发。框架并不是基础网页的必要条件;当页面复杂到需要组件复用、路由、团队协作或自动化构建时,再选择合适的工具更容易判断其价值。
总结
一个简单网页可以从三个文件开始:HTML 负责结构,CSS 负责呈现,JavaScript 负责交互。本文的任务清单示例能在浏览器中直接运行,也展示了表单校验、本地存储和安全输出的基本做法。需要牢记的是,客户端限制可被绕过,本地存储不适合敏感数据,用户输入也不应未经处理地作为 HTML 插入。理解这些边界,比单纯把页面“跑起来”更重要。









