HTML、CSS 与 JavaScript 入门:制作可交互的网页
现代网站由三种协同工作的技术构成。HTML 定义内容的结构,CSS 定义外观和布局,JavaScript 则让页面对用户的操作作出反应,例如点击按钮、输入文字或切换标签。本文将制作一个把三者结合在一起的小示例,说明每一部分的作用,以及为什么要把它们的职责分开。
理解每种语言的职责
可以把盖房子作为比喻。HTML 是框架和墙体,决定客厅、厨房和门在哪里。CSS 是油漆、材料和家具的摆放,让房子住起来舒适。JavaScript 则是电路和开关:按下开关灯就亮,打开门就响起铃声。
把这些职责分开非常有用。要修改整个网站的颜色,只需编辑 CSS,不必改动结构。要添加交互功能,可以编写 JavaScript,而无需改变外观。这样代码更易读,也更便于长期维护。
项目结构
新建一个名为 `interactive-demo` 的文件夹,其中包含三个文件。
- `index.html`:结构
- `style.css`:样式
- `script.js`:交互行为
三个文件放在同一文件夹中,HTML 中的引用就无需写完整地址。
第一步:HTML 结构
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>可交互计分器</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="card">
<h1>计分器</h1>
<p id="score-text">当前分数:<span id="score">0</span></p>
<div class="buttons">
<button id="add-btn" type="button">+1 分</button>
<button id="reset-btn" type="button">重置</button>
</div>
<p id="message" aria-live="polite"></p>
</main>
<script src="script.js"></script>
</body>
</html>
这段代码包含三组关键元素:带有 `id="score"` 的分数数字、各自拥有独立 `id` 的两个按钮,以及用于显示提示的段落 `id="message"`。属性 `aria-live="polite"` 告诉屏幕阅读器在内容变化时朗读新文字,使看不见屏幕的用户能和其他人获得相同的信息。
把 `<script>` 放在 `<body>` 末尾,意味着 JavaScript 会在页面所有元素加载完成后才开始运行,因此可以通过 `id` 找到元素而不会出错。
第二步:用 CSS 美化
:root {
--bg: #f0f4fb;
--card: #ffffff;
--accent: #2255aa;
--text: #1a1a1a;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg);
color: var(--text);
font-family: system-ui, sans-serif;
}
.card {
background: var(--card);
padding: 32px;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
text-align: center;
width: min(90vw, 360px);
}
.buttons {
display: flex;
gap: 12px;
justify-content: center;
}
button {
padding: 10px 18px;
border: none;
border-radius: 8px;
background: var(--accent);
color: #fff;
font-size: 16px;
cursor: pointer;
transition: transform 0.1s ease, opacity 0.2s ease;
}
button:hover {
opacity: 0.9;
}
button:active {
transform: scale(0.96);
}
#reset-btn {
background: #8a94a8;
}
逐条看看这些规则:
- `:root` 用来保存颜色变量。要修改网站的主色,只需改一行。变量以 `var(--名称)` 的形式使用。
- `body` 使用 Flexbox 让卡片在水平和垂直方向都居中。`min-height: 100vh` 使布局占满整个屏幕高度。
- `.card` 以点号开头,因为它是 class,可以被多个元素共享。`width: min(90vw, 360px)` 使卡片在大屏上不会过宽,同时在手机上也不会溢出。
- `button:hover` 和 `button:active` 描述鼠标悬停和按下时的状态,让用户感觉到按钮在响应。
- `#reset-btn` 以 `#` 开头,因为它指向 id,而 id 只标识一个元素。
class 与 id 的区别很重要。class 用于把具有相同样式的元素归为一组,id 则用于精确指向某一个元素。
第三步:用 JavaScript 让页面动起来
const scoreEl = document.getElementById("score");
const addBtn = document.getElementById("add-btn");
const resetBtn = document.getElementById("reset-btn");
const messageEl = document.getElementById("message");
let score = 0;
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "太棒了!你已经达到 10 分。";
} else {
messageEl.textContent = "";
}
}
addBtn.addEventListener("click", function () {
score = score + 1;
render();
});
resetBtn.addEventListener("click", function () {
score = 0;
render();
});
render();
这个文件把工作划分为清晰的部分。
1. `document.getElementById` 通过 id 在 HTML 中找到元素,并保存到变量中。在开始时只查找一次,代码会更快、更易读。
2. `let score = 0` 保存程序的状态。这个值是整个程序共用的唯一可信来源。
3. 函数 `render` 读取 `score`,并让屏幕与该值保持一致。这称为“根据状态渲染”。我们不是在每次操作时直接修改屏幕上的文字,而是修改数值,然后由渲染函数刷新显示。
4. `addEventListener("click", ...)` 把函数绑定到点击事件上。每当用户点击按钮,该函数就会执行。
5. 文件末尾调用的 `render()` 使页面一加载就显示初始分数 0。
动手试试:每按一次 +1,数字就增加;达到 10 时显示提示;按重置则回到零。
第四步:让代码更健壮
这个示例能正常工作,但以下几种习惯会让代码更加可靠。
- **检查元素是否存在:** 如果 id 拼错,`getElementById` 会返回 `null`,代码在使用时就会出错。在较大的项目中,使用前应先检查。
- **默认使用 `const`:** 只有值确实需要改变时才使用 `let`,例如 `score`。
- **保持函数职责单一:** 每个函数只做一件事。`render` 只负责显示状态,不自己计算分数。
安全检查的示例:
if (!scoreEl) {
console.error("HTML 中找不到 #score 元素");
}
`console.error` 会把信息显示在浏览器的 Console 标签页中(按 F12 打开),便于快速定位问题。
CSS 还是 JavaScript?一个简单的判断标准
初学者有时不确定某个效果应由哪种语言实现。一个简单的标准是:如果只是纯粹的视觉变化,并且不依赖用户做了什么,就使用 CSS,例如鼠标悬停时改变颜色,或元素之间的间距。如果行为取决于数据或操作,例如统计点击次数,或保存输入的值,就使用 JavaScript。
例如,鼠标悬停时改变按钮颜色可以用 CSS 的 `:hover` 完成,但统计按钮被按了多少次则需要 JavaScript。
用 class 切换外观
假设希望分数达到 10 时卡片改变颜色。与其在 JavaScript 中直接写样式,不如用 JavaScript 添加一个 class,然后在 CSS 中定义外观。
.card.success {
background: #e6f7ec;
border: 2px solid #2e9e5b;
}
const cardEl = document.querySelector(".card");
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "太棒了!你已经达到 10 分。";
cardEl.classList.add("success");
} else {
messageEl.textContent = "";
cardEl.classList.remove("success");
}
}
这种做法让职责更加清晰。JavaScript 负责声明“当前状态是成功”,CSS 负责决定成功时的样子。你可以在不修改 JavaScript 的情况下重新设计外观。
额外示例:简单的标签菜单
这个例子根据点击内容切换显示,是许多真实网站中常见的模式。
<nav>
<button class="tab-btn active" data-tab="home">首页</button>
<button class="tab-btn" data-tab="about">关于</button>
</nav>
<section class="tab-panel active" id="home">欢迎来到我的网站。</section>
<section class="tab-panel" id="about">我是一名正在学习网页制作的学生。</section>
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.tab-btn.active { background: #2255aa; color: #fff; }
const buttons = document.querySelectorAll(".tab-btn");
const panels = document.querySelectorAll(".tab-panel");
buttons.forEach(function (btn) {
btn.addEventListener("click", function () {
const target = btn.dataset.tab;
buttons.forEach(b => b.classList.remove("active"));
panels.forEach(p => p.classList.remove("active"));
btn.classList.add("active");
document.getElementById(target).classList.add("active");
});
});
`querySelectorAll` 会返回所有匹配选择器的元素列表,`forEach` 则遍历该列表,为每个按钮绑定点击处理函数。`btn.dataset.tab` 读取 `data-tab` 属性的值,因此一个函数就能服务所有按钮,而无需为每个按钮编写单独的函数。
JavaScript 常见错误
- **在 HTML 加载完成前使用元素:** 解决办法是把 `<script>` 放在 `<body>` 末尾,或在 script 标签上添加 `defer`。
- **漏写括号或引号:** 检查每个括号是否成对,出现错误时查看浏览器的 Console。
- **用 `=` 代替 `===` 进行比较:** 要严格比较数值,请使用 `===`。
- **在循环中反复更新页面:** 尽量先汇总变化,再一次性更新,使页面更流畅。
给初学者的建议
- 先用 HTML 编写结构,并确认在没有 CSS 和 JavaScript 时页面仍然可读。
- 分小步添加 CSS,每次修改后在浏览器中查看效果。
- 每次只添加一个 JavaScript 函数,测试通过后再继续。
- 使用 Console 查找问题,并先读完整的错误信息再修改代码。
- 通过制作自己的小项目来练习,例如计数器、时钟、计算器或待办清单。
保存与运行方法
1. 新建一个名为 `interactive-demo` 的文件夹。
2. 在该文件夹中创建 `index.html`、`style.css` 和 `script.js` 三个文件,并把每一步的代码粘贴进去。
3. 以 UTF-8 编码保存文件。在记事本中选择“另存为”,并将编码设为 UTF-8,否则中文可能显示为乱码。
4. 检查文件名。在 Windows 上先打开“显示文件扩展名”,避免出现 `script.js.txt` 这样的错误文件名。
5. 双击 `index.html`,或把它拖进浏览器窗口。
6. 如果使用 VS Code,请安装 Live Server 扩展,右键点击 `index.html`,选择“Open with Live Server”。之后每次保存页面都会自动刷新。
实际运行的结果
以下结果来自在真实浏览器中运行计分器,并按顺序点击按钮的测试。测试使用的是泰语版代码,因此下面的提示信息是泰语。
- **开始时:** 分数 0,没有提示,卡片为白色。
- **连续点击 +1 九次:** 分数 9,没有提示,卡片仍为白色。
- **第十次点击 +1:** 分数 10,出现提示“ยอดเยี่ยม! คุณทำได้ถึง 10 คะแนนแล้ว”,同时 JavaScript 为卡片添加 `success` 类,卡片变成绿色。
- **点击重置:** 分数回到 0,提示消失,JavaScript 移除 `success` 类,卡片恢复白色。
标签菜单示例也在浏览器中测试过。从首页开始,点击“เกี่ยวกับ(关于)”会显示关于面板并高亮该按钮,再点击“หน้าแรก(首页)”则回到首页面板。测试中的文字同样是泰语,但交互行为在任何语言下都相同。
下一步
熟悉这三种语言后,你可以继续探索:用于高级布局的 CSS Flexbox 和 Grid、用于从服务器加载数据的 `fetch` 与 JSON、用于管理代码的 Git,以及项目变大后的 React 等框架。
结语
HTML 提供结构,CSS 提供外观,JavaScript 提供交互。把这三种职责分开,代码就更容易理解、修改和扩展。从计分器或标签菜单这样的小示例开始,然后逐步增加复杂度。每天亲手编写和测试代码,进步会比只阅读快得多。