前言
前面全套 57 节我们吃透了ES6+ JavaScript 核心语法,但纯 JS 代码无法直接运行在浏览器页面,真实网页开发是一套完整技术栈组合:
HTML5:页面骨架,搭建结构、标签语义化、多媒体、表单、画布等页面载体;
CSS3:页面样式,布局、动画、渐变、弹性 / 网格布局,负责视觉展示;
JavaScript:页面交互逻辑,操作 DOM、异步请求、数据处理、动画控制,承接 ES6 所有语法能力。
本章分为三大部分:
JS 在 HTML 中的标准引入规范、书写规范、作用域隔离;
HTML5 专属特性与 JS 联动(画布、音视频、本地存储、表单校验);
CSS3 + JS 协同实战:动态样式、JS 控制动画、响应式交互、完整页面综合案例。
一、JS 嵌入 HTML 的 4 种规范写法(优劣对比)
1. 内联行内 JS(不推荐,仅简单调试)
直接写在标签事件属性中,耦合 HTML 结构与逻辑,无法复用、难以维护。
html
预览
<!-- 缺陷:结构、逻辑混写,无法单独模块化管理 -->
<button onclick="alert('点击按钮')">点我</button>
规范禁止场景:正式项目全部舍弃,仅临时测试使用。
2. 页面内部 script 标签(页面专属少量逻辑可用)
在 <script> 标签内书写 JS,分为两种放置位置。
写法 A:放在 <head> 头部(需 DOM 加载判断)
页面先加载 JS,再渲染 DOM,直接操作 DOM 会报错,必须等待页面加载完成。
html
预览
<head>
<script>
// 等待全部DOM渲染完毕再执行代码
document.addEventListener('DOMContentLoaded', () => {
const box = document.querySelector('.box');
box.style.color = 'red';
})
</script>
</head>
写法 B:放在 <body> 末尾(推荐单页面简易脚本)
DOM 从上至下渲染,标签加载完成后再执行 JS,无需等待事件,代码更简洁。
html
预览
<body>
<div class="box">测试文本</div>
<!-- DOM全部渲染完成后执行JS -->
<script>
const box = document.querySelector('.box');
box.style.color = 'red';
</script>
</body>
3. 外部独立 JS 文件(正式项目标准规范,模块化首选)
将所有 JS 逻辑拆分独立 .js 文件,通过 src 引入,分离结构与逻辑,支持 ES6 Module 模块化。
普通脚本(全局脚本,无模块化)
html
预览
<script src="./js/main.js"></script>
ES6 Module 模块化脚本(工程化开发必用,对应第 56 节知识点)
添加 type="module",开启独立模块作用域,支持 import/export。
html
预览
<!-- 开启ES6模块化,内部可使用import导入其他js模块 -->
<script src="./js/index.js" type="module"></script>
Module 补充规范:
模块自动开启严格模式 use strict;
每个模块独立作用域,变量不会污染全局;
本地直接打开 HTML 会跨域报错,必须使用本地服务运行(vscode LiveServer 插件)。
4. 延迟加载脚本 defer /async(优化页面加载性能)
页面 JS 文件过大时,会阻塞 HTML 渲染,两个属性实现异步加载脚本。
defer:后台下载 JS,DOM 渲染完成后按引入顺序执行
html
预览
<script src="./js/api.js" defer></script>
<script src="./js/ui.js" defer></script>
async:后台下载 JS,下载完成立即执行,执行顺序不受引入顺序控制
html
预览
<script src="./js/fast.js" async></script>
选型规范:
需要保证执行顺序(模块化、业务逻辑)→ 使用 defer;
独立不依赖其他脚本的统计、埋点代码 → 使用 async。
二、JS 网页开发统一编码规范(工程通用标准)
1. 变量与语法规范(延续 ES6 规范)
禁用 var,全部使用 const / let;常量大写命名 const MAX_WIDTH = 1200;
操作 DOM 的元素缓存到变量,避免重复查询 DOM:
js
运行
// 规范:只查询一次DOM,重复复用
const btn = document.querySelector('#submit-btn');
btn.onclick = () => {};
btn.style.background = '#fff';
大量字符串拼接优先使用模板字符串 `,淘汰 + 拼接;
遍历 DOM 列表优先 for...of,放弃老式 for 循环、forEach(支持 break 中断);
事件回调优先箭头函数,不滥用 function 普通函数。
2. DOM 操作规范
统一使用 querySelector / querySelectorAll 选择元素,兼容 CSS 选择器;
批量修改 DOM 时,优先操作类名 classList,不直接修改 style 行内样式:
js
运行
// 推荐:切换css类,样式统一交给CSS管理
box.classList.add('active');
box.classList.remove('hide');
box.classList.toggle('show');
// 不推荐:直接修改行内样式,难以统一维护
box.style.display = 'block';
批量新增 DOM 节点,使用文档碎片 DocumentFragment,减少页面重绘重排,提升性能;
禁止频繁循环修改 DOM,单次完成 DOM 更新。
3. 作用域与全局污染规范
ES6 Module 优先,天然隔离作用域;
非模块化页面,使用 IIFE 立即执行函数包裹代码,隔离局部变量;
js
运行
// IIFE 隔离,内部变量不会泄露到window全局
(() => {
const num = 100; // 局部私有变量,外部无法访问
})();
全局配置统一挂载单一全局对象,杜绝零散全局变量:
js
运行
// 规范:仅暴露一个全局对象,所有配置存放内部
window.AppConfig = {
maxStack: 64,
apiUrl: '/api/getData'
}
4. 事件绑定规范
优先事件委托(事件冒泡),减少 DOM 事件绑定数量,适配动态生成元素;
js
运行
// 父容器统一监听,动态新增子元素也能触发事件
document.querySelector('.bag-list').addEventListener('click', (e) => {
if(e.target.matches('.item')){
console('点击道具', e.target);
}
})
页面销毁、组件卸载时移除事件监听,防止内存泄漏;
禁止行内 onclick 事件,全部 JS 统一绑定。
三、HTML5 新特性 + JS 联动开发
HTML5 新增大量原生能力,全部依赖 JS 实现交互,是 H5 页面核心功能。
1. 本地存储 localStorage /sessionStorage(持久化数据)
localStorage:永久存储,关闭页面数据不丢失;
sessionStorage:会话存储,关闭标签页自动清空;
基础语法(ES6 模板字符串配合):
js
运行
// 写入
localStorage.setItem('userName', '胡桃');
// 读取
const name = localStorage.getItem('userName');
// 删除
localStorage.removeItem('userName');
// 清空全部
localStorage.clear();
实战场景:保存玩家背包、表单填写缓存、页面主题设置。
2. Canvas 画布(JS 绘制 2D 图形、小游戏)
HTML5 <canvas> 标签搭配 JS 实现绘图,MC、2D 小游戏底层核心方案。
html
预览
<canvas id="gameCanvas" width="800" height="400"></canvas>
<script type="module">
const canvas = document.querySelector('#gameCanvas');
const ctx = canvas.getContext('2d');
// 绘制矩形
ctx.fillStyle = '#ff0000';
ctx.fillRect(50,50,100,100);
</script>
3. 音视频 Audio / Video 媒体控制
HTML5 `` <video>,JS 控制播放、暂停、音量、进度:
js
运行
const audio = document.querySelector('#bgm');
audio.play(); // 播放
audio.pause(); // 暂停
audio.volume = 0.5; // 音量0~1
4. 表单原生校验 + JS 增强校验
HTML5 自带 required pattern,JS 补充复杂逻辑校验,适配登录、注册页面。
html
预览
<input type="text" id="phone" pattern="^\d{11}$" required>
<script>
const input = document.querySelector('#phone');
input.addEventListener('blur', () => {
if(!input.validity.valid){
alert('手机号格式错误');
}
})
</script>
5. 语义化标签规范(配合 JS 选择器)
HTML5 标准语义标签:<header> <nav> <main> <section> <footer>,搭配 CSS 选择器精准获取 DOM,替代无意义 div。
js
运行
// 直接通过语义标签选择器获取
const mainBox = document.querySelector('main');
const navList = document.querySelectorAll('nav li');
四、CSS3 + JS 协同开发(样式、动画交互方案)
1. JS 控制 CSS 类,实现动态样式(最优方案)
所有动画、显示隐藏、主题切换,预先写好 CSS 类,JS 只负责增删类名,样式统一管理在 CSS 文件。
CSS 预定义样式
css
.box {
width: 200px;
transition: all 0.3s ease; /* CSS3过渡动画 */
}
.box.active {
background: #42b983;
transform: scale(1.1);
}
.box.hide {
opacity: 0;
visibility: hidden;
}
JS 仅操作类名
js
运行
const box = document.querySelector('.box');
box.classList.toggle('active'); // 切换放大高亮样式
box.classList.add('hide'); // 隐藏元素
2. JS 读取 / 修改 CSS3 变量(全局主题切换)
CSS3 自定义变量,JS 动态修改变量,一键切换深色 / 浅色主题、全局配色。
css
:root {
--main-color: #fff;
--text-color: #333;
}
.dark {
--main-color: #222;
--text-color: #eee;
}
.box {
background: var(--main-color);
color: var(--text-color);
}
JS 修改根变量:
js
运行
const root = document.documentElement;
// 切换深色主题
root.style.setProperty('--main-color', '#222');
root.style.setProperty('--text-color', '#eee');
3. CSS3 过渡 / 帧动画 + JS 触发控制
动画逻辑交给 CSS,JS 只负责开关,性能远高于 JS 逐帧动画。
transition 过渡动画:切换类名自动触发;
@keyframes 关键帧动画:JS 控制 animation-play-state 暂停 / 播放。
css
@keyframes move {
0% { transform: translateX(0); }
100% { transform: translateX(300px); }
}
.run {
animation: move 2s infinite alternate;
animation-play-state: paused;
}
.run.start {
animation-play-state: running;
}
js
运行
const item = document.querySelector('.run');
item.classList.add('start'); // 启动动画
item.classList.remove('start'); // 暂停动画
4. 弹性 Flex / Grid 响应式布局 + JS 判断屏幕尺寸
CSS3 Flex、Grid 完成页面基础布局,JS 监听窗口尺寸变化,适配移动端逻辑:
js
运行
// 监听窗口缩放
window.addEventListener('resize', () => {
const width = window.innerWidth;
if(width < 768){
// 移动端逻辑
document.body.classList.add('mobile-layout');
}else{
document.body.classList.remove('mobile-layout');
}
})
五、完整综合实战:H5 交互道具卡片页面(H5+CSS3+ES6 JS)
1. index.html 结构(HTML5 语义化标签 + 模块化 JS)
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="./css/style.css">
</head>
<body>
<header class="page-header">
<h2>道具背包</h2>
<button id="clear-btn">清空选中</button>
</header>
<main class="bag-container"></main>
<!-- ES6模块化JS -->
<script src="./js/bag.js" type="module"></script>
</body>
</html>
2. css/style.css (CSS3 弹性布局、过渡动画、变量)
css
:root {
--card-bg: #f5f5f5;
--card-active: #ffe8cc;
--transition-time: 0.25s;
}
.bag-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
padding: 20px;
}
.item-card {
width: 120px;
padding: 12px;
background: var(--card-bg);
border-radius: 8px;
transition: all var(--transition-time);
cursor: pointer;
}
.item-card.active {
background: var(--card-active);
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(255,150,0,0.3);
}
3. js/bag.js (ES6 Module、Set、DOM 操作、事件委托)
js
运行
// 模拟道具数据
const itemList = [
{name:"桃木符", stack:64, id:1001},
{name:"幽冥灯笼", stack:32, id:1002},
{name:"招魂幡", stack:16, id:1003}
];
// Set存储选中道具ID
const selectSet = new Set();
const bagWrap = document.querySelector('.bag-container');
const clearBtn = document.querySelector('#clear-btn');
// 渲染道具卡片
function renderItems(list){
bagWrap.innerHTML = '';
const frag = document.createDocumentFragment();
for(const item of list){
const div = document.createElement('div');
div.className = 'item-card';
div.dataset.id = item.id;
div.innerHTML = `
<h4>${item.name}</h4>
<p>堆叠:${item.stack}</p>
`;
frag.appendChild(div);
}
bagWrap.appendChild(frag);
}
renderItems(itemList);
// 事件委托:选中切换
bagWrap.addEventListener('click', e => {
const card = e.target.closest('.item-card');
if(!card) return;
const id = Number(card.dataset.id);
if(selectSet.has(id)){
selectSet.delete(id);
card.classList.remove('active');
}else{
selectSet.add(id);
card.classList.add('active');
}
})
// 清空选中按钮
clearBtn.addEventListener('click', () => {
selectSet.clear();
document.querySelectorAll('.item-card.active').forEach(card => {
card.classList.remove('active');
})
})
六、本章课后练习
基础题
写出 ES6 模块化 JS 引入 HTML 的完整 script 标签;
简述 defer 和 async 的加载执行区别;
JS 控制页面主题切换,优先修改 CSS 变量还是直接修改元素 style?说明理由。
进阶实战题
基于本章综合案例改造:
使用 localStorage 保存选中道具 ID,刷新页面自动恢复选中状态;
添加深色主题切换按钮,通过 JS 修改 CSS 全局变量实现黑白主题切换;
点击道具卡片使用 CSS3 动画放大,依靠 classList 切换动画类实现。
参考答案
基础题简答
<script src="./xxx.js" type="module"></script>
defer:后台加载 JS,DOM 渲染完成后按引入顺序执行;async:下载完成立刻执行,顺序不保证。
优先修改 CSS 全局变量;所有样式统一托管 CSS,减少 JS 操作行内样式,便于统一维护、修改主题配色。
进阶核心代码片段
1. localStorage 持久化选中
js
运行
// 页面加载读取缓存
const saveIds = JSON.parse(localStorage.getItem('selectIds')) || [];
saveIds.forEach(id => selectSet.add(id));
// 渲染后恢复选中
document.querySelectorAll('.item-card').forEach(card => {
const id = Number(card.dataset.id);
if(selectSet.has(id)) card.classList.add('active');
})
// 每次选中变更同步存储
function saveSelect(){
localStorage.setItem('selectIds', JSON.stringify([...selectSet]));
}
// 在点击、清空按钮逻辑末尾调用 saveSelect();
2. 深色主题切换
js
运行
const themeBtn = document.querySelector('#theme-btn');
themeBtn.addEventListener('click', () => {
const root = document.documentElement;
const isDark = root.style.getPropertyValue('--card-bg') === '#2a2a2a';
if(isDark){
root.style.setProperty('--card-bg', '#f5f5f5');
root.style.setProperty('--text-color', '#222');
}else{
root.style.setProperty('--card-bg', '#2a2a2a');
root.style.setProperty('--text-color', '#eee');
}
})