零基础用JavaScript做网站:从HTML/CSS到原生JS交互

作者:页面旅途时间:2026-11-14 14:48:15浏览:643

面向零基础读者的原生JavaScript建站教程:从本地目录准备、HTML语义骨架、CSS布局与状态类到原生JS事件监听与DOM更新,通过完整可运行示例掌握网页制作流程。

零基础用JavaScript做网站:从HTML/CSS到原生JS交互
本地三文件协作示意:HTML 提供骨架,CSS 控制外观,原生 JS 负责读取操作并更新界面

零基础用 JavaScript 做网站,不需要先搭建复杂的开发环境,也不需要提前学习前端框架。只要一个纯文本编辑器和浏览器,就能从零写出一个可点击、可输入、可实时更新内容的网页。全过程按职责分成三步:先用 HTML 搭出网页骨架,再用 CSS 安排版面与状态样式,最后用浏览器自带的原生 JavaScript 监听用户操作并修改页面节点。下面通过一个本地可运行的“今日待办清单”页面,完整走一遍从空白文件夹到动态交互的过程。

一、准备本地文件与理解三层分工

网页本质上是一组按规范编写的文本文件。浏览器打开入口文件后,会自上而下读取内容,并把结构、样式和行为分别交给对应文件处理。

开始编码前,先在电脑桌面或常用文档目录下新建一个文件夹,命名为 my-first-site,在其中创建三个空白文本文件,注意文件名和扩展名必须完全一致:

  • index.html:存放网页的文字、输入框、按钮和列表结构。
  • style.css:控制页面居中、字体、边框以及“已完成”“深色模式”的样式规则。
  • app.js:编写原生 JavaScript 代码,处理添加任务、切换完成状态、删除条目和主题切换。

编辑器推荐使用 VS Code,或者系统自带的记事本(保存时编码选择 UTF-8,避免出现 .txt 隐藏后缀)。双击 index.html 即可用 Chrome、Edge 或 Safari 打开;在页面中按 F12(macOS 上为 Command + Option + I)并切换到 Console(控制台) 标签页,就能看到后续 JS 运行时是否报错。

零基础用JavaScript做网站:从HTML/CSS到原生JS交互
HTML、CSS 与原生 JS 的核心纽带是 DOM 树:JS 修改 DOM 节点或类名后,浏览器自动刷新画面

二、第一步:用 HTML 搭出网页骨架

写 HTML 时不要急着考虑颜色或圆角,先把页面里“有什么元素”表达清楚。在这个待办清单中,页面包含四块内容:顶部标题与主题切换按钮、输入新任务的表单、未完成数量提示,以及任务列表容器。

把下面的代码完整写入 my-first-site/index.html 并保存:

my-first-site/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>我的待办清单</title>
  <link rel="stylesheet" href="style.css">
  <script src="app.js" defer></script>
</head>
<body>
  <main class="card">
    <header class="card-header">
      <h1>今日待办清单</h1>
      <button id="theme-toggle" type="button" class="btn-secondary">切换深色</button>
    </header>

    <form id="task-form" class="task-form">
      <label for="task-input" class="sr-only">输入新任务</label>
      <input id="task-input" type="text" placeholder="输入一项任务,例如:写完首页 HTML" autocomplete="off">
      <button type="submit" class="btn-primary">添加</button>
    </form>

    <p id="task-count" class="task-count">剩余 1 项未完成</p>

    <ul id="task-list" class="task-list">
      <li class="task-item">
        <span class="task-text">认识 HTML、CSS 与原生 JS 的分工</span>
        <button type="button" class="btn-delete">删除</button>
      </li>
    </ul>
  </main>
</body>
</html>

这里有三处细节是为后续 CSS 和 JS 提前留好的接口:

  • id 与 class 属性:id(如 task-form、task-input、task-list)用于在 JS 中唯一定位某个元素;class(如 task-item、btn-delete)既用来统一设置样式,也方便 JS 识别同一类可重复元素。
  • 按钮的 type 属性:表单外的按钮或列表里的“删除”按钮显式写上 type="button",只有提交表单的“添加”按钮用 type="submit"。因为 <button> 默认行为就是提交表单,不标明类型容易在点击时意外触发页面提交。
  • <script src="app.js" defer>:defer 告诉浏览器一边继续解析后面的 HTML,一边下载脚本,等整个文档解析完成后再执行 app.js,避免脚本运行时找不到页面元素。

