HTML、CSS 与 JavaScript 入门:制作可交互的网页

现代网站由三种协同工作的技术构成。HTML 定义内容的结构,CSS 定义外观和布局,JavaScript 则让页面对用户的操作作出反应,例如点击按钮、输入文字或切换标签。本文将制作一个把三者结合在一起的小示例,说明每一部分的作用,以及为什么要把它们的职责分开。

理解每种语言的职责

可以把盖房子作为比喻。HTML 是框架和墙体,决定客厅、厨房和门在哪里。CSS 是油漆、材料和家具的摆放,让房子住起来舒适。JavaScript 则是电路和开关:按下开关灯就亮,打开门就响起铃声。

把这些职责分开非常有用。要修改整个网站的颜色,只需编辑 CSS,不必改动结构。要添加交互功能,可以编写 JavaScript,而无需改变外观。这样代码更易读,也更便于长期维护。

项目结构

新建一个名为 `interactive-demo` 的文件夹,其中包含三个文件。

三个文件放在同一文件夹中,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;
}

逐条看看这些规则:

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 时显示提示;按重置则回到零。

第四步:让代码更健壮

这个示例能正常工作,但以下几种习惯会让代码更加可靠。

安全检查的示例:

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 常见错误

给初学者的建议

保存与运行方法

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”。之后每次保存页面都会自动刷新。

实际运行的结果

以下结果来自在真实浏览器中运行计分器,并按顺序点击按钮的测试。测试使用的是泰语版代码,因此下面的提示信息是泰语。

标签菜单示例也在浏览器中测试过。从首页开始,点击“เกี่ยวกับ(关于)”会显示关于面板并高亮该按钮,再点击“หน้าแรก(首页)”则回到首页面板。测试中的文字同样是泰语,但交互行为在任何语言下都相同。

下一步

熟悉这三种语言后,你可以继续探索:用于高级布局的 CSS Flexbox 和 Grid、用于从服务器加载数据的 `fetch` 与 JSON、用于管理代码的 Git,以及项目变大后的 React 等框架。

结语

HTML 提供结构,CSS 提供外观,JavaScript 提供交互。把这三种职责分开,代码就更容易理解、修改和扩展。从计分器或标签菜单这样的小示例开始,然后逐步增加复杂度。每天亲手编写和测试代码,进步会比只阅读快得多。