本书标签: 游戏同人 

拓展篇:JS 在 HTML 网页开发规范

原神第二课堂:胡桃讲编程之JS——ES6进阶实战

前言

前面全套 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');

}

})