此时用浏览器打开 index.html,应当能看到左上角对齐的标题、输入框、按钮和一条默认列表项。画面还很朴素,点击“添加”会发现地址栏末尾多了一个问号并刷新页面——这正是浏览器默认表单提交行为,后面会用 JS 接管它。

三、第二步:用 CSS 安排版面与状态样式

CSS 的作用不只是让页面居中、加圆角,更重要的是提前定义好状态类。当某个任务被标记为完成,或者页面切到深色模式时,JavaScript 不需要逐行修改几十个颜色属性,只需要给元素加上或去掉一个类名(例如 is-done 或 is-dark),样式就会自动生效。

把下面的代码写入 my-first-site/style.css 并保存:

my-first-site/style.css
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 40px 16px;
  font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
  background-color: #f5f5f2;
  color: #2c2f2d;
  transition: background-color 0.2s, color 0.2s;
}

body.is-dark {
  background-color: #1f2220;
  color: #e8e8e4;
}

.card {
  max-width: 520px;
  margin: 0 auto;
  padding: 24px;
  background-color: #ffffff;
  border: 1px solid #dcdcd5;
  border-radius: 10px;
}

body.is-dark .card {
  background-color: #2a2e2b;
  border-color: #404541;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 18px;
}

.card-header h1 {
  margin: 0;
  font-size: 20px;
}

.task-form {
  display: flex;
  gap: 8px;
}

.task-form input {
  flex: 1;
  padding: 10px 12px;
  font-size: 14px;
  border: 1px solid #c9c9c0;
  border-radius: 6px;
  background-color: #ffffff;
  color: inherit;
}

body.is-dark .task-form input {
  background-color: #1f2220;
  border-color: #4b504c;
}

button {
  padding: 9px 14px;
  font-size: 14px;
  border-radius: 6px;
  border: 1px solid transparent;
  cursor: pointer;
}

.btn-primary {
  background-color: #464b48;
  color: #ffffff;
}

.btn-secondary {
  background-color: transparent;
  border-color: #c9c9c0;
  color: inherit;
}

.task-count {
  margin: 14px 0 10px;
  font-size: 13px;
  color: #6a706c;
}

body.is-dark .task-count {
  color: #a9afa9;
}

.task-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.task-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-top: 1px solid #ecece6;
}

body.is-dark .task-item {
  border-top-color: #3a3f3b;
}

.task-text {
  cursor: pointer;
  flex: 1;
  margin-right: 12px;
}

.task-item.is-done .task-text {
  text-decoration: line-through;
  color: #8c918d;
}

.btn-delete {
  padding: 4px 10px;
  font-size: 12px;
  background-color: transparent;
  border-color: #d6d6ce;
  color: inherit;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

保存后刷新浏览器,应当能看到一个居中对齐的白色圆角卡片,输入框占满剩余宽度,“添加”按钮靠右,列表项呈水平两端对齐。其中 body.is-dark 和 .task-item.is-done 这两条规则现在还没有被触发,接下来就用原生 JavaScript 去控制它们。

四、第三步:用原生 JS 获取元素并实现交互

几乎所有原生 JS 页面交互都遵循同一条主线:选中元素(querySelector) → 监听事件(addEventListener) → 更新 DOM 或类名。下面以最核心的“提交表单添加任务”为例,先看完整的四步执行流程。

零基础用JavaScript做网站:从HTML/CSS到原生JS交互
按“阻止默认刷新 → 校验输入 → 创建并插入节点 → 同步状态”的顺序编写交互逻辑

在 my-first-site/app.js 中写入以下完整代码。除了添加任务,它还实现了点击文字切换完成状态、点击“删除”移除条目,以及点击右上角按钮切换深浅色主题:

my-first-site/app.js
// 1. 获取页面上需要操作的 DOM 元素
const taskForm = document.querySelector('#task-form');
const taskInput = document.querySelector('#task-input');
const taskList = document.querySelector('#task-list');
const taskCount = document.querySelector('#task-count');
const themeToggle = document.querySelector('#theme-toggle');

// 2. 统计并更新“剩余未完成”数量
function updateCount() {
  const remaining = taskList.querySelectorAll('.task-item:not(.is-done)').length;
  taskCount.textContent = `剩余 ${remaining} 项未完成`;
}

// 3. 构造一条新的任务列表节点
function createTaskItem(text) {
  const li = document.createElement('li');
  li.className = 'task-item';

  const span = document.createElement('span');
  span.className = 'task-text';
  span.textContent = text;

  const deleteBtn = document.createElement('button');
  deleteBtn.type = 'button';
  deleteBtn.className = 'btn-delete';
  deleteBtn.textContent = '删除';

  li.append(span, deleteBtn);
  return li;
}

// 4. 监听表单提交:添加任务(点击“添加”按钮或按回车都会触发)
taskForm.addEventListener('submit', (event) => {
  event.preventDefault();

  const text = taskInput.value.trim();
  if (text === '') {
    taskInput.focus();
    return;
  }

  const newItem = createTaskItem(text);
  taskList.append(newItem);

  taskInput.value = '';
  taskInput.focus();
  updateCount();
});

// 5. 事件委托:在父容器 ul 上统一处理“删除”和“标记完成”
taskList.addEventListener('click', (event) => {
  const deleteBtn = event.target.closest('.btn-delete');
  if (deleteBtn) {
    const item = deleteBtn.closest('.task-item');
    item.remove();
    updateCount();
    return;
  }

  const taskText = event.target.closest('.task-text');
  if (taskText) {
    const item = taskText.closest('.task-item');
    item.classList.toggle('is-done');
    updateCount();
  }
});

// 6. 切换深浅色主题
themeToggle.addEventListener('click', () => {
  const isDark = document.body.classList.toggle('is-dark');
  themeToggle.textContent = isDark ? '切换浅色' : '切换深色';
});

这段代码中有两个新手很容易受益的原生 JS 写法:

  • 用 textContent 而不是 innerHTML 写入用户输入:如果用字符串拼接配合 innerHTML 插入用户输入的文本,当输入内容里出现 < 或 > 等符号时,浏览器会尝试把它当作 HTML 标签解析;改用 document.createElement 加 textContent,无论输入什么字符都只会原样显示为安全文本。
  • 用事件委托监听列表点击:任务项是用户动态添加的,如果只给页面初始那一条绑定点击事件,新添加的任务点击就会没反应。把监听函数挂在始终存在的父元素 #task-list 上,利用事件冒泡和 event.target.closest(...) 判断点击的究竟是“.btn-delete”还是“.task-text”,这样新增的条目不需要重复绑定事件就能直接工作。

验证页面是否正常工作

保存 app.js 后回到浏览器刷新页面,依次做下面四项操作:

  1. 在输入框中输入“练习事件委托”并按回车或点击“添加”,页面应新增一条任务,输入框自动清空,计数变为“剩余 2 项未完成”。
  2. 点击任意一条任务的文字部分,文字应当出现删除线并变浅;计数减少 1。再次点击则恢复未完成状态。
  3. 点击某条任务右侧的“删除”按钮,该行应从页面移除,计数同步更新。
  4. 点击右上角“切换深色”,整页背景应变为深灰、按钮文字变为“切换浅色”;同时开发者工具 Console 面板中没有任何红色报错。

五、容易混淆的说法辨析

刚开始接触用 JavaScript 做网站时,网上的教程和讨论里常出现几种看似合理、但条件没说清就容易把新手带偏的说法。

说法一:“做网站用 JavaScript,必须先装 Node.js 并学 React 或 Vue”

这个说法在团队开发大型单页应用时成立:当网站有几十个页面、复杂的数据流和多人协作需求时,框架负责组件复用和状态同步,Node.js 负责打包和工程化工具链。但它并不意味着“没有框架和 Node.js 就不能用 JS 做网站”。

浏览器本身就内置了完整的 HTML 解析器、CSS 引擎和 JavaScript 引擎。像上面的待办清单、产品展示页、个人简历站或表单计算器,只要双击本地 index.html 就能运行,部署时也只需把这几个静态文件上传到服务器即可。先掌握原生 DOM 查询、事件机制和 classList,再去学任何框架都会明白它们底层在替你封装什么。

说法二:“把 <script> 放在 <head> 里最规范,或者直接把 JS 写在 onclick 里更省事”

把资源统一放在 <head> 里看起来整齐,在 HTML 标签上直接写 <button onclick="..."> 在三行代码的演示里也显得很短。但如果不加条件照做,就会踩到两个典型问题。

第一,如果在 <head> 里写普通 <script src="app.js"></script> 且不加 defer,浏览器解析到这一行时会暂停解析后面的 <body>,立刻下载并执行 app.js。此时 #task-form 根本还没有被创建出来,document.querySelector('#task-form') 的结果就是 null,紧接着调用 addEventListener 就会报错崩溃。

<!-- 错误写法:位于 head 且没有 defer,执行时 body 尚未解析 -->
<head>
  <script src="app.js"></script>
</head>

<!-- 正确写法一:放在 head 中并加上 defer(推荐) -->
<head>
  <script src="app.js" defer></script>
</head>

<!-- 正确写法二:放在 </body> 结束标签之前 -->
  <script src="app.js"></script>
</body>

第二,虽然 onclick="addTask()" 在极小的演示里能跑通,但它把 JS 函数名硬编码在 HTML 里,随着功能增加,你不得不在多个文件之间来回找某个按钮调用了哪个全局函数,也难以配合事件委托一起使用。坚持用 addEventListener 把交互逻辑留在 app.js,后期改起来清晰得多。

零基础用JavaScript做网站:从HTML/CSS到原生JS交互
确保 DOM 解析完成后再执行 JS,是避免 querySelector 返回 null 的关键

六、新手排查问题的三个检查点

按上面步骤编写代码时,如果页面没有出现预期效果,可以对照 Console 报错按下面顺序检查:

  • 点击“添加”后页面闪一下,刚输入的字没了:检查 taskForm.addEventListener('submit', ...) 里的第一行是否漏写了 event.preventDefault(),或者事件是不是误绑在了按钮的 click 上而非表单的 submit 上。
  • 控制台提示 Cannot read properties of null:说明 document.querySelector(...) 没找到对应元素。先核对选择器前面有没有漏掉 #(id)或 .(class),再核对 index.html 里的拼写与 <script> 是否带了 defer。
  • 样式或 JS 完全没生效:检查三个文件是否真的放在同一个文件夹 my-first-site 下,以及 index.html 中 href="style.css" 和 src="app.js" 的文件名大小写是否完全匹配。

七、总结与下一步扩展方向

到这里,你已经完成了用原生前端技术做一个可交互网站的完整闭环:HTML 负责定义表单与列表结构,CSS 负责页面排版和 .is-done、.is-dark 等视觉状态,原生 JavaScript 负责接管事件并动态增删 DOM 节点。由于整个项目只包含三个纯静态文件,只要把文件夹上传到任意静态托管平台(如 GitHub Pages、Cloudflare Pages 或自己的 Web 服务器静态目录),别人就能通过网址访问你的页面。

在这个页面稳定运行之后,如果希望继续完善它,可以沿两个最自然的方向扩展:第一是用浏览器自带的 localStorage 把任务数组保存下来,这样刷新或重新打开页面后任务不会丢失;第二是用原生 fetch() 请求后端接口,把本地列表升级成能联网保存数据的完整 Web 应用。

零基础用JavaScript做网站:从HTML/CSS到原生JS交互
掌握三层协作后,再逐步引入本地存储与网络请求能力
作者最新文章
相关文章
更多