- 整体课时规划
- 第一部分:课前准备(2课时)
- 第二部分:HTML(网页骨架)
- 课时3:HTML基础语法——标签和属性
- 课时4:HTML文本标签——标题、段落、换行
- 课时5:HTML图片标签——插入本地/网络图片
- 课时6:HTML链接标签——跳转网页/锚点
- 课时7:HTML无序列表——ul+li
- 课时8:HTML有序列表——ol+li
- 课时9:HTML表格——table+tr+td
- 课时10:HTML表单——form+input(文本框/密码框)
- 课时11:HTML表单——单选框/复选框
- 课时12:HTML表单——下拉框/提交按钮
- 课时13:HTML分区标签——div和span
- 课时14:HTML语义化标签——header/nav/main/footer
- 课时15-18:HTML综合练习
- 第三部分:CSS(样式)
- 课时19:CSS引入方式——行内样式
- 课时20:CSS引入方式——内嵌样式
- 课时21:CSS选择器——标签选择器
- 课时22:CSS选择器——类选择器(核心)
- 课时23:CSS选择器——ID选择器
- 课时24:CSS文字样式——颜色、大小、粗细
- 课时25:CSS文字样式——行高、对齐、装饰
- 课时26:CSS背景样式——背景颜色、背景图片
- 课时27:CSS边框样式——border
- 课时28:CSS盒子模型——width/height
- 课时29:CSS盒子模型——padding(内边距)
- 课时30:CSS盒子模型——margin(外边距)
- 课时31:CSS显示模式——display(block/inline/inline-block)
- 课时32:CSS浮动布局——float(基础)
- 课时33-37:CSS Flex布局(核心布局,5课时)
- 课时38:CSS定位——相对定位(relative)
- 课时39:CSS定位——绝对定位(absolute)
- 课时40:CSS定位——固定定位(fixed)
- CSS综合练习
- 第四部分:JS(交互)
- 课时44:JS引入方式——行内/内嵌
- 课时45:JS变量——声明和赋值
- 课时46:JS数据类型——基本类型(字符串/数字/布尔)
- 课时47:JS数据类型——特殊类型(null/undefined)
- 课时48:JS运算符——算术运算符
- 课时49:JS运算符——赋值/比较运算符
- 课时50:JS运算符——逻辑运算符
- 课时51:JS条件语句——if语句
- 课时52:JS条件语句——if-else if-else
- 课时53:JS条件语句——switch语句
- 课时54:JS循环——for循环(基础)
- 课时55:JS循环——for循环(进阶)
- 课时56:JS循环——while循环
- 课时57:JS数组——声明/访问/修改
- 课时58:JS数组——常用方法(增删改查)
- 课时59:JS函数——声明和调用
- 课时60:JS函数——参数和返回值
- 课时61:JS DOM——获取元素(基础)
- 课时62:JS DOM——获取元素(进阶)
- 课时63:JS DOM——修改元素内容和样式
- 课时64:JS DOM——修改元素类名
- 课时65:JS事件——点击事件(基础)
- 课时66:JS事件——鼠标/键盘事件
- 课时67:JS事件——表单事件
- 课时68:JS综合练习1——个人主页交互优化(点击事件)
- 课时69:JS综合练习2——个人主页交互优化(表单验证)
- 课时70:JS综合练习3——个人主页交互优化(动态数据)
- 课时71:JS综合练习4——个人主页交互优化(简易轮播图)
- 课时72:JS模块总结与查漏补缺
- 课时73:实战1——需求分析与页面规划(简易个人博客)
- 课时74:实战2——搭建HTML结构(简易个人博客)
- 课时75:实战3——编写CSS样式(简易个人博客)
- 课时76:实战4——编写JS交互功能(简易个人博客)
整体课时规划
| 模块 | 课时数 | 核心目标 |
|---|---|---|
| 课前准备 | 2 | 认识前端、搭建极简学习环境 |
| HTML(骨架) | 18 | 掌握网页结构搭建的所有核心 |
| CSS(样式) | 25 | 搞定美化+布局,做出美观网页 |
| JS(交互) | 30 | 实现网页动态效果,会做简单交互 |
| 综合实战 | 5 | 整合知识,完成2个入门级项目 |
第一部分:课前准备(2课时)
课时1:认识Web前端——到底学什么?
学习目标
知道前端是做什么的,分清HTML/CSS/JS的分工,建立学习框架。
通俗讲解
前端 = 网页/APP的「用户看得见的部分」,比如淘宝首页、微信公众号页面;
- HTML:网页的「骨架」(决定有什么,比如标题、图片、按钮);
- CSS:网页的「装修」(决定长什么样,比如颜色、大小、位置);
- JS:网页的「功能」(决定能做什么,比如点击按钮弹窗、输入内容实时显示)。

核心认知
- 前端开发工具:只用「记事本/VS Code + 浏览器(Chrome)」,不用装复杂软件;
- 学习路径:先搭骨架(HTML)→ 再装修(CSS)→ 最后加功能(JS);
- 核心原则:先实现「能用」,再优化「好看/好用」。
课后小练习
打开Chrome浏览器,随便打开一个网页(比如百度),右键→「检查」,看看Elements面板里的HTML/CSS代码(不用懂,只需要知道“网页是由这些代码组成的”)。
课时2:搭建极简学习环境
学习目标
会安装VS Code,能新建/保存/运行HTML文件。
通俗讲解
VS Code是「前端专用记事本」,比系统记事本好用,支持代码高亮、自动补全,新手只需要掌握最基础的操作。
实操步骤
- 下载安装VS Code:官网(https://code.visualstudio.com/)下载,一路点「下一步」安装;

2.安装中文插件:打开VS Code→左侧「扩展」→搜「Chinese」→安装「Chinese (Simplified) Language Pack」→重启VS Code;

安装Live Server插件,代码更新,浏览器自动更新页面

设置自动保存,每次编辑自动保存文件

3.新建第一个文件:
- 新建文件夹(比如「前端学习」);
- VS Code→打开文件夹→右键「新建文件」→命名为
01-测试.html(后缀必须是.html); -
输入
<!DOCTYPE html><body>你好,前端!</body></html>,按Ctrl+S保存; -
运行文件:右键文件→「在默认浏览器中打开」,能看到页面显示“你好,前端!”即可。

易错点提醒
- ❌ 文件后缀写错:把
.html写成.htm/.txt,浏览器打开不是网页; - ❌ 保存路径有中文/空格:比如「我的 前端/测试.html」,部分场景会出问题,建议用英文/数字命名(如
web-study/01-test.html)。
课后小练习
参考01-测试.html代码,创建02-VSCode.html,编辑“我的第一个VS Code网页”,保存后刷新浏览器,看文字变化。
VScode快捷键(了解):
| 功能 | Windows/Linux | Mac | 前端使用场景 |
|---|---|---|---|
| 复制当前行/选中内容 | Ctrl + C | Cmd + C | 复制代码片段(如CSS样式、JS函数) |
| 剪切当前行/选中内容 | Ctrl + X | Cmd + X | 快速移动代码行(如调整HTML标签顺序) |
| 粘贴 | Ctrl + V | Cmd + V | 粘贴复制的代码 |
| 撤销 | Ctrl + Z | Cmd + Z | 回退错误操作(如误删代码) |
| 重做 | Ctrl + Y | Cmd + Shift + Z | 恢复撤销的操作 |
| 保存文件 | Ctrl + S | Cmd + S | 前端开发必高频用(避免代码丢失) |
| 全选 | Ctrl + A | Cmd + A | 选中整段代码(如替换整块CSS样式) |
| 格式化代码(核心) | Shift + Alt + F | Shift + Option + F | 一键整理HTML/CSS/JS格式,解决缩进混乱 |
| 注释行(单行/多行) | Ctrl + / | Cmd + / | 快速注释调试代码(前端调试必备) |
| 复制当前行到下一行 | Shift + Alt + ↓ | Shift + Option + ↓ | 快速复制重复代码(如多个li标签、CSS属性) |
| 移动当前行上下 | Alt + ↓/↑ | Option + ↓/↑ | 调整代码行顺序(如调整JS逻辑、CSS属性) |
| 快速查找 | Ctrl + F | Cmd + F | 在当前文件搜索关键词(如找CSS类名、JS变量) |
| 查找并替换 | Ctrl + H | Cmd + H | 批量替换(如修改类名、变量名) |
VSCode内置Emmet语法,输入缩写后按Tab键即可快速生成代码,是前端写HTML/CSS的效率神器:
| 输入缩写 | 按Tab生成效果 |
|---|---|
! |
生成HTML5完整骨架(DOCTYPE、html/head/body等) |
div.container |
<div class="container"></div> |
ul>li*3 |
生成包含3个li的ul列表 <ul><li></li><li></li><li></li></ul> |
a[href="#"] |
<a href="#"></a> |
p{这是文本} |
<p>这是文本</p> |
css: w100px |
width: 100px;(CSS缩写,支持h、m、p等属性,如h50px→height: 50px;) |
.box>h2{标题}+p{内容} |
<div class="box"><h2>标题</h2><p>内容</p></div> |
JavaScript专属快捷键
| 功能 | Windows/Linux | Mac | 前端使用场景 |
|---|---|---|---|
| 快速打开终端 | Ctrl + | Cmd + |
运行npm命令、启动本地服务(如live-server) | |
| 跳转到变量/函数定义 | F12 | F12 | 查看JS函数/变量的定义位置(调试必备) |
| 快速重命名变量/函数 | F2 | F2 | 批量修改变量名(如把num改为count) |
| 折叠/展开代码块 | Ctrl + -/+ | Cmd + -/+ | 折叠冗长的JS函数、CSS样式块 |
| 查看代码提示/补全 | Ctrl + 空格 | Cmd + 空格 | 触发VSCode代码补全(如JS方法、CSS属性) |
窗口/文件管理(前端多文件开发)
| 功能 | Windows/Linux | Mac | 前端使用场景 |
|---|---|---|---|
| 新建文件 | Ctrl + N | Cmd + N | 新建HTML/CSS/JS文件 |
| 打开文件 | Ctrl + O | Cmd + O | 打开本地前端项目文件 |
| 切换已打开的文件 | Ctrl + Tab | Cmd + Tab | 在HTML/CSS/JS文件间快速切换 |
| 分屏编辑(左右) | Ctrl + \ | Cmd + \ | 同时查看HTML和CSS文件(前端布局必备) |
| 关闭当前文件 | Ctrl + W | Cmd + W | 关闭无用文件,保持编辑器整洁 |
总结
- 前端开发最高频快捷键:
Shift + Alt + F(格式化代码)、Ctrl + /(注释)、Ctrl + S(保存),以及Emmet缩写+Tab(快速写HTML/CSS); - Emmet语法是前端提效核心,记住
!(HTML骨架)、>(子元素)、*(重复)、.(类名)这几个核心规则即可覆盖80%场景; - 若快捷键无效,可检查VSCode是否开启「自动格式化」(设置中搜索
Format On Save),保存时自动整理代码,减少手动操作。
你可根据自己的系统(Windows/Mac)记忆对应快捷键,先掌握前3类核心快捷键,后续再逐步熟悉其他功能。
第二部分:HTML(网页骨架)
课时3:HTML基础语法——标签和属性
学习目标
认识HTML标签的写法,知道属性的作用。
通俗讲解
HTML标签就像「作文的格式符号」:<h1>是标题标记,<p>是段落标记,标签必须成对(除了少数单标签);属性是标签的「补充说明」,比如<img>标签的src属性告诉浏览器“图片在哪”。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>标签和属性</title>
</head>
<body>
<!-- 双标签:<标签>内容</标签> -->
<h1>这是一级标题</h1>
<p>这是段落文字</p>
<!-- 单标签:<标签 属性="值"> -->
<img src="https://picsum.photos/200/100" alt="风景图">
</body>
</html>

实操步骤
- 新建
03-html基础.html,粘贴代码; - 运行后看页面显示,修改
<h1>里的文字、<img>的src地址,看效果; - 尝试删除
</h1>,刷新页面,看浏览器是否自动补全(观察即可,知道双标签要成对)。
易错点提醒
- ❌ 标签大小写混用:比如
<H1>标题</h1>,虽然浏览器兼容,但规范写法是小写; - ❌ 属性值漏加引号:比如
<img src=https://picsum.photos/200/100>,虽然部分场景生效,但必须加引号(单/双引号都可以)。
课后小练习
添加<h3>标签写二级标题,添加<br>单标签给段落换行。
课时4:HTML文本标签——标题、段落、换行
学习目标
掌握h1-h6、p、br、strong/em的用法。
通俗讲解
- h1-h6:标题层级,h1最大(一个页面只写1个),h6最小;
- p:段落,自动换行且和其他段落有间距;
- br:强制换行(无间距);
- strong:文字加粗,em:文字斜体。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文本标签</title>
</head>
<body>
<h1>网页主标题</h1>
<h3>二级小标题</h3>
<p>这是第一段文字,<strong>加粗部分</strong>很重要,<em>斜体部分</em>是注释</p>
<p>这是第二段文字<br>这行是强制换行的内容</p>
</body>
</html>

实操步骤
- 新建
04-文本标签.html,粘贴代码; - 修改标题层级、加粗/斜体的位置,看显示效果;
- 对比p标签和br标签的换行差异(p有间距,br无)。
易错点提醒
- ❌ 用多个br代替p:比如
<br><br>文字,可读性差,段落用p,强制换行才用br; - ❌ 一个页面写多个h1:不符合SEO规范,一个页面只保留1个h1。
课后小练习
写一段自我介绍,包含h1(我的自我介绍)、h3(基本信息)、p(姓名/年龄/爱好),给重点内容加粗。
课时5:HTML图片标签——插入本地/网络图片
学习目标
会插入网络图片和本地图片,掌握img核心属性。
通俗讲解
img标签是「网页的图片框」,src是图片地址(网络地址/本地路径),alt是图片加载失败时的提示文字(必须加),width/height控制图片大小(只设一个会等比例缩放)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>图片标签</title>
</head>
<body>
<!-- 网络图片 -->
<img src="https://picsum.photos/300/200" alt="风景图" width="200">
<!-- 本地图片:把图片和html文件放同一文件夹,写文件名即可 -->
<img src="1.jpeg" alt="本地图片" height="100">
</body>
</html>

实操步骤
- 新建
05-图片标签.html,粘贴网络图片代码,运行看效果; - 找一张本地图片(比如截图),重命名为
我的图片.jpg,和html文件放同一文件夹,补充本地图片代码,运行看是否显示; - 只改width,看图片是否等比例缩放。
易错点提醒
- ❌ 本地图片路径错:比如图片在「图片文件夹」里,src要写
图片文件夹/我的图片.jpg; - ❌ 同时设width和height导致图片变形:只设一个,让浏览器自动适配比例。
课后小练习
插入一张自己的头像图片(本地),设置宽度为150px,添加alt="我的头像"。
课时6:HTML链接标签——跳转网页/锚点
学习目标
会做网页跳转链接,知道锚点链接的用法。
通俗讲解
a标签是「网页的跳转按钮」,href是跳转地址,target="_blank"是新窗口打开(不覆盖当前页面);锚点链接是「跳转到页面内指定位置」,比如长页面的“回到顶部”。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>链接标签</title>
</head>
<body>
<!-- 跳转到外部网页 -->
<a href="https://www.baidu.com" target="_blank">打开百度(新窗口)</a>
<a href="https://www.taobao.com">打开淘宝(当前窗口)</a>
<!-- 锚点链接:先定义锚点,再跳转 -->
<a href="#bottom">跳转到页面底部</a>
<p style="height: 1000px;">占位文字,让页面变长</p>
<div id="bottom">我是页面底部</div>
</body>
</html>

实操步骤
- 新建
06-链接标签.html,粘贴代码; - 点击两个外部链接,看跳转方式差异;
- 点击锚点链接,看是否跳转到页面底部。
易错点提醒
- ❌ href漏写https://:比如
<a href="www.baidu.com">,会跳转到本地路径,不是百度; - ❌ 锚点id写错:比如
href="#bot",但锚点id是bottom,跳转无效。
课后小练习
添加“回到顶部”锚点链接,跳转到页面最上方(给h1加id="top",链接href="#top")。
课时7:HTML无序列表——ul+li
学习目标
掌握无序列表的写法,用于非顺序类内容(比如购物清单)。
通俗讲解
无序列表是「没有数字的清单」,用<ul>包裹<li>,默认显示圆点,适合列爱好、导航栏等不需要排序的内容。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>无序列表</title>
</head>
<body>
<h3>我的爱好</h3>
<ul>
<li>学习前端</li>
<li>看电影</li>
<li>跑步</li>
</ul>
</body>
</html>

实操步骤
- 新建
07-无序列表.html,粘贴代码; - 新增2个li标签,添加更多爱好;
- 尝试把ul改成ol,看显示差异(提前感知有序列表)。
易错点提醒
- ❌ 直接在ul里写文字:比如
<ul>学习前端</ul>,不会显示列表样式,必须包在li里; - ❌ li标签不成对:比如
<li>跑步,浏览器会自动补全,但要养成成对写的习惯。
课后小练习
用无序列表做一个“早餐清单”,包含3-5个菜品。
课时8:HTML有序列表——ol+li
学习目标
掌握有序列表的写法,用于顺序类内容(比如步骤说明)。
通俗讲解
有序列表是「带数字的步骤」,用<ol>包裹<li>,默认显示1、2、3…,适合写教程步骤、排行榜等需要排序的内容。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>有序列表</title>
</head>
<body>
<h3>学习前端的步骤</h3>
<ol>
<li>学HTML搭建骨架</li>
<li>学CSS美化样式</li>
<li>学JS实现交互</li>
</ol>
</body>
</html>

实操步骤
- 新建
08-有序列表.html,粘贴代码; - 修改li的顺序,看数字是否自动调整;
- 尝试在li里嵌套无序列表(比如步骤2里加“学Flex布局、学盒子模型”)。
易错点提醒
- ❌ 嵌套列表层级混乱:比如
<ol><li>步骤1<ul>子项</li></ul></ol>,要保证标签嵌套正确(ul/ol里只能放li); - ❌ 用有序列表做导航:导航不需要排序,优先用无序列表。
课后小练习
用有序列表写“煮泡面的步骤”,包含4-5个步骤。
课时9:HTML表格——table+tr+td
学习目标
会做简单表格,用于展示结构化数据(比如课程表)。
通俗讲解
表格是「行+列的格子」:
<table>:表格容器;<tr>:表格的一行;<td>:一行里的一个单元格;<th>:表头单元格(加粗居中)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表格标签</title>
</head>
<body>
<h3>简易课程表</h3>
<table border="1"> <!-- border=1 加边框,方便看 -->
<tr>
<th>时间</th>
<th>课程</th>
</tr>
<tr>
<td>周一上午</td>
<td>HTML</td>
</tr>
<tr>
<td>周一下午</td>
<td>CSS</td>
</tr>
</table>
</body>
</html>

实操步骤
- 新建
09-表格标签.html,粘贴代码; - 新增一行(复制
... ),添加“周二上午”和“JS”; - 尝试合并单元格(简单版:给td加
colspan="2",比如<td colspan="2">午休</td>)。
易错点提醒
- ❌ 表格没有border:新手开发时加border="1",方便看结构,上线后再去掉;
- ❌ tr/td数量不匹配:比如一行有2个td,另一行有3个,表格会错位。
课后小练习
做一个“个人信息表”,包含姓名、年龄、性别、爱好4列,1行数据。
课时10:HTML表单——form+input(文本框/密码框)
学习目标
会做基础表单,用于收集用户输入(比如登录框)。
通俗讲解
表单是「用户的输入界面」,<form>是表单容器,<input>是核心输入元素,不同type对应不同输入类型:
- type="text":普通文本框;
- type="password":密码框(输入隐藏)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表单-文本框/密码框</title>
</head>
<body>
<h3>登录表单</h3>
<form>
<p>用户名:<input type="text" placeholder="请输入用户名"></p>
<p>密码:<input type="password" placeholder="请输入密码"></p>
</form>
</body>
</html>

实操步骤
- 新建
10-表单-文本框.html,粘贴代码; - 在文本框/密码框输入内容,看密码框是否隐藏;
- 修改placeholder提示文字,看显示效果。
易错点提醒
- ❌ 密码框用type="text":输入内容能直接看到,必须用type="password";
- ❌ 忘记加placeholder:用户不知道该输入什么,体验差。
课后小练习
添加“手机号”文本框,placeholder写“请输入11位手机号”。
课时11:HTML表单——单选框/复选框
学习目标
掌握单选框(radio)、复选框(checkbox)的用法。
通俗讲解
- 单选框:只能选一个(比如性别),必须给同一组单选框加相同的
name; - 复选框:可以选多个(比如爱好),同一组复选框加相同的
name。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表单-单选/复选框</title>
</head>
<body>
<h3>注册表单</h3>
<form>
<!-- 单选框:name相同为一组 -->
<p>性别:
<input type="radio" name="gender" id="male"> <label for="male">男</label>
<input type="radio" name="gender" id="female"> <label for="female">女</label>
</p>
<!-- 复选框:name相同为一组 -->
<p>爱好:
<input type="checkbox" name="hobby" id="read"> <label for="read">阅读</label>
<input type="checkbox" name="hobby" id="sport"> <label for="sport">运动</label>
</p>
</form>
</body>
</html>

实操步骤
- 新建
11-表单-单选复选框.html,粘贴代码; - 点击单选框,看是否只能选一个;点击复选框,看是否能选多个;
- 给单选框加
checked属性(比如<input type="radio" checked>),看默认选中效果。
易错点提醒
- ❌ 单选框没加name:多个单选框能同时选中,失去单选意义;
- ❌ label的for和id不匹配:点击label文字不能选中输入框,体验差。
课后小练习
新增“是否同意协议”复选框,默认选中(加checked)。
课时12:HTML表单——下拉框/提交按钮
学习目标
掌握下拉框(select)和提交按钮(submit)的用法。
通俗讲解
- 下拉框:用
<select>+<option>实现,适合选项多的场景(比如城市选择); - 提交按钮:type="submit",点击后提交表单(新手暂时不用管提交后的逻辑)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>表单-下拉框/提交按钮</title>
</head>
<body>
<h3>注册表单</h3>
<form>
<!-- 下拉框 -->
<p>所在城市:
<select>
<option value="">请选择</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
</select>
</p>
<!-- 提交按钮 -->
<p><input type="submit" value="注册"></p>
</form>
</body>
</html>

实操步骤
- 新建
12-表单-下拉框.html,粘贴代码; - 选择下拉框的选项,点击提交按钮(会触发表单提交,暂时不用管跳转);
- 给下拉框的某个option加
selected属性,设置默认选中。
易错点提醒
- ❌ 下拉框没加空选项:默认选中第一个,用户可能误选,建议加
<option value="">请选择</option>; - ❌ 提交按钮用type="button":不会触发表单提交,新手先记type="submit"。
课后小练习
添加“学历”下拉框,包含小学、初中、高中、大学选项。
课时13:HTML分区标签——div和span
学习目标
掌握div(块级)和span(行内)的用法,用于布局分组。
通俗讲解
- div:「块级容器」,默认占一行,用来划分网页大区域(比如头部、主体、底部);
- span:「行内容器」,和文字同行,用来包裹小段文字(比如给某几个字改颜色)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>div和span</title>
</head>
<body>
<!-- div:块级,占一行 -->
<div style="background-color: #eee;">我是第一个div,占一行</div>
<div style="background-color: #ddd;">我是第二个div,占一行</div>
<!-- span:行内,和文字同行 -->
<p>我是段落文字,<span style="color: red;">这段文字变红</span>,其他文字不变</p>
</body>
</html>

实操步骤
- 新建
13-div和span.html,粘贴代码; - 观察div和span的显示差异(div换行,span不换行);
- 用div包裹之前学的列表/表单,做简单分组。
易错点提醒
- ❌ 用span做大区域布局:span是行内元素,不能设置宽高,布局优先用div;
- ❌ 嵌套div层级过多:新手先保持2-3层,避免混乱。
课后小练习
用div划分“头部、主体、底部”3个区域,头部放标题,主体放表单,底部放版权文字。
课时14:HTML语义化标签——header/nav/main/footer
学习目标
认识语义化标签,替代div让代码更易读。
通俗讲解
语义化标签是「有含义的div」,比如<header>就是网页头部,<nav>是导航栏,浏览器显示效果和div一样,但代码更易读,对SEO更友好。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>语义化标签</title>
</head>
<body>
<!-- 头部 -->
<header>
<h1>我的网页</h1>
</header>
<!-- 导航 -->
<nav>
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">关于我</a></li>
</ul>
</nav>
<!-- 主体 -->
<main>
<p>网页的主要内容</p>
</main>
<!-- 底部 -->
<footer>
<p>版权所有 © 2025</p>
</footer>
</body>
</html>

实操步骤
- 新建
14-语义化标签.html,粘贴代码; - 运行后看显示效果(和div一样),理解每个标签的含义;
- 把之前的div布局改成语义化标签。
易错点提醒
- ❌ 滥用语义化标签:比如用header包裹表单,语义不符,按“头部/导航/主体/底部”对应使用;
- ❌ 认为语义化标签有特殊样式:默认样式和div一致,样式需要自己用CSS改。
课后小练习
用语义化标签重构“个人主页”的HTML结构。
课时15-18:HTML综合练习
课时15:综合练习1——搭建个人主页骨架
学习目标
整合HTML标签,搭建包含“头部、导航、主体、底部”的个人主页骨架。
实操任务
- 新建
15-个人主页骨架.html; - 用语义化标签划分结构:
- header:放头像+姓名;
- nav:放导航链接(首页、我的爱好、我的经历、留言板);
- main:放我的爱好(无序列表)、我的经历(有序列表);
- footer:放版权信息。
- 确保所有标签成对,结构清晰。
课时16:综合练习2——给个人主页加表单
学习目标
在个人主页添加“留言板”表单,包含姓名、留言内容、提交按钮。
实操任务
- 在main里新增留言板区域;
- 表单包含:
- 姓名文本框;
- 留言内容文本域(
<textarea>); - 提交按钮。
- 测试表单显示是否正常。
课时17:综合练习3——优化个人主页结构
学习目标
调整标签嵌套,修复布局混乱问题,规范属性写法。
实操任务
- 检查所有img标签是否加了alt属性;
- 检查表单的单选/复选框是否加了label;
- 给所有区域加div/语义化标签分组,避免标签嵌套混乱。
参考代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的个人主页</title>
</head>
<body>
<!-- 头部:头像 + 姓名(语义化标签header) -->
<header>
<!-- img标签添加alt属性,优化可访问性 -->
<img src="avatar.jpg" alt="我的头像">
<h1>张三</h1>
</header>
<!-- 导航栏:导航链接(语义化标签nav) -->
<nav>
<a href="#home">首页</a>
<a href="#hobby">我的爱好</a>
<a href="#experience">我的经历</a>
<a href="#message">留言板</a>
</nav>
<!-- 主体内容:爱好、经历、留言板(语义化标签main) -->
<main>
<!-- 我的爱好:无序列表 -->
<section id="hobby" class="section">
<h2>我的爱好</h2>
<ul>
<li>阅读(偏爱技术类书籍)</li>
<li>编程(HTML/CSS/JavaScript)</li>
<li>运动(跑步、羽毛球)</li>
<li>摄影(记录生活点滴)</li>
</ul>
</section>
<!-- 我的经历:有序列表 -->
<section id="experience" class="section">
<h2>我的经历</h2>
<ol>
<li>2018-2022:XX大学 计算机专业 本科</li>
<li>2022-2023:XX科技公司 前端实习生</li>
<li>2023-至今:XX互联网公司 前端开发工程师</li>
</ol>
</section>
<!-- 留言板:表单(新增区域,优化label关联) -->
<section id="message" class="section">
<h2>留言板</h2>
<form action="#" method="post">
<div class="form-item">
<!-- label通过for关联input的id,提升可访问性 -->
<label for="username">姓名:</label>
<input type="text" id="username" name="username" placeholder="请输入你的姓名">
</div>
<div class="form-item">
<label for="content">留言内容:</label>
<textarea id="content" name="content" placeholder="请输入你的留言..."></textarea>
</div>
<button type="submit">提交留言</button>
</form>
</section>
</main>
<!-- 底部:版权信息(语义化标签footer) -->
<footer>
<p>© 2025 张三的个人主页 | 版权所有</p>
</footer>
</body>
</html>

课时18:HTML模块总结与查漏补缺
学习目标
回顾HTML所有核心知识点,解决学习中遇到的问题。
核心回顾
- HTML核心:标签(双标签/单标签)+ 属性(src/alt/href等);
- 常用标签分类:
- 文本:h1-h6、p、br、strong/em;
- 媒体:img;
- 链接:a;
- 列表:ul/ol+li;
- 表格:table+tr+td/th;
- 表单:form+input/select/textarea;
- 布局:div/span、语义化标签。
查漏补缺
针对前17课时的易错点,逐一检查自己的代码,比如:
- 标签是否成对;
- 属性值是否加引号;
- 表单的name/for/id是否匹配。
第三部分:CSS(样式)
课时19:CSS引入方式——行内样式
学习目标
掌握最简单的CSS引入方式(行内样式),给元素改样式。
通俗讲解
行内样式是「直接给标签加style属性」,比如<div style="color: red;">,优点是简单,缺点是不能复用,适合临时改样式。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>行内样式</title>
</head>
<body>
<h1 style="color: red; font-size: 28px;">红色大号标题</h1>
<p style="color: #666; line-height: 2;">深灰色段落,行高2倍</p>
</body>
</html>

实操步骤
- 新建
19-行内样式.html,粘贴代码; - 修改style里的属性值(比如color改成green,font-size改成32px);
- 给div加行内样式(width: 200px; height: 100px; background-color: #eee;)。
易错点提醒
- ❌ style里漏写分号:比如
style="color: red font-size:28px",后面的样式失效; - ❌ 样式名写错:比如
style="fontsize:28px"(正确是font-size)。
课后小练习
给个人主页的header加行内样式,设置背景颜色为#f5f5f5。
课时20:CSS引入方式——内嵌样式
学习目标
掌握内嵌样式(style标签),批量修改元素样式,避免重复写行内样式。
通俗讲解
内嵌样式是「把样式写在head里的style标签里」,用选择器选中元素,批量改样式,比行内样式高效,适合单页面样式。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>内嵌样式</title>
<!-- 内嵌样式:style标签写在head里 -->
<style>
/* 标签选择器:选中所有h1标签 */
h1 {
color: green;
font-size: 28px;
}
/* 标签选择器:选中所有p标签 */
p {
color: #666;
line-height: 2;
}
</style>
</head>
<body>
<h1>绿色标题</h1>
<p>第一个段落</p>
<p>第二个段落(和第一个样式一样)</p>
</body>
</html>

实操步骤
- 新建
20-内嵌样式.html,粘贴代码; - 新增一个div标签,在style里加div选择器,设置宽高和背景色;
- 对比行内样式和内嵌样式的差异(内嵌样式批量生效)。
易错点提醒
- ❌ style标签放body里:样式不生效,必须放head里;
- ❌ 选择器和样式块之间漏写空格:比如
h1{color:red;}(虽然生效,但规范写法是h1 { ... })。
课后小练习
把个人主页的行内样式改成内嵌样式,批量设置相同元素的样式。
课时21:CSS选择器——标签选择器
学习目标
掌握标签选择器的用法,选中页面中所有同类型标签。
通俗讲解
标签选择器是「直接写标签名」,比如p { ... }选中所有p标签,优点是批量生效,缺点是不能精准控制单个元素。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>标签选择器</title>
<style>
/* 选中所有ul标签 */
ul {
list-style: none; /* 去掉默认圆点 */
padding: 0; /* 去掉默认内边距 */
}
/* 选中所有li标签 */
li {
line-height: 1.8;
color: #333;
}
</style>
</head>
<body>
<ul>
<li>学习HTML</li>
<li>学习CSS</li>
<li>学习JS</li>
</ul>
</body>
</html>

实操步骤
- 新建
21-标签选择器.html,粘贴代码; - 观察ul的圆点是否消失,li的行高是否变化;
- 给table标签加选择器,设置border-collapse: collapse;(合并单元格边框)。
易错点提醒
- ❌ 用标签选择器改单个元素:比如页面有2个h1,只想改其中1个,标签选择器会全改,需要用类选择器;
- ❌ 选择器大小写:比如
P { ... },虽然生效,但规范写小写。
课后小练习
用标签选择器给个人主页的所有a标签设置颜色为#007bff,去掉下划线(text-decoration: none;)。
课时22:CSS选择器——类选择器(核心)
学习目标
掌握类选择器,精准控制指定元素的样式,实现样式复用。
通俗讲解
类选择器是「给元素贴标签」,比如给某个div加class="box",然后用.box { ... }选中它,优点是精准、可复用,是CSS最常用的选择器。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>类选择器</title>
<style>
/* 类选择器:.类名 */
.red-text {
color: red;
}
.big-text {
font-size: 20px;
}
.bg-gray {
background-color: #eee;
padding: 5px;
}
</style>
</head>
<body>
<!-- 一个元素可以加多个类,空格分隔 -->
<p class="red-text">只变红</p>
<p class="red-text big-text">变红+变大</p>
<div class="bg-gray">灰色背景</div>
</body>
</html>

实操步骤
- 新建
22-类选择器.html,粘贴代码; - 新增一个
.center-text类(text-align: center;),给h1加这个类,看文字是否居中; - 尝试把类名写错(比如red-text写成redtext),看样式是否失效。
易错点提醒
- ❌ 类选择器漏写点:比如
red-text { ... },样式不生效,必须加.; - ❌ 类名有空格:比如
class="red text",实际是两个类(red和text); - ❌ 类名以数字开头:比如
.1box { ... },语法错误。
课后小练习
给个人主页的“留言板”区域加类选择器,设置宽600px、背景色#f8f8f8、内边距20px。
课时23:CSS选择器——ID选择器
学习目标
掌握ID选择器,选中页面中唯一的元素。
通俗讲解
ID选择器是「给元素加唯一标识」,比如<div id="header">,用#header { ... }选中它,一个页面中ID只能出现一次,适合选唯一元素(比如页面头部、底部)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>ID选择器</title>
<style>
/* ID选择器:#ID名 */
#title {
color: blue;
font-size: 32px;
}
</style>
</head>
<body>
<h1 id="title">唯一的标题</h1>
<h1>普通标题(不受ID选择器影响)</h1>
</body>
</html>

实操步骤
- 新建
23-ID选择器.html,粘贴代码; - 尝试给两个h1加相同的ID,看浏览器是否报错(不会报错,但不符合规范);
- 对比类选择器和ID选择器的差异(类可复用,ID唯一)。
易错点提醒
- ❌ 重复使用ID:一个页面中ID只能有一个,重复使用会导致JS获取元素出错;
- ❌ 用ID选择器复用样式:ID是唯一的,复用样式优先用类选择器。
课后小练习
给个人主页的footer加ID选择器,设置文字居中、颜色#999。
课时24:CSS文字样式——颜色、大小、粗细
学习目标
掌握文字的核心样式:color、font-size、font-weight、font-family。
通俗讲解
- color:文字颜色(英文/十六进制/RGB);
- font-size:文字大小(单位px,默认16px);
- font-weight:文字粗细(normal/400 正常,bold/700 加粗);
- font-family:字体(比如“微软雅黑”“宋体”)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文字样式</title>
<style>
.text-style {
color: #333; /* 深灰色 */
font-size: 16px;
font-weight: 500; /* 半加粗 */
font-family: "Microsoft Yahei", sans-serif; /* 微软雅黑,无衬线字体兜底 */
}
</style>
</head>
<body>
<p class="text-style">这是设置了字体样式的文字</p>
</body>
</html>

实操步骤
- 新建
24-文字样式.html,粘贴代码; - 修改color为rgb(255,0,0),font-weight为700,看效果;
- 给body加font-family,统一整个页面的字体。
易错点提醒
- ❌ 漏写字体单位:比如
font-size: 16,样式不生效,必须加px; - ❌ 字体名有空格没加引号:比如
font-family: Microsoft Yahei,要加引号("Microsoft Yahei"); - ❌ 用数字写font-weight:比如
font-weight: bold和font-weight: 700效果一样,新手记700加粗即可。
课后小练习
统一个人主页的字体为“Microsoft Yahei”,设置所有p标签的font-size为14px,line-height为1.8。
课时25:CSS文字样式——行高、对齐、装饰
学习目标
掌握line-height、text-align、text-decoration的用法。
通俗讲解
- line-height:行高,文字行之间的距离,设为数值(比如2)是字体大小的倍数,设为和容器高度相同可垂直居中;
- text-align:文字水平对齐(left/center/right);
- text-decoration:文字装饰(none 去掉下划线,underline 加下划线,line-through 删除线)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文字样式进阶</title>
<style>
.box {
width: 300px;
height: 100px;
background-color: #eee;
text-align: center; /* 水平居中 */
line-height: 100px; /* 垂直居中(和容器高度相同) */
}
a {
text-decoration: none; /* 去掉下划线 */
}
.del-text {
text-decoration: line-through; /* 删除线 */
}
</style>
</head>
<body>
<div class="box">文字水平+垂直居中</div>
<a href="#">无下划线的链接</a>
<p class="del-text">带删除线的文字</p>
</body>
</html>

实操步骤
- 新建
25-文字样式进阶.html,粘贴代码; - 修改line-height为1.8,看垂直居中是否失效;
- 给个人主页的导航链接加hover样式(a:hover { text-decoration: underline; })。
易错点提醒
- ❌ 行高设为数值加px导致垂直居中失效:比如容器高度100px,line-height设为100px才会垂直居中,设为100则是字体大小的100倍;
- ❌ 给块级元素加text-decoration:只会影响元素内的文字,不会影响子元素(比如div加underline,里面的a标签不会有下划线)。
课后小练习
给个人主页的h1标签设置text-align: center,给“已完成的经历”加删除线样式。
课时26:CSS背景样式——背景颜色、背景图片
学习目标
掌握background-color、background-image的用法。
通俗讲解
- background-color:背景颜色,可设为颜色值或transparent(透明);
- background-image:背景图片,用url()指定图片地址,默认重复平铺。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>背景样式</title>
<style>
.bg-color {
width: 200px;
height: 100px;
background-color: #007bff; /* 蓝色背景 */
color: white; /* 文字白色 */
}
.bg-img {
width: 300px;
height: 200px;
background-image: url("https://picsum.photos/300/200");
background-repeat: no-repeat; /* 不平铺 */
background-size: cover; /* 覆盖容器 */
}
</style>
</head>
<body>
<div class="bg-color">纯色背景</div>
<div class="bg-img">背景图片</div>
</body>
</html>

实操步骤
- 新建
26-背景样式.html,粘贴代码; - 修改background-repeat为repeat,看图片是否平铺;
- 修改background-size为contain,看图片是否完整显示。
易错点提醒
- ❌ 背景图片容器没设宽高:背景图片不会显示,必须给容器设置宽高;
- ❌ url里的图片地址漏加引号:比如
url(https://picsum.photos/300/200),虽然生效,但建议加引号; - ❌ 背景图片和文字重叠:给文字加背景色或调整文字颜色,保证可读性。
课后小练习
给个人主页的header加背景颜色#f8f8f8,给main加轻微的背景图片(平铺)。
课时27:CSS边框样式——border
学习目标
掌握border的用法,给元素加边框。
通俗讲解
border是「元素的边框」,格式:border: 粗细 样式 颜色;,比如border: 1px solid #ccc;(1像素实线灰色边框);也可以单独设置某一边(border-top、border-left)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>边框样式</title>
<style>
.box1 {
width: 200px;
height: 100px;
border: 1px solid #ccc; /* 全边框 */
margin-bottom: 10px;
}
.box2 {
width: 200px;
height: 100px;
border-top: 2px solid red; /* 只加顶部边框 */
border-left: 1px dashed blue; /* 左侧虚线边框 */
}
</style>
</head>
<body>
<div class="box1">全边框</div>
<div class="box2">部分边框</div>
</body>
</html>

实操步骤
- 新建
27-边框样式.html,粘贴代码; - 修改border的样式为dashed(虚线)、dotted(点线),看效果;
- 给个人主页的表单输入框加border: 1px solid #ccc;,聚焦时加border: 1px solid #007bff;(:focus)。
易错点提醒
- ❌ 边框样式漏写:比如
border: 1px #ccc;,没有样式(solid/dashed),边框不显示; - ❌ 边框加在行内元素上:行内元素加边框后会影响文字排版,建议转块级(display: block);
- ❌ 边框宽度设为0:边框不显示,新手容易误写。
课后小练习
给个人主页的“留言板”区域加1px solid #eee的边框,设置border-radius: 8px(圆角)。
课时28:CSS盒子模型——width/height
学习目标
理解盒子模型的宽高,区分content-box和border-box。
通俗讲解
CSS盒子模型的宽高默认是「内容区的宽高」(content-box),如果加了边框/内边距,元素实际宽度=width+padding+border;设置box-sizing: border-box后,宽高包含padding和border,更符合直觉。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>盒子模型-宽高</title>
<style>
.box1 {
width: 200px;
height: 100px;
padding: 10px;
border: 1px solid #ccc;
/* 默认content-box:实际宽度=200+20+2=222px */
background-color: #eee;
margin-bottom: 10px;
}
.box2 {
width: 200px;
height: 100px;
padding: 10px;
border: 1px solid #ccc;
box-sizing: border-box; /* 宽高包含padding和border,实际宽度200px */
background-color: #ddd;
}
</style>
</head>
<body>
<div class="box1">content-box(实际更宽)</div>
<div class="box2">border-box(宽200px)</div>
</body>
</html>

实操步骤
- 新建
28-盒子模型-宽高.html,粘贴代码; - 打开浏览器控制台(F12),查看两个盒子的实际宽度;
- 给body加
* { box-sizing: border-box; },统一页面所有元素的盒模型。
易错点提醒
- ❌ 没设box-sizing导致布局错位:新手建议一开始就加
* { box-sizing: border-box; },避免计算宽高出错; - ❌ 给行内元素设宽高:行内元素(span/a)默认不能设宽高,需加
display: block或display: inline-block。
课后小练习
给个人主页的所有元素加border-box盒模型,调整各区域的宽高,避免布局错位。
课时29:CSS盒子模型——padding(内边距)
学习目标
掌握padding的用法,控制元素内部的间距。
通俗讲解
padding是「元素内容和边框之间的距离」,可以设单个值(padding: 10px; 上下左右都10px)、两个值(padding: 10px 20px; 上下10px,左右20px)、四个值(padding: 上 右 下 左)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>盒子模型-padding</title>
<style>
.box {
width: 200px;
height: 100px;
border: 1px solid #ccc;
box-sizing: border-box;
/* 四个值:上10px 右20px 下10px 左20px */
padding: 10px 20px;
background-color: #eee;
}
</style>
</head>
<body>
<div class="box">内容和边框之间有间距,文字不会贴边</div>
</body>
</html>

实操步骤
- 新建
29-盒子模型-padding.html,粘贴代码; - 修改padding为10px(单值)、10px 20px 30px 40px(四值),看间距变化;
- 给个人主页的表单输入框加padding: 8px 10px;,提升输入体验。
易错点提醒
- ❌ padding设为百分比:百分比是相对于父元素宽度的,新手建议用px;
- ❌ 内边距过大导致内容溢出:比如容器高度100px,padding-top设为110px,内容会溢出容器。
课后小练习
给个人主页的header、nav、main、footer分别加合适的padding,避免内容贴边。
课时30:CSS盒子模型——margin(外边距)
学习目标
掌握margin的用法,控制元素之间的间距。
通俗讲解
margin是「元素和其他元素之间的距离」,用法和padding类似,可设单值/双值/四值;margin: 0 auto;可让固定宽度的块级元素水平居中。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>盒子模型-margin</title>
<style>
.box {
width: 200px;
height: 100px;
border: 1px solid #ccc;
margin: 10px 20px; /* 上下10px,左右20px */
background-color: #eee;
}
.center-box {
width: 300px;
height: 100px;
border: 1px solid #ccc;
margin: 0 auto; /* 水平居中 */
background-color: #ddd;
}
</style>
</head>
<body>
<div class="box">有外边距的盒子</div>
<div class="center-box">水平居中的盒子</div>
</body>
</html>

实操步骤
- 新建
30-盒子模型-margin.html,粘贴代码; - 观察两个盒子的间距和居中效果;
- 给个人主页的main区域设置width: 1200px; margin: 0 auto;,让页面主体居中。
易错点提醒
- ❌ 行内元素的margin上下无效:行内元素(span/a)的margin-top/margin-bottom不生效,需转块级;
- ❌ 外边距合并:两个相邻块级元素的margin-top和margin-bottom会合并(取最大值),新手暂时不用处理,知道即可;
- ❌ margin: 0 auto不生效:必须给元素设置固定宽度,否则无效。
课后小练习
调整个人主页各区域的margin,避免元素之间间距过大/过小。
课时31:CSS显示模式——display(block/inline/inline-block)
学习目标
掌握display的核心值,转换元素的显示模式。
通俗讲解
display控制元素的「显示方式」:
- block:块级元素,占一行,可设宽高(div/h1/p/ul/ol/table/form);
- inline:行内元素,和文字同行,不可设宽高(span/a/em/strong);
- inline-block:行内块元素,和文字同行,可设宽高(img/input)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>显示模式</title>
<style>
.inline-box {
display: inline; /* 转成行内元素 */
width: 200px; /* 无效 */
height: 100px; /* 无效 */
background-color: #eee;
}
.inline-block-box {
display: inline-block; /* 转成行内块 */
width: 200px; /* 有效 */
height: 50px; /* 有效 */
background-color: #ddd;
}
</style>
</head>
<body>
<div class="inline-box">行内元素(宽高无效)</div>
<div class="inline-box">和我同行</div>
<br>
<div class="inline-block-box">行内块(宽高有效)</div>
<div class="inline-block-box">和我同行</div>
</body>
</html>

实操步骤
- 新建
31-显示模式.html,粘贴代码; - 观察行内元素和行内块元素的差异;
- 给个人主页的导航li标签加
display: inline-block;,让导航横向排列。
易错点提醒
- ❌ 给行内元素设宽高:必须先转block/inline-block,否则无效;
- ❌ inline-block元素之间有默认间距:多个inline-block元素并排时,标签之间的空格会产生间距,新手可把标签写在同一行(比如
<li>首页</li><li>关于我</li>)。
课后小练习
把个人主页的导航从垂直排列改成水平排列(用inline-block)。
课时32:CSS浮动布局——float(基础)
学习目标
掌握float的基础用法,实现简单的左右排列。
通俗讲解
float是「让元素浮动」,脱离普通文档流,实现“左图右文”“多列布局”,常用值:left(左浮动)、right(右浮动)、none(不浮动)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>浮动布局</title>
<style>
.img-box {
float: left; /* 左浮动 */
width: 100px;
height: 100px;
margin-right: 10px;
}
.text-box {
overflow: hidden; /* 清除浮动影响(简单版) */
}
</style>
</head>
<body>
<img src="https://picsum.photos/100/100" alt="图片" class="img-box">
<div class="text-box">
这是右边的文字,图片浮动到左边啦<br>
多行文字也能和图片并排显示<br>
是不是很简单!
</div>
</body>
</html>

实操步骤
- 新建
32-浮动布局.html,粘贴代码; - 把float改成right,看图片是否浮动到右边;
- 去掉overflow: hidden,看文字是否环绕图片。
易错点提醒
- ❌ 浮动元素没设宽高:布局会混乱,必须给浮动元素设置宽高;
- ❌ 忘记清除浮动:父元素高度会塌陷,新手用
overflow: hidden简单清除; - ❌ 浮动元素太多导致换行:调整宽高和margin,确保总宽度不超过父元素。
课后小练习
给个人主页的header加浮动,让头像左浮动,姓名右浮动。
课时33-37:CSS Flex布局(核心布局,5课时)
课时33:Flex布局——开启Flex容器
学习目标
掌握display: flex,开启Flex布局,理解容器和项目的概念。
通俗讲解
Flex布局是「弹性布局」,给父元素加display: flex,父元素变成Flex容器,子元素变成Flex项目,可轻松控制项目的排列方式,是新手最容易上手的布局方式。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Flex容器</title>
<style>
.container {
display: flex; /* 开启Flex布局 */
width: 400px;
height: 100px;
border: 1px solid #ccc;
}
.item {
width: 100px;
height: 50px;
background-color: #007bff;
color: white;
text-align: center;
line-height: 50px;
margin: 0 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="item">项目1</div>
<div class="item">项目2</div>
<div class="item">项目3</div>
</div>
</body>
</html>

实操任务
- 新建
33-Flex容器.html,粘贴代码; - 观察项目是否横向排列(默认);
- 去掉display: flex,看项目是否变回块级元素(垂直排列)。
课时34:Flex布局——主轴排列(justify-content)
学习目标
掌握justify-content,控制项目在主轴(默认水平)的排列方式。
通俗讲解
justify-content控制项目「水平排列方式」,常用值:
- flex-start:左对齐(默认);
- flex-end:右对齐;
- center:水平居中;
- space-between:两端对齐,项目之间间距相等;
- space-around:项目两侧间距相等。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Flex-主轴排列</title>
<style>
.container {
display: flex;
justify-content: center; /* 水平居中 */
width: 400px;
height: 100px;
border: 1px solid #ccc;
}
.item {
width: 80px;
height: 50px;
background-color: #007bff;
color: white;
line-height: 50px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
</body>
</html>

实操任务
- 依次修改justify-content为flex-end、space-between、space-around,观察排列变化;
- 给个人主页的nav容器加justify-content: space-around,让导航项均匀分布。
课时35:Flex布局——侧轴对齐(align-items)
学习目标
掌握align-items,控制项目在侧轴(默认垂直)的对齐方式。
通俗讲解
align-items控制项目「垂直对齐方式」,常用值:
- flex-start:顶部对齐;
- flex-end:底部对齐;
- center:垂直居中;
- stretch:拉伸(项目没设高度时,占满容器高度)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Flex-侧轴对齐</title>
<style>
.container {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
width: 400px;
height: 150px;
border: 1px solid #ccc;
}
.item {
width: 80px;
height: 50px;
background-color: #007bff;
color: white;
line-height: 50px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
</div>
</body>
</html>

实操任务
- 依次修改align-items为flex-end、stretch,观察变化;
- 给个人主页的header容器加align-items: center,让头像和姓名垂直居中。
课时36:Flex布局——换行(flex-wrap)
学习目标
掌握flex-wrap,控制项目超出容器时是否换行。
通俗讲解
flex-wrap默认值是nowrap(不换行),项目总宽度超过容器时会被压缩;设为wrap则换行,避免项目压缩。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Flex-换行</title>
<style>
.container {
display: flex;
flex-wrap: wrap; /* 换行 */
width: 300px;
height: 150px;
border: 1px solid #ccc;
}
.item {
width: 100px;
height: 50px;
background-color: #007bff;
color: white;
line-height: 50px;
text-align: center;
margin: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
</body>
</html>

实操任务
- 把flex-wrap改回nowrap,看项目是否被压缩;
- 给个人主页的“我的爱好”列表加Flex布局,设置换行,适配小屏幕。
课时37:Flex布局——综合练习(网页头部布局)
学习目标
用Flex布局重构个人主页的头部,实现“头像左、导航右、姓名居中”。
实操任务
- 给header加display: flex; justify-content: space-between; align-items: center;;
- 头像、姓名、导航分别作为Flex项目;
- 调整各项目的宽高和间距,实现美观的头部布局。
参考案例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的个人主页</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
font-family: "微软雅黑", sans-serif;
}
/* 核心修改:头部Flex布局 */
header {
display: flex; /* 开启Flex布局 */
justify-content: space-between; /* 项目两端对齐,中间自动填充 */
align-items: center; /* 项目垂直居中 */
padding: 20px 30px;
border-bottom: 1px solid #ccc;
margin-bottom: 20px;
height: 180px; /* 固定头部高度,保证布局稳定 */
}
/* 头像样式(左) */
.avatar-box {
width: 120px;
flex-shrink: 0; /* 防止头像被压缩 */
}
.avatar-box img {
width: 100%;
height: 120px;
border-radius: 50%;
object-fit: cover; /* 保证图片比例不变形 */
}
/* 姓名样式(中) */
.name-box {
flex: 1; /* 占满中间剩余空间,实现居中 */
text-align: center;
}
.name-box h1 {
font-size: 28px;
color: #333;
letter-spacing: 2px;
}
/* 导航样式(右) */
.nav-box {
width: 400px;
flex-shrink: 0; /* 防止导航被压缩 */
text-align: right;
}
.nav-box a {
margin: 0 10px;
text-decoration: none;
color: #333;
font-size: 16px;
}
.nav-box a:hover {
color: #0088ff;
text-decoration: underline;
}
/* 主体及底部样式(无修改,保留原有效果) */
main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.section {
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
}
.section h2 {
margin-bottom: 15px;
color: #333;
}
ul, ol {
padding-left: 20px;
line-height: 1.8;
}
form {
grid-column: 1 / 3;
}
.form-item {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"], textarea {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
textarea {
height: 150px;
resize: vertical;
}
button {
padding: 8px 20px;
background-color: #0088ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0066cc;
}
footer {
text-align: center;
padding: 10px 0;
border-top: 1px solid #ccc;
color: #666;
}
</style>
</head>
<body>
<!-- 重构后的头部:整合头像、姓名、导航到header中 -->
<header>
<!-- 头像(左) -->
<div class="avatar-box">
<img src="5.jpg" alt="我的头像">
</div>
<!-- 姓名(中) -->
<div class="name-box">
<h1>张三</h1>
</div>
<!-- 导航(右) -->
<div class="nav-box">
<a href="#home">首页</a>
<a href="#hobby">我的爱好</a>
<a href="#experience">我的经历</a>
<a href="#message">留言板</a>
</div>
</header>
<!-- 主体内容(无结构修改) -->
<main>
<section id="hobby" class="section">
<h2>我的爱好</h2>
<ul>
<li>阅读(偏爱技术类书籍)</li>
<li>编程(HTML/CSS/JavaScript)</li>
<li>运动(跑步、羽毛球)</li>
<li>摄影(记录生活点滴)</li>
</ul>
</section>
<section id="experience" class="section">
<h2>我的经历</h2>
<ol>
<li>2018-2022:XX大学 计算机专业 本科</li>
<li>2022-2023:XX科技公司 前端实习生</li>
<li>2023-至今:XX互联网公司 前端开发工程师</li>
</ol>
</section>
<section id="message" class="section">
<h2>留言板</h2>
<form action="#" method="post">
<div class="form-item">
<label for="username">姓名:</label>
<input type="text" id="username" name="username" placeholder="请输入你的姓名">
</div>
<div class="form-item">
<label for="content">留言内容:</label>
<textarea id="content" name="content" placeholder="请输入你的留言..."></textarea>
</div>
<button type="submit">提交留言</button>
</form>
</section>
</main>
<footer>
<p>© 2025 张三的个人主页 | 版权所有</p>
</footer>
</body>
</html>

课时38:CSS定位——相对定位(relative)
学习目标
掌握相对定位,让元素相对于自身位置偏移。
通俗讲解
相对定位是「元素搬家,但原位置还留着」,加position: relative后,用top/right/bottom/left控制偏移,不会脱离文档流,适合微调元素位置。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>相对定位</title>
<style>
.box {
width: 200px;
height: 100px;
background-color: #eee;
position: relative; /* 相对定位 */
top: 20px; /* 向下偏移20px */
left: 30px; /* 向右偏移30px */
}
</style>
</head>
<body>
<div>原位置会留空</div>
<div class="box">相对定位的盒子</div>
</body>
</html>

实操步骤
- 新建
38-相对定位.html,粘贴代码; - 修改top为-20px(向上偏移),left为-30px(向左偏移);
- 给个人主页的“新品”标签加相对定位,微调位置。
易错点提醒
- ❌ 相对定位脱离文档流:相对定位不会脱离文档流,原位置会保留;
- ❌ 没加top/left:只加position: relative,元素位置不变;
- ❌ 偏移值用百分比:百分比是相对于父元素的,新手建议用px。
课后小练习
用相对定位调整个人主页中某个文字的位置,微调1-2px。
课时39:CSS定位——绝对定位(absolute)
学习目标
掌握绝对定位,让元素相对于最近的已定位父元素偏移。
通俗讲解
绝对定位是「元素搬家,原位置不留空」,加position: absolute后,元素脱离文档流,相对于最近的加了position(relative/absolute/fixed)的父元素偏移;如果没有,相对于浏览器窗口偏移。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>绝对定位</title>
<style>
.parent {
position: relative; /* 父元素相对定位,作为参考 */
width: 300px;
height: 200px;
border: 1px solid #ccc;
margin: 50px;
}
.child {
position: absolute; /* 绝对定位 */
top: 20px;
right: 20px;
width: 100px;
height: 50px;
background-color: #007bff;
color: white;
}
</style>
</head>
<body>
<div class="parent">
父元素(相对定位)
<div class="child">绝对定位的子元素</div>
</div>
</body>
</html>

实操步骤
- 新建
39-绝对定位.html,粘贴代码; - 去掉父元素的position: relative,看子元素是否相对于浏览器窗口偏移;
- 给个人主页的“关闭”按钮加绝对定位,放在右上角。
易错点提醒
- ❌ 绝对定位没设参考父元素:元素会相对于浏览器窗口偏移,新手一定要给父元素加position: relative;
- ❌ 绝对定位元素没设宽高:宽高默认由内容决定,可能导致布局混乱;
- ❌ 绝对定位和浮动同时用:绝对定位会覆盖浮动,只需要选一种即可。
课后小练习
用绝对定位给个人主页加一个“回到顶部”按钮,放在页面右下角。
课时40:CSS定位——固定定位(fixed)
学习目标
掌握固定定位,让元素固定在浏览器窗口的某个位置。
通俗讲解
固定定位是「元素固定在窗口,滚动页面也不动」,加position: fixed后,元素脱离文档流,相对于浏览器窗口偏移,适合做导航栏、回到顶部按钮。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>固定定位</title>
<style>
.back-top {
position: fixed; /* 固定定位 */
bottom: 50px; /* 距离窗口底部50px */
right: 50px; /* 距离窗口右侧50px */
width: 50px;
height: 50px;
background-color: #007bff;
color: white;
text-align: center;
line-height: 50px;
text-decoration: none;
border-radius: 50%; /* 圆形按钮 */
}
/* 占位文字,让页面能滚动 */
.content {
height: 2000px;
}
</style>
</head>
<body>
<div class="content">滚动页面看看,按钮不会动</div>
<a href="#" class="back-top">↑</a>
</body>
</html>

实操步骤
- 新建
40-固定定位.html,粘贴代码; - 滚动页面,观察“回到顶部”按钮是否固定在右下角;
- 修改bottom/right的值,调整按钮位置。
易错点提醒
- ❌ 固定定位相对于父元素:固定定位只相对于浏览器窗口,和父元素无关;
- ❌ 固定定位元素被遮挡:可加
z-index: 999;提高层级(z-index只对定位元素生效); - ❌ 固定导航栏遮挡内容:给页面主体加
padding-top,值等于导航栏高度。
课后小练习
给个人主页加固定导航栏,滚动页面时导航栏始终在顶部。
CSS综合练习
课时41:综合练习1——美化个人主页样式
学习目标
整合CSS样式,给个人主页添加颜色、字体、间距、边框等样式,提升美观度。
实操任务
- 统一页面字体为“Microsoft Yahei”,设置body文字颜色#333,背景色#f5f5f5;
- 给header设置背景色白色,加阴影(box-shadow: 0 2px 4px rgba(0,0,0,0.1));
- 给导航链接设置hover样式(颜色变红、加下划线);
- 给表单输入框加聚焦样式(边框变色、加阴影)。
参考案例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的个人主页</title>
<style>
/* 全局样式重置 - 基础优化 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* ========== 任务1:统一页面字体、body文字色和背景色 ========== */
body {
font-family: "Microsoft Yahei", sans-serif; /* 统一字体为微软雅黑 */
color: #333; /* 文字主色 */
background-color: #f5f5f5; /* 页面背景色 */
line-height: 1.6; /* 行高优化,提升阅读体验 */
padding: 0;
margin: 0;
}
/* ========== 任务2:美化header - 白色背景+阴影 ========== */
header {
background-color: #fff; /* 白色背景 */
box-shadow: 0 2px 4px rgba(0,0,0,0.1); /* 阴影效果 */
padding: 20px 30px; /* 内边距,增加间距 */
margin-bottom: 30px; /* 与下方内容间距 */
}
/* 导航样式基础 */
.nav {
list-style: none;
display: flex;
gap: 30px; /* 导航项间距 */
}
.nav li a {
text-decoration: none;
color: #333; /* 导航默认颜色 */
font-size: 16px;
padding: 5px 0; /* 增加点击区域 */
transition: all 0.3s; /* 过渡动画,hover更丝滑 */
}
/* ========== 任务3:导航链接hover样式 - 变红+下划线 ========== */
.nav li a:hover {
color: red; /* hover文字变红 */
text-decoration: underline; /* hover添加下划线 */
text-underline-offset: 4px; /* 下划线偏移,更美观 */
}
/* 内容容器 - 优化整体布局 */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 表单基础样式 */
.form-section {
background-color: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
margin-bottom: 30px;
}
.form-section h2 {
margin-bottom: 20px;
color: #333;
font-size: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
/* 表单输入框基础样式 */
.form-control {
width: 100%;
max-width: 400px;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
font-family: "Microsoft Yahei", sans-serif; /* 继承统一字体 */
}
/* ========== 任务4:表单输入框聚焦样式 - 边框变色+阴影 ========== */
.form-control:focus {
outline: none; /* 清除默认聚焦轮廓 */
border-color: #007bff; /* 聚焦边框变色(蓝色更醒目) */
box-shadow: 0 0 5px rgba(0,123,255,0.3); /* 聚焦阴影效果 */
}
/* 按钮样式优化(配套提升美观度) */
.btn {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: "Microsoft Yahei", sans-serif;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<header>
<ul class="nav">
<li><a href="#home">首页</a></li>
<li><a href="#about">关于我</a></li>
<li><a href="#hobby">我的爱好</a></li>
<li><a href="#message">留言板</a></li>
</ul>
</header>
<!-- 内容容器 -->
<div class="container">
<!-- 留言表单区域 -->
<section class="form-section" id="message">
<h2>留言板</h2>
<form>
<div class="form-group">
<label for="username">姓名</label>
<input type="text" id="username" class="form-control" placeholder="请输入您的姓名">
</div>
<div class="form-group">
<label for="content">留言内容</label>
<textarea id="content" class="form-control" rows="5" placeholder="请输入留言内容"></textarea>
</div>
<button type="submit" class="btn">提交留言</button>
</form>
</section>
</div>
</body>
</html>

课时42:综合练习2——用Flex布局优化个人主页布局
学习目标
用Flex布局重构个人主页的所有区域,实现响应式基础布局。
实操任务
- 给header加Flex布局,实现头像、姓名、导航的合理排列;
- 给main加Flex布局,让“我的爱好”“我的经历”“留言板”横向排列(大屏)/纵向排列(小屏);
- 给footer加Flex布局,让版权信息水平垂直居中。
参考代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的个人主页</title>
<style>
/* 全局重置 & 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Microsoft Yahei", sans-serif;
color: #333;
background-color: #f5f5f5;
line-height: 1.6;
min-height: 100vh; /* 让body占满视口高度,方便footer置底 */
display: flex;
flex-direction: column; /* 让header-main-footer垂直排列 */
}
/* ========== 1. Header Flex布局:头像+姓名+导航 ========== */
header {
background-color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 15px 30px;
/* 核心:开启Flex布局 */
display: flex;
align-items: center; /* 垂直居中 */
justify-content: space-between; /* 左右分布(头像+姓名左,导航右) */
gap: 20px; /* 元素间间距 */
flex-wrap: wrap; /* 小屏时自动换行,避免挤压 */
}
/* 头像样式 */
.avatar {
width: 60px;
height: 60px;
border-radius: 50%; /* 圆形头像 */
overflow: hidden;
flex-shrink: 0; /* 头像不收缩 */
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 姓名区域 */
.name-card {
flex-shrink: 0; /* 姓名不收缩 */
}
.name-card h1 {
font-size: 20px;
margin-bottom: 5px;
}
.name-card p {
font-size: 12px;
color: #999;
}
/* 导航样式 */
.nav {
list-style: none;
/* 导航内部也用Flex */
display: flex;
gap: 25px;
}
.nav li a {
text-decoration: none;
color: #333;
font-size: 16px;
padding: 5px 0;
transition: all 0.3s;
}
.nav li a:hover {
color: red;
text-decoration: underline;
text-underline-offset: 4px;
}
/* ========== 2. Main Flex布局:大屏横向/小屏纵向 ========== */
main {
flex: 1; /* 占据剩余空间,让footer置底 */
max-width: 1200px;
margin: 0 auto;
padding: 30px 20px;
/* 核心:开启Flex布局 */
display: flex;
gap: 25px; /* 模块间间距 */
flex-wrap: wrap; /* 小屏自动换行 */
justify-content: flex-start; /* 大屏时靠左排列 */
}
/* 每个模块的基础样式 */
.module {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
/* 大屏时:每个模块均分宽度(减去gap后) */
flex: 1;
min-width: 280px; /* 小屏时最小宽度,避免太窄 */
}
.module h2 {
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
font-size: 18px;
}
/* 表单输入框样式 */
.form-control {
width: 100%;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 15px;
font-family: "Microsoft Yahei", sans-serif;
}
.form-control:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 5px rgba(0,123,255,0.3);
}
.btn {
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #0056b3;
}
/* ========== 3. Footer Flex布局:版权信息居中 ========== */
footer {
background-color: #333;
color: #fff;
padding: 20px 0;
/* 核心:开启Flex布局 */
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
.copyright {
font-size: 14px;
text-align: center;
}
/* 响应式适配:小屏(768px以下)优化 */
@media (max-width: 768px) {
header {
justify-content: center; /* 小屏时header元素居中 */
text-align: center;
}
.nav {
margin-top: 15px; /* 换行后增加间距 */
justify-content: center; /* 导航居中 */
width: 100%; /* 导航占满宽度 */
}
main {
flex-direction: column; /* 小屏强制纵向排列 */
}
.module {
min-width: 100%; /* 小屏模块占满宽度 */
}
}
</style>
</head>
<body>
<!-- Header:头像+姓名+导航 -->
<header>
<div class="avatar">
<img src="https://picsum.photos/60/60?random=1" alt="我的头像">
</div>
<div class="name-card">
<h1>张三</h1>
<p>前端学习中 | 热爱技术</p>
</div>
<ul class="nav">
<li><a href="#hobby">我的爱好</a></li>
<li><a href="#experience">我的经历</a></li>
<li><a href="#message">留言板</a></li>
</ul>
</header>
<!-- Main:三个模块横向/纵向排列 -->
<main>
<section class="module" id="hobby">
<h2>我的爱好</h2>
<ul>
<li>学习HTML/CSS/JS</li>
<li>写前端小项目</li>
<li>阅读技术文档</li>
<li>和同行交流学习</li>
</ul>
</section>
<section class="module" id="experience">
<h2>我的经历</h2>
<p>2025年1月:开始学习前端基础</p>
<p>2025年2月:完成个人主页项目</p>
<p>2025年3月:学习Flex布局和响应式</p>
</section>
<section class="module" id="message">
<h2>留言板</h2>
<form>
<input type="text" class="form-control" placeholder="请输入您的姓名">
<textarea class="form-control" rows="3" placeholder="请输入留言内容"></textarea>
<button type="submit" class="btn">提交留言</button>
</form>
</section>
</main>
<!-- Footer:版权信息居中 -->
<footer>
<div class="copyright">
<p>© 2025 张三的个人主页 | 前端基础学习笔记</p>
</div>
</footer>
</body>
</html>

小屏显示效果:

课时43:CSS模块总结与查漏补缺
学习目标
回顾CSS核心知识点,解决样式不生效、布局错位等问题。
核心回顾
- CSS引入方式:行内(临时)、内嵌(单页面)、外部(多页面,后续拓展);
- 选择器优先级:ID选择器 > 类选择器 > 标签选择器;
- 布局核心:盒子模型(宽高/padding/margin)、Flex布局、定位(辅助);
- 常见问题:样式不生效(选择器写错/属性值错误)、布局错位(盒模型没设border-box/浮动没清除)。
查漏补缺
针对前24课时的易错点,逐一检查个人主页的CSS代码:
- 选择器是否写错(类选择器漏点、ID选择器漏#);
- 盒模型是否统一设为border-box;
- Flex布局的容器和项目是否设置正确;
- 定位元素是否有正确的参考父元素。
第四部分:JS(交互)
课时44:JS引入方式——行内/内嵌
学习目标
掌握JS的两种基础引入方式,执行第一个JS代码。
通俗讲解
JS引入方式和CSS类似:
- 行内JS:直接写在标签事件里(比如
onclick="alert('你好')"),适合简单交互; - 内嵌JS:写在
<script>标签里(通常放body底部),适合单页面脚本。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS引入方式</title>
</head>
<body>
<!-- 行内JS:点击按钮弹窗 -->
<button onclick="alert('行内JS生效了!')">点击我(行内JS)</button>
<!-- 内嵌JS:放body底部,避免DOM未加载完成 -->
<script>
// 控制台输出(F12打开控制台查看)
console.log('内嵌JS生效了!');
// 页面弹出提示
alert('这是内嵌JS的提示!');
</script>
</body>
</html>

实操步骤
- 新建
44-JS引入方式.html,粘贴代码; - 点击按钮,看行内JS的弹窗效果;
- 打开浏览器控制台(F12→Console),查看console.log的输出。
易错点提醒
- ❌ script标签放head里:DOM未加载完成时,获取元素会失败,新手先放body底部;
- ❌ 代码拼写错误:比如
alert写成alet,控制台会报错,要学会看报错信息; - ❌ 字符串漏加引号:比如
alert(你好),会报错,字符串必须加单/双引号。
课后小练习
给个人主页的“留言提交”按钮加行内JS,点击弹窗“留言提交成功!”。
课时45:JS变量——声明和赋值
学习目标
掌握变量的声明(let/const/var)和赋值,理解变量的作用。
通俗讲解
变量是「存放数据的容器」,比如用let name = '张三'把“张三”存到name容器里,后续可直接用name调用这个值:
- let:可修改的变量(常用);
- const:不可修改的常量(比如PI=3.14);
- var:旧版声明方式,新手优先用let/const。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS变量</title>
</head>
<body>
<script>
// 声明变量并赋值
let username = '前端新手'; // 字符串
let age = 18; // 数字
const PI = 3.14; // 常量(不可修改)
// 输出变量
console.log('用户名:', username);
console.log('年龄:', age);
console.log('圆周率:', PI);
// 修改变量(let可改,const不可改)
age = 19;
console.log('修改后的年龄:', age);
// PI = 3.1415; // 报错:常量不能修改
</script>
</body>
</html>

实操步骤
- 新建
45-JS变量.html,粘贴代码; - 尝试修改PI的值,看控制台报错信息;
- 声明自己的变量(比如hobby='学习JS'),并输出到控制台。
易错点提醒
- ❌ 变量名有空格/特殊字符:比如
let user name = '张三',报错,变量名只能是字母/数字/下划线/$,且不能以数字开头; - ❌ 重复声明let变量:比如
let age=18; let age=19;,报错,const同理; - ❌ 常量赋值后修改:const声明的变量必须初始化,且不能修改。
课后小练习
声明3个变量:name(你的名字)、gender(性别)、score(学习进度80),并输出到控制台。
课时46:JS数据类型——基本类型(字符串/数字/布尔)
学习目标
掌握JS的3种核心基本数据类型,能区分和转换类型。
通俗讲解
JS的基础数据类型就像不同类型的“容器”:
- 字符串(String):放文字,必须加引号('张三'、"123");
- 数字(Number):放数字,不用加引号(18、3.14、-5);
- 布尔(Boolean):只有两个值,true(真)/false(假),用于判断。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS数据类型</title>
</head>
<body>
<script>
// 1. 字符串
let str1 = 'Hello JS';
let str2 = "123456"; // 数字加引号也是字符串
// 2. 数字
let num1 = 100;
let num2 = 3.14;
// 3. 布尔
let isStudy = true;
let isSleep = false;
// 查看数据类型(typeof)
console.log(typeof str1); // string
console.log(typeof num1); // number
console.log(typeof isStudy); // boolean
// 类型转换:字符串转数字
let strNum = '18';
let num = Number(strNum);
console.log(num + 2); // 20(数字运算)
console.log(strNum + 2); // 182(字符串拼接)
</script>
</body>
</html>

实操步骤
- 新建
46-JS数据类型.html,粘贴代码; - 用typeof查看不同变量的类型,理解字符串和数字的区别;
- 尝试把布尔值转数字(Number(true)=1,Number(false)=0)。
易错点提醒
- ❌ 字符串和数字相加:比如'18'+2=182(拼接),不是20,要先转数字;
- ❌ 布尔值直接用数字判断:true等价于1,false等价于0,但不要混用;
- ❌ 空字符串转数字:Number('')=0,新手要注意这个特性。
课后小练习
声明一个字符串变量('99'),转成数字后加1,输出结果(100)。
课时47:JS数据类型——特殊类型(null/undefined)
学习目标
掌握null和undefined的区别,理解“空值”的含义。
通俗讲解
这两种是“空容器”,但含义不同:
- undefined:变量声明了但没赋值(比如
let a;),或者访问不存在的属性,是“默认空”; - null:主动赋值为“空”(比如
let b = null;),表示“故意为空”。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS特殊数据类型</title>
</head>
<body>
<script>
// 1. undefined:声明未赋值
let a;
console.log(a); // undefined
console.log(typeof a); // undefined
// 2. null:主动赋值为空
let b = null;
console.log(b); // null
console.log(typeof b); // object(历史遗留问题,记住即可)
// 3. 区别:转数字
console.log(Number(undefined)); // NaN(非数字)
console.log(Number(null)); // 0
</script>
</body>
</html>

实操步骤
- 新建
47-JS特殊类型.html,粘贴代码; - 观察undefined和null的输出差异;
- 尝试访问对象不存在的属性(比如
let obj = {}; console.log(obj.name);),结果是undefined。
易错点提醒
- ❌ 混淆null和undefined:记住“undefined是默认空,null是主动空”;
- ❌ 认为typeof null是null:实际是object,这是JS的历史bug,不用纠结,记住即可;
- ❌ 用undefined赋值:比如
let a = undefined;,不推荐,主动空用null。
课后小练习
声明变量c,先不赋值(输出undefined),再赋值为null,观察控制台输出变化。
课时48:JS运算符——算术运算符
学习目标
掌握+、-、*、/、%等算术运算符,实现基本的数学运算。
通俗讲解
算术运算符和数学里的运算规则一致:
- +:加法(数字相加/字符串拼接);
- -:减法;
- *:乘法;
- /:除法;
- %:取余(求余数,比如7%3=1)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS算术运算符</title>
</head>
<body>
<script>
let num1 = 10;
let num2 = 3;
// 加法
console.log(num1 + num2); // 13
// 减法
console.log(num1 - num2); // 7
// 乘法
console.log(num1 * num2); // 30
// 除法
console.log(num1 / num2); // 3.333...
// 取余
console.log(num1 % num2); // 1
// 注意:字符串拼接
console.log('10' + 3); // 103
console.log(10 + '3'); // 103
</script>
</body>
</html>

实操步骤
- 新建
48-JS算术运算符.html,粘贴代码; - 尝试修改num1和num2的值,观察运算结果;
- 用取余判断数字是否为偶数(num%2===0 是偶数)。
易错点提醒
- ❌ 除法结果是小数:JS的除法不会自动取整,比如10/3=3.333,取整用Math.floor()/Math.ceil();
- ❌ 取余符号写错:是%不是&,新手容易混淆;
- ❌ 字符串和数字相加:优先拼接,不是运算,要先转数字。
课后小练习
计算“80课时,每天学2课时,需要多少天”,用除法运算并输出结果(40)。
课时49:JS运算符——赋值/比较运算符
学习目标
掌握赋值运算符(=、+=、-=)和比较运算符(==、===、>、<)。
通俗讲解
- 赋值运算符:给变量赋值,
+=是“累加”(比如a+=2等价于a=a+2); - 比较运算符:判断两个值的关系,返回布尔值(true/false),
===是严格相等(值+类型都相等),优先用===。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS赋值/比较运算符</title>
</head>
<body>
<script>
// 1. 赋值运算符
let a = 5;
a += 3; // 等价于a = a + 3
console.log(a); // 8
a -= 2; // 等价于a = a - 2
console.log(a); // 6
// 2. 比较运算符
let num1 = 18;
let num2 = '18';
console.log(num1 == num2); // true(只比数值)
console.log(num1 === num2); // false(类型不同)
console.log(num1 > 10); // true
console.log(num1 < 5); // false
</script>
</body>
</html>

实操步骤
- 新建
49-JS赋值比较运算符.html,粘贴代码; - 理解==和===的区别,记住优先用===;
- 用比较运算符判断“年龄是否大于等于18”,输出布尔值。
易错点提醒
- ❌ 把=当==:
if(a=5)是赋值,不是判断,判断要用==/===; - ❌ 混用==和===:比如'18'===18是false,新手优先用===避免类型问题;
- ❌ 比较运算符顺序错:比如a > b < c 不是数学里的连续比较,要写成a > b && b < c。
课后小练习
声明变量score=85,判断是否大于等于80且小于90,输出布尔值(true)。
课时50:JS运算符——逻辑运算符
学习目标
掌握&&(与)、||(或)、!(非)逻辑运算符,实现多条件判断。
通俗讲解
逻辑运算符用于组合多个条件,返回布尔值:
- &&(与):所有条件都为true,结果才是true(比如“年龄≥18 且 有身份证”);
- ||(或):只要有一个条件为true,结果就是true(比如“有手机 或 有电脑”);
- !(非):取反,true变false,false变true(比如!true=false)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS逻辑运算符</title>
</head>
<body>
<script>
let age = 20;
let hasId = true;
let hasPhone = false;
let hasComputer = true;
// &&(与):都真才真
console.log(age >= 18 && hasId); // true
// ||(或):一真就真
console.log(hasPhone || hasComputer); // true
// !(非):取反
console.log(!hasPhone); // true
console.log(!(age > 30)); // true(age>30是false,取反为true)
</script>
</body>
</html>

实操步骤
- 新建
50-JS逻辑运算符.html,粘贴代码; - 修改age=17,观察&&的结果是否变成false;
- 用逻辑运算符判断“分数≥60 且 <100”,输出是否及格。
易错点提醒
- ❌ 逻辑运算符顺序错:&&优先级高于||,复杂判断加括号(比如(a||b)&&c);
- ❌ 混淆&&和||:记住“&&是且,||是或”;
- ❌ 非运算符用错:!只能加在布尔值前,比如!5是false(非0数字转布尔是true,取反为false)。
课后小练习
声明变量hour=19,判断是否是“晚上”(hour≥18 且 hour<24),输出布尔值(true)。
课时51:JS条件语句——if语句
学习目标
掌握if基本语句,实现“满足条件就执行代码”的逻辑。
通俗讲解
if语句是「条件判断」,就像“如果…就…”:
if (条件) {
// 条件为true时执行的代码
}
条件必须返回布尔值,非布尔值会自动转换(比如0=false,非0=true)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS if语句</title>
</head>
<body>
<script>
let score = 85;
// 基本if语句
if (score >= 60) {
console.log('及格了!');
alert('恭喜,你的分数是' + score + ',及格啦!');
}
// if-else语句:二选一
if (score >= 90) {
console.log('优秀');
} else {
console.log('良好');
}
</script>
</body>
</html>

实操步骤
- 新建
51-JS if语句.html,粘贴代码; - 修改score=59,观察是否输出“及格了!”(不输出),else部分输出“良好”;
- 用if语句判断年龄是否≥18,弹窗“成年”或“未成年”。
易错点提醒
- ❌ 条件括号漏写:比如
if score>=60,报错,条件必须加(); - ❌ 代码块漏写{}:单条语句可以省略,但新手建议都加,避免逻辑混乱;
- ❌ 条件是赋值语句:比如
if(score=60),永远为true,要写if(score===60)。
课后小练习
声明变量time=22,用if-else判断是否超过22点,弹窗“该睡觉了”或“还可以学习一会儿”。
课时52:JS条件语句——if-else if-else
学习目标
掌握多条件判断语句,实现“多选一”的逻辑。
通俗讲解
if-else if-else是「多条件分支」,就像“如果…就…,否则如果…就…,否则…”,按顺序判断,满足第一个条件后不再执行后续判断。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS if-else if-else</title>
</head>
<body>
<script>
let score = 85;
// 多条件判断
if (score >= 90) {
console.log('优秀');
alert('成绩优秀!');
} else if (score >= 80) {
console.log('良好');
alert('成绩良好!');
} else if (score >= 60) {
console.log('及格');
alert('成绩及格!');
} else {
console.log('不及格');
alert('成绩不及格,继续努力!');
}
</script>
</body>
</html>

实操步骤
- 新建
52-JS if-else if-else.html,粘贴代码; - 依次修改score为95、75、55,观察不同的弹窗结果;
- 用多条件判断“成绩等级”:100-90优秀,89-80良好,79-70中等,69-60及格,<60不及格。
易错点提醒
- ❌ 条件顺序错:比如先判断≥60,再判断≥90,会导致90分也被判定为及格;
- ❌ 漏写else:多条件判断可以没有else,但建议加,处理所有情况;
- ❌ 条件重叠:比如≥80和≥70重叠,按顺序执行第一个满足的条件。
课后小练习
声明变量month=7,判断季节(3-5春,6-8夏,9-11秋,12-2冬),弹窗对应的季节。
课时53:JS条件语句——switch语句
学习目标
掌握switch语句,实现“固定值匹配”的多条件判断。
通俗讲解
switch语句适合「固定值的多分支判断」(比如匹配月份、星期),比if-else if更简洁:
switch (变量) {
case 值1:
// 变量===值1时执行
break; // 跳出switch,避免执行后续case
case 值2:
// 变量===值2时执行
break;
default:
// 都不匹配时执行
}
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS switch语句</title>
</head>
<body>
<script>
let week = 3; // 1-周一,2-周二...7-周日
switch (week) {
case 1:
console.log('周一:学习HTML');
break;
case 2:
console.log('周二:学习CSS');
break;
case 3:
console.log('周三:学习JS');
break;
default:
console.log('周末:休息+复习');
}
</script>
</body>
</html>

实操步骤
- 新建
53-JS switch语句.html,粘贴代码; - 修改week为7,观察是否执行default;
- 去掉break,看是否会执行后续case(穿透效果)。
易错点提醒
- ❌ 漏写break:会导致case穿透(执行完当前case继续执行下一个);
- ❌ switch判断类型:用===严格匹配,比如case '3'和week=3不匹配;
- ❌ 复杂条件用switch:switch适合固定值匹配,范围判断(比如≥80)用if-else if。
课后小练习
用switch语句判断月份(1-12)对应的季节,弹窗季节名称。
课时54:JS循环——for循环(基础)
学习目标
掌握for循环的基本用法,实现重复执行代码。
通俗讲解
for循环是「重复执行固定次数的代码」,就像“循环做10次题”,语法:
for (初始化变量; 循环条件; 变量更新) {
// 循环体:满足条件时执行
}
比如for(let i=0; i<5; i++)会执行5次循环体。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS for循环</title>
</head>
<body>
<script>
// 循环输出1-5
for (let i = 1; i <= 5; i++) {
console.log('第' + i + '次循环');
}
// 计算1-100的和
let sum = 0;
for (let i = 1; i <= 100; i++) {
sum += i; // 累加
}
console.log('1-100的和:', sum); // 5050
</script>
</body>
</html>

实操步骤
- 新建
54-JS for循环.html,粘贴代码; - 修改循环条件为
i<10,观察循环执行次数(9次); - 用for循环计算1-50的偶数和。
易错点提醒
- ❌ 循环条件写死:比如
i=10,循环只执行一次; - ❌ 变量更新漏写:比如没有
i++,循环变成死循环(浏览器会卡死); - ❌ 初始化变量重复声明:比如
for(let i=1; i<5; let i++),报错。
课后小练习
用for循环输出10次“我要学好前端!”,每次输出的数字从1到10。
课时55:JS循环——for循环(进阶)
学习目标
掌握for循环遍历数组、跳过/终止循环,提升循环灵活性。
通俗讲解
for循环常用来遍历数组(依次访问数组的每个元素),还可以用continue跳过当前循环、break终止循环:
- continue:跳过本次循环,执行下一次;
- break:直接终止整个循环。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS for循环进阶</title>
</head>
<body>
<script>
// 1. 遍历数组
let hobbies = ['HTML', 'CSS', 'JS', 'Vue'];
for (let i = 0; i < hobbies.length; i++) {
console.log('我的爱好:', hobbies[i]);
}
// 2. continue:跳过偶数
for (let i = 1; i <= 5; i++) {
if (i % 2 === 0) {
continue; // 跳过偶数
}
console.log('奇数:', i); // 1,3,5
}
// 3. break:找到3就终止
for (let i = 1; i <= 5; i++) {
if (i === 3) {
break; // 终止循环
}
console.log('循环次数:', i); // 1,2
}
</script>
</body>
</html>

实操步骤
- 新建
55-JS for循环进阶.html,粘贴代码; - 修改数组hobbies,新增元素,观察遍历结果;
- 用break实现“找到第一个大于80的分数就终止循环”。
易错点提醒
- ❌ 数组索引越界:比如
i<=hobbies.length,会访问到undefined; - ❌ continue/break位置错:要放在条件判断里,否则会直接跳过/终止;
- ❌ 遍历数组用错变量:比如
hobbies[i]写成hobbies,输出整个数组。
课后小练习
声明数组scores=[75,88,92,60,85],用for循环遍历,输出所有≥80的分数。
课时56:JS循环——while循环
学习目标
掌握while循环,实现“满足条件就循环”的逻辑。
通俗讲解
while循环是「先判断条件,再执行循环体」,适合循环次数不确定的场景,语法:
while (循环条件) {
// 循环体:条件为true时执行
// 必须有变量更新,避免死循环
}
比如while(score < 60)会一直循环直到分数≥60。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS while循环</title>
</head>
<body>
<script>
// 输出1-5
let i = 1;
while (i <= 5) {
console.log('while循环:', i);
i++; // 变量更新,避免死循环
}
// 计算直到和≥100
let sum = 0;
let num = 1;
while (sum < 100) {
sum += num;
num++;
}
console.log('和≥100时的num:', num); // 14
console.log('最终和:', sum); // 105
</script>
</body>
</html>

实操步骤
- 新建
56-JS while循环.html,粘贴代码; - 去掉
i++,看是否变成死循环(浏览器卡死,需关闭标签); - 用while循环实现“输入数字直到输入0为止”(结合prompt)。
易错点提醒
- ❌ 忘记变量更新:导致死循环,浏览器会卡死;
- ❌ 条件永远为true:比如
while(true),必须加break终止; - ❌ 初始化变量在循环内:比如
while(i<=5){let i=1; i++;},i永远是1,死循环。
课后小练习
用while循环计算1-10的乘积(1*2*3*...*10=3628800)。
课时57:JS数组——声明/访问/修改
学习目标
掌握数组的基础操作,存储和管理多个数据。
通俗讲解
数组是「存放多个数据的有序列表」,用[]声明,每个元素有索引(从0开始),可以通过索引访问/修改元素:
- 声明:
let arr = [1,2,3]; - 访问:
arr[0](第一个元素); - 修改:
arr[0] = 10。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS数组基础</title>
</head>
<body>
<script>
// 1. 声明数组
let fruits = ['苹果', '香蕉', '橙子'];
let nums = [1, 2, 3, 4, 5];
let mix = ['前端', 80, true]; // 混合类型
// 2. 访问元素(索引从0开始)
console.log('第一个水果:', fruits[0]); // 苹果
console.log('最后一个数字:', nums[nums.length - 1]); // 5
// 3. 修改元素
fruits[1] = '葡萄';
console.log('修改后的数组:', fruits); // ['苹果','葡萄','橙子']
// 4. 数组长度
console.log('水果数组长度:', fruits.length); // 3
</script>
</body>
</html>

实操步骤
- 新建
57-JS数组基础.html,粘贴代码; - 访问mix数组的第二个元素(80),修改为90;
- 声明自己的数组(比如学习计划),访问并修改元素。
易错点提醒
- ❌ 索引从1开始:数组索引从0开始,arr[0]是第一个元素;
- ❌ 访问不存在的索引:比如arr[10],返回undefined;
- ❌ 修改长度导致数据丢失:比如arr.length=2,会删除索引≥2的元素。
课后小练习
声明数组lessons=['HTML','CSS','JS'],添加第四个元素'Vue'(先修改length为4,再赋值lessons[3]='Vue')。
课时58:JS数组——常用方法(增删改查)
学习目标
掌握数组的push/pop/unshift/shift/splice等核心方法,高效操作数组。
通俗讲解
数组提供了便捷方法,不用手动操作索引:
- push():末尾添加元素;
- pop():末尾删除元素;
- unshift():开头添加元素;
- shift():开头删除元素;
- splice(起始索引, 删除个数, 新增元素):任意位置增删改。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS数组方法</title>
</head>
<body>
<script>
let arr = [1, 2, 3];
// 1. 末尾添加
arr.push(4);
console.log('push后:', arr); // [1,2,3,4]
// 2. 末尾删除
arr.pop();
console.log('pop后:', arr); // [1,2,3]
// 3. 开头添加
arr.unshift(0);
console.log('unshift后:', arr); // [0,1,2,3]
// 4. 开头删除
arr.shift();
console.log('shift后:', arr); // [1,2,3]
// 5. 任意位置修改(索引1,删除1个,添加10)
arr.splice(1, 1, 10);
console.log('splice后:', arr); // [1,10,3]
</script>
</body>
</html>

实操步骤
- 新建
58-JS数组方法.html,粘贴代码; - 用splice在索引2的位置添加元素20,观察数组变化;
- 用pop删除最后一个元素,再用push添加回来。
易错点提醒
- ❌ splice参数错:第一个参数是起始索引,第二个是删除个数,不是结束索引;
- ❌ push传多个参数:push(4,5)会添加两个元素,不是数组;
- ❌ 混淆unshift和shift:unshift是加,shift是删,新手记“shift=删除开头”。
课后小练习
声明数组hobbies=['跑步','看书'],用unshift添加'学习前端',用splice在索引2的位置添加'看电影',最终数组为['学习前端','跑步','看电影','看书']。
课时59:JS函数——声明和调用
学习目标
掌握函数的声明和调用,实现代码复用。
通俗讲解
函数是「封装可重复执行的代码块」,就像“定义一个公式,多次使用”:
- 声明:
function 函数名(参数1, 参数2) { 代码块; return 返回值; }; - 调用:
函数名(参数值)。 参数是“输入”,返回值是“输出”。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS函数基础</title>
</head>
<body>
<script>
// 1. 声明函数:计算两数之和
function add(a, b) {
let sum = a + b;
return sum; // 返回结果
}
// 2. 调用函数
let result1 = add(10, 20);
let result2 = add(5, 8);
console.log('10+20=', result1); // 30
console.log('5+8=', result2); // 13
// 3. 无返回值函数
function sayHello(name) {
alert('你好,' + name + '!');
}
sayHello('前端新手'); // 弹窗
</script>
</body>
</html>


实操步骤
- 新建
59-JS函数基础.html,粘贴代码; - 修改add函数的参数,计算3个数之和(添加参数c,sum=a+b+c);
- 声明函数judgeScore(score),判断分数是否及格,返回true/false。
易错点提醒
- ❌ 函数名重复:不能声明两个同名函数,后声明的会覆盖前一个;
- ❌ 参数个数不匹配:调用时参数少了,未传的参数是undefined;
- ❌ 忘记return:函数默认返回undefined,需要返回值必须加return。
课后小练习
声明函数calculateAge(birthYear),根据出生年份计算年龄(2025 - birthYear),调用函数计算自己的年龄。
课时60:JS函数——参数和返回值
学习目标
深入理解函数的参数(默认值/不定参数)和返回值,提升函数灵活性。
通俗讲解
- 参数默认值:给参数设置默认值,调用时不传该参数就用默认值;
- 不定参数:用
...args接收任意个数的参数,变成数组; - 返回值:一个函数只能有一个return,执行return后函数立即结束。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS函数参数和返回值</title>
</head>
<body>
<script>
// 1. 参数默认值
function sayHello(name = '陌生人') {
return '你好,' + name + '!';
}
console.log(sayHello()); // 你好,陌生人!
console.log(sayHello('张三')); // 你好,张三!
// 2. 不定参数:计算任意个数的和
function sum(...args) {
let total = 0;
for (let i = 0; i < args.length; i++) {
total += args[i];
}
return total;
}
console.log(sum(1,2,3)); // 6
console.log(sum(10,20,30,40)); // 100
// 3. 多个return(但只执行第一个)
function judgeNum(num) {
if (num > 0) {
return '正数';
} else if (num < 0) {
return '负数';
} else {
return '零';
}
}
console.log(judgeNum(-5)); // 负数
</script>
</body>
</html>

实操步骤
- 新建
60-JS函数参数返回值.html,粘贴代码; - 给sum函数加默认值,不传参数时返回0;
- 声明函数getMax(...args),返回参数中的最大值。
易错点提醒
- ❌ 默认参数位置错:默认参数要放在参数列表的最后;
- ❌ 不定参数多个:一个函数只能有一个不定参数,且放在最后;
- ❌ return后写代码:return后的代码不会执行,比如
return 1; console.log(2);不会输出2。
课后小练习
声明函数getAverage(...args),计算任意个数的平均值,调用函数计算[80,90,85,95]的平均值(87.5)。
课时61:JS DOM——获取元素(基础)
学习目标
掌握DOM基础,获取页面元素(id/标签/类名)。
通俗讲解
DOM是「文档对象模型」,把HTML标签变成JS可以操作的对象,获取元素是第一步:
- getElementById(id):通过ID获取唯一元素;
- getElementsByTagName(标签名):通过标签名获取元素集合;
- getElementsByClassName(类名):通过类名获取元素集合。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS DOM获取元素</title>
</head>
<body>
<h1 id="title">DOM操作</h1>
<p class="content">第一个段落</p>
<p class="content">第二个段落</p>
<script>
// 1. 通过ID获取(返回单个元素)
let title = document.getElementById('title');
console.log('ID获取:', title); // h1元素
console.log('标题文字:', title.innerText); // DOM操作
// 2. 通过标签名获取(返回集合)
let ps = document.getElementsByTagName('p');
console.log('标签获取数量:', ps.length); // 2
console.log('第一个p:', ps[0].innerText); // 第一个段落
// 3. 通过类名获取(返回集合)
let contents = document.getElementsByClassName('content');
console.log('类名获取第二个:', contents[1].innerText); // 第二个段落
</script>
</body>
</html>

实操步骤
- 新建
61-JS DOM获取元素.html,粘贴代码; - 尝试获取不存在的ID,看返回值(null);
- 遍历ps集合,输出所有p标签的文字。
易错点提醒
- ❌ 获取集合后直接操作:比如ps.innerText,集合没有innerText,要取索引(ps[0].innerText);
- ❌ 脚本放head里:DOM未加载完成,获取元素返回null;
- ❌ 类名/ID拼写错误:返回null或空集合,要检查拼写。
课后小练习
给个人主页的“留言板”div加ID,用getElementById获取该元素,输出其innerHTML。
课时62:JS DOM——获取元素(进阶)
学习目标
掌握querySelector/querySelectorAll,更灵活地获取元素。
通俗讲解
querySelector是「CSS选择器方式获取元素」,比传统方法更灵活:
- querySelector(选择器):返回第一个匹配的元素(支持ID/#、类/.、标签选择器);
- querySelectorAll(选择器):返回所有匹配的元素集合。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS DOM获取元素进阶</title>
</head>
<body>
<div class="box" id="box1">第一个盒子</div>
<div class="box" id="box2">第二个盒子</div>
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
<script>
// 1. querySelector:返回第一个匹配元素
let firstBox = document.querySelector('.box');
console.log('第一个.box:', firstBox.innerText); // 第一个盒子
let box2 = document.querySelector('#box2');
console.log('#box2:', box2.innerText); // 第二个盒子
let li1 = document.querySelector('ul li');
console.log('第一个li:', li1.innerText); // 列表项1
// 2. querySelectorAll:返回所有匹配元素
let boxes = document.querySelectorAll('.box');
console.log('.box数量:', boxes.length); // 2
let lis = document.querySelectorAll('ul li');
lis.forEach(li => { // 遍历集合
console.log('li文字:', li.innerText);
});
</script>
</body>
</html>

实操步骤
- 新建
62-JS DOM获取元素进阶.html,粘贴代码; - 用querySelectorAll获取所有p标签,遍历输出文字;
- 用querySelector获取“ul li:last-child”(最后一个li)。
易错点提醒
- ❌ querySelector返回集合:querySelector只返回第一个元素,要获取所有用querySelectorAll;
- ❌ forEach遍历getElementsByTagName集合:传统集合不能用forEach,querySelectorAll的集合可以;
- ❌ 选择器语法错:比如
.box1 #box2是后代选择器,要检查CSS选择器语法。
课后小练习
用querySelectorAll获取个人主页的所有a标签,遍历修改其颜色为#007bff。
课时63:JS DOM——修改元素内容和样式
学习目标
掌握修改元素的innerText/innerHTML和style样式,实现动态效果。
通俗讲解
- 修改内容:
- innerText:修改纯文字(不会解析HTML标签);
- innerHTML:修改HTML内容(会解析标签,比如
<b>加粗</b>); - 修改样式:
元素.style.样式名 = 值(样式名驼峰命名,比如backgroundColor)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS DOM修改内容和样式</title>
<style>
.box {
width: 200px;
height: 100px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="textBox">原始文字</div>
<div id="styleBox" class="box">修改样式</div>
<script>
// 1. 修改内容
let textBox = document.getElementById('textBox');
textBox.innerText = '修改后的纯文字';
// textBox.innerHTML = '<b>加粗的文字</b>'; // 解析HTML标签
// 2. 修改样式(驼峰命名)
let styleBox = document.getElementById('styleBox');
styleBox.style.backgroundColor = '#007bff';
styleBox.style.color = 'white';
styleBox.style.fontSize = '18px';
</script>
</body>
</html>

实操步骤
- 新建
63-JS DOM修改内容样式.html,粘贴代码; - 切换innerText和innerHTML,观察差异;
- 给styleBox加style.borderRadius = '8px',实现圆角。
易错点提醒
- ❌ 样式名用横线:比如style.background-color,报错,要写成backgroundColor;
- ❌ innerHTML插入不可信内容:比如用户输入的内容,可能导致XSS攻击,新手暂时用innerText;
- ❌ 样式值漏加单位:比如style.fontSize = 18,不生效,要加px。
课后小练习
给个人主页的“学习进度”div加ID,用JS修改其innerText为“已完成80%”,并修改样式为color: green; font-weight: bold;。
课时64:JS DOM——修改元素类名
学习目标
掌握className/classList,批量修改元素样式,实现样式复用。
通俗讲解
直接修改style不够灵活,推荐通过修改类名来切换样式:
- className:替换元素的所有类名(会覆盖原有类);
- classList:更灵活(add添加/remove删除/toggle切换类)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS DOM修改类名</title>
<style>
.box {
width: 200px;
height: 100px;
border: 1px solid #ccc;
}
.active {
background-color: #007bff;
color: white;
border-radius: 8px;
}
.big {
font-size: 20px;
font-weight: bold;
}
</style>
</head>
<body>
<div id="box" class="box">修改类名</div>
<script>
let box = document.getElementById('box');
// 1. className:覆盖原有类
// box.className = 'box active'; // 保留box,添加active
// 2. classList:灵活操作
box.classList.add('active'); // 添加类
box.classList.add('big'); // 再添加一个类
// box.classList.remove('big'); // 删除类
// box.classList.toggle('active'); // 切换类(有则删,无则加)
console.log('当前类名:', box.className); // box active big
</script>
</body>
</html>

实操步骤
- 新建
64-JS DOM修改类名.html,粘贴代码; - 用toggle切换active类,观察样式变化;
- 给个人主页的导航项加active类(选中状态)。
易错点提醒
- ❌ className覆盖原有类:比如box.className='active'会删除box类,要保留需写全;
- ❌ classList.add传多个类:add('active','big')会添加两个类,正确;
- ❌ 类名拼写错误:添加不存在的类,样式不生效,要检查CSS类名。
课后小练习
给个人主页的“留言提交”按钮加点击事件,点击时切换active类(背景色变红)。
课时65:JS事件——点击事件(基础)
学习目标
掌握点击事件(onclick/addEventListener),实现交互的核心逻辑。
通俗讲解
事件是「用户操作触发的动作」,点击事件最常用:
- 行内事件:
<button onclick="函数()">(简单但耦合高); - 动态绑定:
元素.onclick = 函数(中等灵活); - 监听事件:
元素.addEventListener('click', 函数)(推荐,可绑定多个)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS点击事件</title>
</head>
<body>
<!-- 1. 行内点击事件 -->
<button onclick="alert('行内点击!')">按钮1</button>
<!-- 2. 动态绑定 -->
<button id="btn2">按钮2</button>
<!-- 3. 监听事件 -->
<button id="btn3">按钮3</button>
<script>
// 2. 动态绑定点击事件
let btn2 = document.getElementById('btn2');
btn2.onclick = function() {
alert('动态绑定点击!');
};
// 3. 监听点击事件(推荐)
let btn3 = document.getElementById('btn3');
btn3.addEventListener('click', function() {
alert('监听点击!');
});
// 可绑定多个
btn3.addEventListener('click', function() {
console.log('按钮3被点击了');
});
</script>
</body>
</html>



实操步骤
- 新建
65-JS点击事件.html,粘贴代码; - 点击不同按钮,观察效果;
- 给btn3添加第三个点击事件,修改页面文字。
易错点提醒
- ❌ 监听事件重复绑定:多次addEventListener会执行多次,要注意;
- ❌ 动态绑定覆盖:
btn.onclick = fn1; btn.onclick = fn2只会执行fn2; - ❌ 函数加括号:
onclick = fn()会立即执行,不是点击执行,要写onclick = fn。
课后小练习
给个人主页的“回到顶部”按钮加点击事件,点击时页面滚动到顶部(window.scrollTo(0,0))。
课时66:JS事件——鼠标/键盘事件
学习目标
掌握鼠标事件(鼠标移入/移出)和键盘事件(按键按下),丰富交互效果。
通俗讲解
- 鼠标事件:
- mouseover/mouseout:鼠标移入/移出(会冒泡,新手先用);
- mouseenter/mouseleave:鼠标移入/移出(不冒泡,推荐);
- 键盘事件:
- keydown:按键按下(任意键);
- keyup:按键松开;
- 通常绑定在input/文档上。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS鼠标/键盘事件</title>
<style>
.box {
width: 200px;
height: 100px;
border: 1px solid #ccc;
margin: 10px 0;
}
</style>
</head>
<body>
<!-- 鼠标事件 -->
<div id="mouseBox" class="box">鼠标移入/移出</div>
<!-- 键盘事件 -->
<input type="text" id="keyInput" placeholder="输入文字...">
<script>
// 1. 鼠标事件
let mouseBox = document.getElementById('mouseBox');
// 移入
mouseBox.addEventListener('mouseenter', function() {
this.style.backgroundColor = '#007bff';
this.style.color = 'white';
});
// 移出
mouseBox.addEventListener('mouseleave', function() {
this.style.backgroundColor = '';
this.style.color = '';
});
// 2. 键盘事件
let keyInput = document.getElementById('keyInput');
keyInput.addEventListener('keydown', function(e) {
// e.key是按下的键,e.keyCode是键码(已废弃,用e.key)
console.log('按下的键:', e.key);
if (e.key === 'Enter') {
alert('你输入了:' + this.value);
this.value = ''; // 清空输入框
}
});
</script>
</body>
</html>



实操步骤
- 新建
66-JS鼠标键盘事件.html,粘贴代码; - 鼠标移入/移出div,观察样式变化;
- 在输入框输入文字,按回车,观察弹窗和清空效果。
易错点提醒
- ❌ 键盘事件绑定在div上:div默认不聚焦,键盘事件不生效,要绑定在input/textarea/文档上;
- ❌ this指向错:事件函数内的this指向触发事件的元素,箭头函数中this不指向元素;
- ❌ 混淆mouseover和mouseenter:mouseover会冒泡(子元素触发),mouseenter不会,新手优先用mouseenter。
课后小练习
给个人主页的导航项加mouseenter/mouseleave事件,移入时变色,移出时恢复。
课时67:JS事件——表单事件
学习目标
掌握表单事件(聚焦/失焦/内容改变/提交),处理表单交互。
通俗讲解
表单事件是处理用户输入的核心:
- focus:输入框聚焦;
- blur:输入框失焦;
- input:输入框内容实时变化;
- submit:表单提交(绑定在form上,可阻止默认提交)。
极简代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>JS表单事件</title>
</head>
<body>
<form id="myForm">
<input type="text" id="username" placeholder="请输入用户名">
<input type="password" id="password" placeholder="请输入密码">
<button type="submit">提交</button>
</form>
<script>
let username = document.getElementById('username');
let password = document.getElementById('password');
let myForm = document.getElementById('myForm');
// 1. 聚焦事件
username.addEventListener('focus', function() {
this.style.border = '1px solid #007bff';
});
// 2. 失焦事件
username.addEventListener('blur', function() {
this.style.border = '';
// 失焦验证
if (this.value.length < 3) {
alert('用户名至少3位!');
}
});
// 3. 实时输入事件
password.addEventListener('input', function() {
console.log('密码实时输入:', this.value);
});
// 4. 表单提交事件
myForm.addEventListener('submit', function(e) {
e.preventDefault(); // 阻止默认提交(关键)
let user = username.value;
let pwd = password.value;
if (user && pwd) {
alert('提交成功!用户名:' + user);
this.reset(); // 重置表单
} else {
alert('用户名和密码不能为空!');
}
});
</script>
</body>
</html>

实操步骤
- 新建
67-JS表单事件.html,粘贴代码; - 聚焦/失焦用户名输入框,观察边框和验证;
- 输入密码,看控制台实时输出;
- 提交表单,观察验证和重置效果。
易错点提醒
- ❌ 忘记阻止默认提交:表单会刷新页面,必须加e.preventDefault();
- ❌ 提交事件绑定在按钮上:按钮点击不等同于表单提交,要绑定在form上;
- ❌ 空值判断错:
if(user)会把空字符串判断为false,正确;if(user==='')更明确。
课后小练习
给个人主页的留言表单加提交事件,验证姓名和留言内容不能为空,为空则弹窗提示,不为空则弹窗“留言成功”并重置表单。
课时68:JS综合练习1——个人主页交互优化(点击事件)
学习目标
给个人主页添加核心点击交互:回到顶部、导航选中切换、区域显隐控制,掌握「事件绑定+类名切换+DOM样式修改」的组合用法。
前置准备
确保你的个人主页已有基础HTML/CSS结构(包含header导航、main内容区、回到顶部按钮、“我的经历”区域),核心结构参考:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的个人主页</title>
<style>
/* 基础样式 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 2000px; /* 让页面可滚动 */ }
header { background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 20px; }
.nav { list-style: none; display: flex; gap: 20px; }
.nav li a { text-decoration: none; color: #333; padding: 8px 12px; }
.nav li a.active { background: #007bff; color: #fff; border-radius: 4px; }
.experience { margin: 20px; padding: 20px; border: 1px solid #ccc; }
.back-top { position: fixed; bottom: 50px; right: 50px; width: 50px; height: 50px; background: #007bff; color: #fff; text-align: center; line-height: 50px; border-radius: 50%; text-decoration: none; display: none; }
.toggle-btn { margin: 20px; padding: 8px 16px; cursor: pointer; }
</style>
</head>
<body>
<header>
<ul class="nav">
<li><a href="#home" class="active">首页</a></li>
<li><a href="#hobby">我的爱好</a></li>
<li><a href="#experience">我的经历</a></li>
<li><a href="#message">留言板</a></li>
</ul>
</header>
<button class="toggle-btn">隐藏/显示我的经历</button>
<div class="experience" id="experience">
<h2>我的经历</h2>
<p>2025年:学习前端HTML/CSS/JS</p>
<p>目标:掌握前端基础,完成个人博客项目</p>
</div>
<a href="#" class="back-top" id="backTop">↑</a>
<script>
// 后续写交互代码
</script>
</body>
</html>

实操任务拆解
任务1:回到顶部按钮(显示/隐藏+点击滚动)
核心逻辑:页面滚动时,判断滚动距离>300px显示按钮,否则隐藏;点击按钮滚动到顶部。
// 1. 回到顶部功能
const backTop = document.getElementById('backTop');
// 监听页面滚动
window.addEventListener('scroll', function() {
// 获取滚动距离(兼容写法)
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 滚动距离>300px显示按钮,否则隐藏
backTop.style.display = scrollTop > 300 ? 'block' : 'none';
});
// 点击按钮回到顶部
backTop.addEventListener('click', function(e) {
e.preventDefault(); // 阻止a标签默认跳转
// 平滑滚动到顶部
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});

任务2:导航选中状态切换
核心逻辑:点击导航项时,给当前项加active类,移除其他项的active类。
// 2. 导航选中切换
const navLinks = document.querySelectorAll('.nav a');
navLinks.forEach(link => {
link.addEventListener('click', function() {
// 先移除所有导航项的active类
navLinks.forEach(item => item.classList.remove('active'));
// 给当前点击的项加active类
this.classList.add('active');
});
});

任务3:隐藏/显示“我的经历”区域
核心逻辑:点击按钮切换目标区域的display样式(block/ none),同时修改按钮文字。
// 3. 隐藏/显示我的经历
const toggleBtn = document.querySelector('.toggle-btn');
const experience = document.getElementById('experience');
let isShow = true; // 标记是否显示
toggleBtn.addEventListener('click', function() {
if (isShow) {
experience.style.display = 'none';
this.innerText = '显示我的经历';
isShow = false;
} else {
experience.style.display = 'block';
this.innerText = '隐藏我的经历';
isShow = true;
}
});


完整交互代码(替换script标签内内容)
// 1. 回到顶部功能
const backTop = document.getElementById('backTop');
window.addEventListener('scroll', function() {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
backTop.style.display = scrollTop > 300 ? 'block' : 'none';
});
backTop.addEventListener('click', function(e) {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// 2. 导航选中切换
const navLinks = document.querySelectorAll('.nav a');
navLinks.forEach(link => {
link.addEventListener('click', function() {
navLinks.forEach(item => item.classList.remove('active'));
this.classList.add('active');
});
});
// 3. 隐藏/显示我的经历
const toggleBtn = document.querySelector('.toggle-btn');
const experience = document.getElementById('experience');
let isShow = true;
toggleBtn.addEventListener('click', function() {
if (isShow) {
experience.style.display = 'none';
this.innerText = '显示我的经历';
} else {
experience.style.display = 'block';
this.innerText = '隐藏我的经历';
}
isShow = !isShow; // 简化:直接取反
});
易错点提醒
- ❌ 回到顶部按钮点击没效果:忘记加
e.preventDefault()阻止a标签默认跳转,或滚动API写错(behavior: 'smooth'是平滑滚动,低版本浏览器可能不支持,可替换为document.documentElement.scrollTop = 0); - ❌ 导航切换只加不减:只给当前项加
active,没移除其他项的,导致多个项高亮; - ❌ 显隐状态错乱:未用
isShow标记状态,直接判断display可能因样式优先级出错(建议用标记变量)。
验证效果
- 滚动页面,观察回到顶部按钮是否在滚动>300px时显示;
- 点击不同导航项,是否只有当前项高亮;
- 点击“隐藏/显示”按钮,是否切换区域显隐且按钮文字变化。
课时69:JS综合练习2——个人主页交互优化(表单验证)
学习目标
给个人主页的留言表单添加「实时验证+失焦验证+提交验证」,掌握表单事件(focus/blur/input/submit)和数据校验逻辑。
前置准备
先给个人主页添加留言表单结构(放在backTop按钮前):
<!-- 留言板 -->
<div class="message" id="message" style="margin: 20px;">
<h2>留言板</h2>
<form id="messageForm">
<div style="margin: 10px 0;">
<label>姓名:</label>
<input type="text" id="username" placeholder="请输入姓名" style="padding: 8px; width: 300px;">
<span class="tips" id="usernameTips" style="color: red; font-size: 12px; margin-left: 10px;"></span>
</div>
<div style="margin: 10px 0;">
<label>留言:</label>
<textarea id="content" rows="5" cols="50" placeholder="请输入留言内容" style="padding: 8px;"></textarea>
<div style="font-size: 12px; margin-top: 5px;">
已输入:<span id="wordCount">0</span> 字
</div>
</div>
<button type="submit" style="padding: 8px 16px; background: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer;">提交留言</button>
</form>
</div>

实操任务拆解
任务1:姓名输入框失焦验证
核心逻辑:失焦时检查姓名是否为空、长度是否≥2,通过提示文字反馈结果。
// 1. 姓名失焦验证
const username = document.getElementById('username');
const usernameTips = document.getElementById('usernameTips');
username.addEventListener('blur', function() {
const value = this.value.trim(); // 去除首尾空格
if (value === '') {
usernameTips.innerText = '姓名不能为空!';
} else if (value.length < 2) {
usernameTips.innerText = '姓名至少2个字!';
} else {
usernameTips.innerText = ''; // 验证通过,清空提示
}
});
// 聚焦时清空提示(优化体验)
username.addEventListener('focus', function() {
usernameTips.innerText = '';
});

任务2:留言内容实时统计字数
核心逻辑:监听input事件,实时计算输入框字符长度并显示。
// 2. 留言内容实时统计字数
const content = document.getElementById('content');
const wordCount = document.getElementById('wordCount');
content.addEventListener('input', function() {
const len = this.value.length;
wordCount.innerText = len;
// 可选:限制最大字数(比如200字)
if (len > 200) {
this.value = this.value.substring(0, 200); // 截断内容
wordCount.innerText = 200;
alert('留言内容最多200字!');
}
});

任务3:表单提交完整验证
核心逻辑:提交时阻止默认行为,重新校验所有字段,全部通过则提示成功并重置表单,否则提示错误。
// 3. 表单提交验证
const messageForm = document.getElementById('messageForm');
messageForm.addEventListener('submit', function(e) {
e.preventDefault(); // 阻止表单默认提交(关键)
// 1. 重新校验姓名
const userNameVal = username.value.trim();
let isPass = true; // 标记是否通过验证
if (userNameVal === '' || userNameVal.length < 2) {
usernameTips.innerText = '请输入有效的姓名(至少2字)!';
isPass = false;
username.focus(); // 聚焦到错误输入框(优化体验)
}
// 2. 校验留言内容
const contentVal = content.value.trim();
if (contentVal === '') {
alert('留言内容不能为空!');
isPass = false;
content.focus();
return; // 提前终止,避免后续执行
}
// 3. 所有验证通过
if (isPass) {
alert(`感谢${userNameVal}的留言!留言内容:${contentVal}`);
this.reset(); // 重置表单
wordCount.innerText = 0; // 重置字数统计
}
});

完整表单验证代码(添加到script标签内)
// 1. 姓名失焦验证
const username = document.getElementById('username');
const usernameTips = document.getElementById('usernameTips');
username.addEventListener('blur', function() {
const value = this.value.trim();
if (value === '') {
usernameTips.innerText = '姓名不能为空!';
} else if (value.length < 2) {
usernameTips.innerText = '姓名至少2个字!';
} else {
usernameTips.innerText = '';
}
});
username.addEventListener('focus', function() {
usernameTips.innerText = '';
});
// 2. 留言内容实时统计字数
const content = document.getElementById('content');
const wordCount = document.getElementById('wordCount');
content.addEventListener('input', function() {
let len = this.value.length;
if (len > 200) {
this.value = this.value.substring(0, 200);
len = 200;
alert('留言内容最多200字!');
}
wordCount.innerText = len;
});
// 3. 表单提交验证
const messageForm = document.getElementById('messageForm');
messageForm.addEventListener('submit', function(e) {
e.preventDefault();
const userNameVal = username.value.trim();
const contentVal = content.value.trim();
let isPass = true;
// 校验姓名
if (userNameVal === '' || userNameVal.length < 2) {
usernameTips.innerText = '请输入有效的姓名(至少2字)!';
isPass = false;
username.focus();
}
// 校验留言
if (contentVal === '') {
if (isPass) { // 只有姓名验证通过时才聚焦留言框
content.focus();
}
alert('留言内容不能为空!');
isPass = false;
}
// 提交成功
if (isPass) {
alert(`感谢${userNameVal}的留言!\n留言内容:${contentVal}`);
this.reset();
wordCount.innerText = 0;
}
});
易错点提醒
- ❌ 表单提交后页面刷新:忘记加
e.preventDefault(),这是表单交互的核心; - ❌ 字数统计包含空格:
trim()只去除首尾空格,实时统计不需要trim(用户输入的空格也算字数); - ❌ 验证提示重复:提交时未重新校验,仅依赖失焦验证(用户可能跳过失焦直接提交);
- ❌ 截断内容后字数错误:截断后未更新
len,导致字数显示还是超过200。
验证效果
- 姓名输入框失焦时,空值/短于2字会显示红色提示,聚焦时清空;
- 留言框输入内容,字数实时更新,超过200字会截断并提示;
- 提交表单时,空姓名/空留言会提示错误,验证通过则弹窗并重置表单。
课时70:JS综合练习3——个人主页交互优化(动态数据)
学习目标
用「数组+循环+动态创建DOM」渲染“我的爱好”列表,掌握数据驱动页面的核心思想,同时添加鼠标交互效果。
前置准备
先给个人主页添加“我的爱好”基础结构(放在header后、toggle-btn前):
<!-- 我的爱好 -->
<div class="hobby" id="hobby" style="margin: 20px;">
<h2>我的爱好</h2>
<ul id="hobbyList" style="list-style: none; margin-top: 10px; display: flex; gap: 15px; flex-wrap: wrap;"></ul>
</div>

实操任务拆解
任务1:声明爱好数组,动态创建li标签渲染列表
核心逻辑:定义数组存储爱好数据 → 遍历数组 → 逐个创建li元素 → 设置内容和样式 → 添加到ul中。
// 1. 动态渲染我的爱好列表
const hobbies = ['学习HTML', '学习CSS', '学习JS', '写前端项目', '看技术文档', '和同行交流'];
const hobbyList = document.getElementById('hobbyList');
// 清空原有内容(避免重复渲染)
hobbyList.innerHTML = '';
// 遍历数组创建li
hobbies.forEach((item, index) => {
// 创建li元素
const li = document.createElement('li');
// 设置li的内容和样式
li.innerText = `${index + 1}. ${item}`;
li.style.padding = '10px 15px';
li.style.background = '#f5f5f5';
li.style.borderRadius = '4px';
li.style.cursor = 'pointer';
// 添加到ul中
hobbyList.appendChild(li);
});

任务2:给每个爱好li添加鼠标移入/移出效果
核心逻辑:遍历所有动态创建的li → 绑定mouseenter/mouseleave事件 → 切换背景色和文字色。
// 2. 给爱好li添加鼠标交互
function addHobbyHover() {
const hobbyItems = document.querySelectorAll('#hobbyList li');
hobbyItems.forEach(li => {
// 鼠标移入
li.addEventListener('mouseenter', function() {
this.style.background = '#007bff';
this.style.color = '#fff';
this.style.transition = 'all 0.3s'; // 过渡动画(优化体验)
});
// 鼠标移出
li.addEventListener('mouseleave', function() {
this.style.background = '#f5f5f5';
this.style.color = '#333';
});
});
}
// 渲染列表后调用交互函数
addHobbyHover();

任务3:可选拓展——添加“新增爱好”功能
核心逻辑:添加输入框和按钮 → 点击按钮获取输入值 → 验证非空 → 添加到数组 → 重新渲染列表。
<!-- 新增爱好的输入框和按钮(添加到hobby div内,h2下方) -->
<div style="margin-bottom: 10px;">
<input type="text" id="newHobby" placeholder="请输入新爱好" style="padding: 8px; width: 300px;">
<button id="addHobbyBtn" style="padding: 8px 16px; margin-left: 10px; background: #28a745; color: #fff; border: none; border-radius: 4px; cursor: pointer;">新增爱好</button>
</div>
// 3. 新增爱好功能
const newHobby = document.getElementById('newHobby');
const addHobbyBtn = document.getElementById('addHobbyBtn');
addHobbyBtn.addEventListener('click', function() {
const value = newHobby.value.trim();
if (value === '') {
alert('请输入爱好内容!');
newHobby.focus();
return;
}
// 添加到数组
hobbies.push(value);
// 重新渲染列表
hobbyList.innerHTML = '';
hobbies.forEach((item, index) => {
const li = document.createElement('li');
li.innerText = `${index + 1}. ${item}`;
li.style.padding = '10px 15px';
li.style.background = '#f5f5f5';
li.style.borderRadius = '4px';
li.style.cursor = 'pointer';
hobbyList.appendChild(li);
});
// 重新绑定交互事件
addHobbyHover();
// 清空输入框
newHobby.value = '';
});


完整动态数据代码(添加到script标签内)
// 1. 动态渲染我的爱好列表
const hobbies = ['学习HTML', '学习CSS', '学习JS', '写前端项目', '看技术文档', '和同行交流'];
const hobbyList = document.getElementById('hobbyList');
// 渲染爱好列表的函数(封装复用)
function renderHobbyList() {
hobbyList.innerHTML = '';
hobbies.forEach((item, index) => {
const li = document.createElement('li');
li.innerText = `${index + 1}. ${item}`;
li.style.padding = '10px 15px';
li.style.background = '#f5f5f5';
li.style.borderRadius = '4px';
li.style.cursor = 'pointer';
hobbyList.appendChild(li);
});
// 渲染后绑定交互
addHobbyHover();
}
// 2. 爱好li鼠标交互函数
function addHobbyHover() {
const hobbyItems = document.querySelectorAll('#hobbyList li');
hobbyItems.forEach(li => {
li.addEventListener('mouseenter', function() {
this.style.background = '#007bff';
this.style.color = '#fff';
this.style.transition = 'all 0.3s';
});
li.addEventListener('mouseleave', function() {
this.style.background = '#f5f5f5';
this.style.color = '#333';
});
});
}
// 3. 新增爱好功能
const newHobby = document.getElementById('newHobby');
const addHobbyBtn = document.getElementById('addHobbyBtn');
addHobbyBtn.addEventListener('click', function() {
const value = newHobby.value.trim();
if (!value) {
alert('请输入爱好内容!');
newHobby.focus();
return;
}
hobbies.push(value);
renderHobbyList(); // 复用渲染函数
newHobby.value = '';
});
// 初始化渲染列表
renderHobbyList();
易错点提醒
- ❌ 动态元素绑定事件无效:直接在渲染前绑定事件(元素还没创建),必须在渲染后调用绑定函数;
- ❌ 重复渲染列表:新增爱好时未清空
hobbyList.innerHTML,导致列表重复; - ❌ 索引错误:
index + 1写成index,导致爱好编号从0开始; - ❌ 过渡动画不生效:只在mouseenter时加
transition,应放在初始样式中(或mouseenter/mouseleave都加)。
验证效果
- 页面加载后,自动渲染爱好列表,每个项有默认样式;
- 鼠标移入爱好项,背景色变蓝、文字变白(有过渡动画),移出恢复;
- 在新增输入框输入内容,点击按钮可添加新爱好到列表末尾,且新项也有鼠标交互效果。
课时71:JS综合练习4——个人主页交互优化(简易轮播图)
学习目标
用JS实现「图片切换+边界控制+按钮禁用」的简易轮播图,掌握“索引控制+DOM属性修改”的核心逻辑。
前置准备
先给个人主页添加轮播图基础结构(放在hobby区域后):
<!-- 简易轮播图 -->
<div class="carousel" id="carousel" style="margin: 20px; width: 500px; height: 300px; border: 1px solid #ccc; position: relative; overflow: hidden;">
<!-- 轮播图片 -->
<img id="carouselImg" src="https://picsum.photos/500/300?random=1" alt="轮播图" style="width: 100%; height: 100%; object-fit: cover;">
<!-- 切换按钮 -->
<button id="prevBtn" style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); padding: 8px 12px; background: rgba(0,0,0,0.5); color: #fff; border: none; border-radius: 4px; cursor: pointer;">上一张</button>
<button id="nextBtn" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); padding: 8px 12px; background: rgba(0,0,0,0.5); color: #fff; border: none; border-radius: 4px; cursor: pointer;">下一张</button>
</div>

核心逻辑说明
轮播图的核心是「图片数组+当前索引」:
- 定义图片地址数组,存储所有轮播图片;
- 用变量记录当前显示的图片索引;
- 点击“上一张/下一张”时,修改索引并切换图片;
- 索引到边界时(0或数组长度-1),禁用对应按钮。
实操任务拆解
任务1:定义轮播数据,初始化索引和按钮状态
// 1. 轮播图核心数据
const carouselImgs = [
'https://picsum.photos/500/300?random=1',
'https://picsum.photos/500/300?random=2',
'https://picsum.photos/500/300?random=3'
];
const carouselImg = document.getElementById('carouselImg');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
let currentIndex = 0; // 当前图片索引
// 初始化按钮状态(默认第一张,禁用上一张)
function updateBtnStatus() {
// 索引为0时,禁用上一张按钮
prevBtn.disabled = currentIndex === 0;
// 索引为最后一张时,禁用下一张按钮
nextBtn.disabled = currentIndex === carouselImgs.length - 1;
// 禁用时修改样式(优化体验)
prevBtn.style.opacity = prevBtn.disabled ? 0.5 : 1;
nextBtn.style.opacity = nextBtn.disabled ? 0.5 : 1;
}
// 初始化调用
updateBtnStatus();
任务2:实现图片切换逻辑
// 2. 切换图片函数(复用)
function changeImg(index) {
carouselImg.src = carouselImgs[index];
currentIndex = index;
updateBtnStatus(); // 切换后更新按钮状态
}
// 3. 上一张按钮点击事件
prevBtn.addEventListener('click', function() {
if (currentIndex > 0) {
changeImg(currentIndex - 1);
}
});
// 4. 下一张按钮点击事件
nextBtn.addEventListener('click', function() {
if (currentIndex < carouselImgs.length - 1) {
changeImg(currentIndex + 1);
}
});
任务3:可选拓展——自动轮播
// 5. 自动轮播(可选)
let autoPlayTimer = setInterval(function() {
// 计算下一个索引:到最后一张则回到0
const nextIndex = currentIndex === carouselImgs.length - 1 ? 0 : currentIndex + 1;
changeImg(nextIndex);
}, 3000); // 3秒切换一次
// 鼠标移入轮播图暂停自动播放,移出恢复
const carousel = document.getElementById('carousel');
carousel.addEventListener('mouseenter', function() {
clearInterval(autoPlayTimer);
});
carousel.addEventListener('mouseleave', function() {
autoPlayTimer = setInterval(function() {
const nextIndex = currentIndex === carouselImgs.length - 1 ? 0 : currentIndex + 1;
changeImg(nextIndex);
}, 3000);
});



完整轮播图代码(添加到script标签内)
// 简易轮播图核心代码
const carouselImgs = [
'https://picsum.photos/500/300?random=1',
'https://picsum.photos/500/300?random=2',
'https://picsum.photos/500/300?random=3'
];
const carouselImg = document.getElementById('carouselImg');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const carousel = document.getElementById('carousel');
let currentIndex = 0;
let autoPlayTimer;
// 更新按钮状态
function updateBtnStatus() {
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex === carouselImgs.length - 1;
prevBtn.style.opacity = prevBtn.disabled ? 0.5 : 1;
nextBtn.style.opacity = nextBtn.disabled ? 0.5 : 1;
}
// 切换图片
function changeImg(index) {
carouselImg.src = carouselImgs[index];
currentIndex = index;
updateBtnStatus();
}
// 绑定按钮事件
prevBtn.addEventListener('click', function() {
if (currentIndex > 0) {
changeImg(currentIndex - 1);
}
});
nextBtn.addEventListener('click', function() {
if (currentIndex < carouselImgs.length - 1) {
changeImg(currentIndex + 1);
}
});
// 自动轮播
function startAutoPlay() {
autoPlayTimer = setInterval(function() {
const nextIndex = currentIndex === carouselImgs.length - 1 ? 0 : currentIndex + 1;
changeImg(nextIndex);
}, 3000);
}
// 暂停自动轮播
function stopAutoPlay() {
clearInterval(autoPlayTimer);
}
// 初始化
updateBtnStatus();
startAutoPlay();
// 鼠标移入/移出控制自动轮播
carousel.addEventListener('mouseenter', stopAutoPlay);
carousel.addEventListener('mouseleave', startAutoPlay);
易错点提醒
- ❌ 索引越界:点击下一张时未判断
currentIndex < 数组长度-1,导致访问不存在的图片地址; - ❌ 按钮禁用后仍可点击:仅修改样式不设置
disabled,按钮仍能触发事件(必须设置disabled = true); - ❌ 自动轮播内存泄漏:鼠标移入暂停后未清除定时器,多次移入移出会创建多个定时器;
- ❌ 图片变形:未设置
object-fit: cover,导致图片拉伸(轮播图容器固定宽高时必加)。
验证效果
- 页面加载后显示第一张图片,上一张按钮禁用(半透明),下一张按钮可用;
- 点击下一张,切换到第二张/第三张,到第三张时下一张按钮禁用;
- 点击上一张,可回到前一张,到第一张时上一张按钮禁用;
- (可选)鼠标不移入时,每3秒自动切换图片,移入轮播图暂停,移出恢复。
课时72:JS模块总结与查漏补缺
学习目标
系统回顾JS核心知识点,梳理常见bug的排查方法,解决个人主页交互中的高频问题。
一、JS核心知识点回顾(按优先级)
| 模块 | 核心内容 | 应用场景 |
|---|---|---|
| 基础语法 | 变量(let/const)、数据类型、运算符、条件语句(if/switch)、循环(for/while) | 所有逻辑判断、数据处理 |
| 数组 | 声明、访问、push/pop/splice、遍历(for/forEach) | 动态列表、数据存储 |
| 函数 | 声明、调用、参数(默认值/不定参数)、返回值 | 代码复用、逻辑封装 |
| DOM操作 | 获取元素(querySelector/ById)、修改内容/样式/类名 | 页面动态更新 |
| 事件处理 | 点击/鼠标/表单事件、阻止默认行为、事件绑定(addEventListener) | 页面交互(按钮、表单、滚动) |
| 数据验证 | 空值检查、长度校验、类型判断 | 表单提交、用户输入处理 |
二、JS高频bug排查清单
1. DOM相关问题
| 问题现象 | 常见原因 | 解决方法 |
|---|---|---|
| 获取元素返回null | 1. 脚本执行时DOM未加载(脚本放head);2. ID/类名拼写错误;3. 元素不存在 | 1. 脚本放body末尾;2. 检查选择器拼写;3. console.log打印元素验证 |
| 动态元素事件绑定无效 | 元素创建前绑定事件 | 1. 渲染后绑定事件;2. 事件委托(进阶) |
| 修改样式不生效 | 1. 样式名驼峰写错(比如background-color→backgroundColor);2. 样式优先级低 | 1. 检查样式名;2. 用classList替代直接修改style;3. 加!important(临时) |
2. 逻辑相关问题
| 问题现象 | 常见原因 | 解决方法 |
|---|---|---|
| 循环死循环 | 循环条件永远为true(比如未写i++) | 检查循环条件和变量更新,加break/continue控制 |
| 条件判断失效 | 1. 用=代替===;2. 字符串和数字比较('18'===18);3. 逻辑运算符错误 | 1. 优先用===;2. 统一数据类型;3. 复杂条件加括号 |
| 函数返回值undefined | 忘记写return,或return在条件里未执行 | 检查return位置,确保所有分支都有返回值 |
3. 事件相关问题
| 问题现象 | 常见原因 | 解决方法 |
|---|---|---|
| 表单提交后页面刷新 | 未阻止默认行为(e.preventDefault()) | 在submit事件中第一行加e.preventDefault() |
| 按钮点击无反应 | 1. 事件未绑定;2. 按钮被禁用(disabled=true);3. 代码报错中断执行 | 1. 检查事件绑定代码;2. 取消disabled;3. F12控制台看报错信息 |
| 滚动事件触发太频繁 | 未做节流(进阶) | 简易解决:加标记变量,100ms内只执行一次 |
三、个人主页交互优化建议
- 代码复用:将重复逻辑封装成函数(比如渲染列表、验证输入),避免冗余;
- 体验优化:
- 所有按钮/输入框添加hover/聚焦样式;
- 验证提示用友好的文字(避免“错误!”等生硬表述);
- 轮播图/回到顶部添加过渡动画,避免生硬切换;
- 容错处理:
- 输入框验证时去除首尾空格(trim());
- 动态渲染列表前先清空,避免重复;
- 图片加载失败时显示默认图(carouselImg.onerror = () => { this.src = '默认图地址' });
- 代码可读性:
- 变量/函数名用语义化命名(比如
renderHobbyList而非fn1); - 加注释说明核心逻辑(比如轮播图的索引控制、表单的验证规则)。
四、查漏补缺练习
- 给个人主页的轮播图添加“图片加载失败”容错:
// 轮播图图片加载失败处理
carouselImg.onerror = function() {
this.src = 'https://picsum.photos/500/300?random=0'; // 默认图
alert('图片加载失败,显示默认图!');
};
- 给表单验证添加“姓名只能包含中文/字母”的规则:
// 姓名格式验证(新增)
username.addEventListener('blur', function() {
const value = this.value.trim();
const reg = /^[a-zA-Z\u4e00-\u9fa5]{2,10}$/; // 2-10位中文/字母
if (value === '') {
usernameTips.innerText = '姓名不能为空!';
} else if (!reg.test(value)) {
usernameTips.innerText = '姓名只能是2-10位中文/字母!';
} else {
usernameTips.innerText = '';
}
});
课时73:实战1——需求分析与页面规划(简易个人博客)
学习目标
明确“简易个人博客”的核心需求,拆分页面结构和功能模块,制定开发计划。
一、项目需求(极简版,适合新手)
1. 核心功能
- 页面布局:头部(Logo+导航)、主体(文章列表+侧边栏)、底部(版权);
- 文章列表:展示文章标题、简介、发布时间、点赞数;
- 侧边栏:搜索框(筛选文章)、热门标签;
- 交互功能:文章点赞、留言提交、搜索筛选。
2. 非功能要求
- 响应式:适配电脑/平板/手机;
- 样式美观:统一配色、合理间距、hover效果;
- 交互友好:表单验证、操作反馈(弹窗/提示)。
二、页面结构拆分
| 区域 | 子元素 | 核心功能 |
|---|---|---|
| 头部(header) | Logo(h1)、导航栏(nav+ul+li) | 页面导航 |
| 主体(main) | 文章列表(article-list) | 展示文章、点赞交互 |
| 侧边栏(aside) | 搜索文章、显示热门标签 | |
| 底部(footer) | 版权信息(p) | 页面底部说明 |
三、数据规划(模拟数据)
1. 文章数据(数组)
const articles = [
{
id: 1,
title: 'HTML语义化的重要性',
desc: '语义化标签让页面结构更清晰,利于SEO和维护...',
time: '2025-01-01',
like: 12,
tag: ['HTML', '基础']
},
{
id: 2,
title: 'Flex布局快速上手',
desc: 'Flex布局是前端必备的布局方式,轻松实现水平垂直居中...',
time: '2025-01-05',
like: 28,
tag: ['CSS', '布局']
},
{
id: 3,
title: 'JS DOM操作核心技巧',
desc: '获取元素、修改样式、绑定事件,DOM操作的核心知识点...',
time: '2025-01-10',
like: 45,
tag: ['JS', 'DOM']
}
];
2. 热门标签(数组)
const hotTags = ['HTML', 'CSS', 'JS', 'Flex', 'DOM', '表单验证'];
四、开发计划(分阶段)
- 阶段1:搭建HTML结构(1课时)→ 课时74;
- 阶段2:编写CSS样式(响应式)→ 课时75;
- 阶段3:编写JS交互(搜索、点赞、留言)→ 课时76;
- 阶段4:测试、修复bug、优化体验 → 课时77。
课时74:实战2——搭建HTML结构(简易个人博客)
学习目标
用语义化标签搭建个人博客的完整HTML结构,为后续样式和交互打下基础。
完整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>
<!-- 后续引入CSS -->
<style>
/* 临时基础样式(课时75替换为完整CSS) */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: "Microsoft Yahei", sans-serif; color: #333; background: #f8f9fa; }
</style>
</head>
<body>
<!-- 头部 -->
<header class="header" id="header">
<div class="container" style="max-width: 1200px; margin: 0 auto; padding: 20px; display: flex; justify-content: space-between; align-items: center;">
<h1 class="logo" style="font-size: 24px; color: #007bff;">我的前端博客</h1>
<nav class="nav">
<ul style="list-style: none; display: flex; gap: 20px;">
<li><a href="#home" style="text-decoration: none; color: #333;">首页</a></li>
<li><a href="#articles" style="text-decoration: none; color: #333;">文章列表</a></li>
<li><a href="#message" style="text-decoration: none; color: #333;">留言板</a></li>
</ul>
</nav>
</div>
</header>
<!-- 主体 -->
<main class="main" id="main" style="max-width: 1200px; margin: 0 auto; padding: 20px; display: flex; gap: 20px;">
<!-- 文章列表 -->
<section class="article-list" id="articleList" style="flex: 7;">
<h2 style="margin-bottom: 20px; color: #007bff;">文章列表</h2>
<!-- 文章项(JS动态渲染) -->
<div class="article-items" id="articleItems"></div>
</section>
<!-- 侧边栏 -->
<aside class="sidebar" id="sidebar" style="flex: 3;">
<!-- 搜索框 -->
<div class="search-box" style="margin-bottom: 20px; padding: 15px; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h3 style="margin-bottom: 10px; font-size: 16px;">搜索文章</h3>
<div style="display: flex; gap: 10px;">
<input type="text" id="searchInput" placeholder="输入标题关键词" style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
<button id="searchBtn" style="padding: 8px 16px; background: #007bff; color: #fff; border: none; border-radius: 4px; cursor: pointer;">搜索</button>
</div>
</div>
<!-- 热门标签 -->
<div class="hot-tags" style="padding: 15px; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<h3 style="margin-bottom: 10px; font-size: 16px;">热门标签</h3>
<div id="hotTagsList" style="display: flex; flex-wrap: wrap; gap: 8px;"></div>
</div>
</aside>
</main>
<!-- 留言板 -->
<section class="message" id="message" style="max-width: 1200px; margin: 0 auto; padding: 20px;">
<h2 style="margin-bottom: 20px; color: #007bff;">留言板</h2>
<form id="messageForm" style="background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px;">姓名:</label>
<input type="text" id="msgName" placeholder="请输入姓名" style="width: 100%; max-width: 400px; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
<span class="tips" id="msgNameTips" style="color: red; font-size: 12px; display: block; margin-top: 5px;"></span>
</div>
<div style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px;">留言内容:</label>
<textarea id="msgContent" rows="5" placeholder="请输入留言内容" style="width: 100%; max-width: 600px; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"></textarea>
</div>
<button type="submit" style="padding: 8px 16px; background: #28a745; color: #fff; border: none; border-radius: 4px; cursor: pointer;">提交留言</button>
</form>
<!-- 留言列表(JS动态渲染) -->
<div id="msgList" style="margin-top: 20px;"></div>
</section>
<!-- 底部 -->
<footer class="footer" id="footer" style="background: #333; color: #fff; text-align: center; padding: 20px; margin-top: 40px;">
<p>© 2025 我的前端博客 | 零基础前端学习笔记</p>
</footer>
<!-- JS脚本 -->
<script>
// 后续添加JS交互代码(课时76)
</script>
</body>
</html>
结构说明
- 语义化标签:用header/main/aside/section/footer替代div,提升可读性和SEO;
- 容器类:所有内容放在
max-width: 1200px的容器中,避免页面过宽; - 动态区域:文章列表(articleItems)、热门标签(hotTagsList)、留言列表(msgList)留空,后续用JS动态渲染;
- 响应式基础:添加
meta viewport标签,主体用flex布局(后续CSS优化响应式)。
验证效果
- 打开页面,能看到完整的页面框架:头部Logo+导航、主体文章列表+侧边栏、留言板、底部版权;
- 所有输入框、按钮、标题显示正常,无布局错乱;
- 检查标签嵌套:无未闭合标签,语义化标签使用正确。
课时75:实战3——编写CSS样式(简易个人博客)
学习目标
编写完整的CSS样式,实现响应式布局,美化博客的视觉效果,统一交互样式。
完整CSS代码(替换head中的style标签内容)
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 基础样式 */
body {
font-family: "Microsoft Yahei", "Helvetica Neue", sans-serif;
color: #333;
background-color: #f8f9fa;
line-height: 1.6;
}
a {
text-decoration: none;
color: inherit;
transition: color 0.3s;
}
a:hover {
color: #007bff;
}
button {
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
opacity: 0.9;
}
/* 容器样式(居中+限制宽度) */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 头部样式 */
.header {
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 15px 0;
position: sticky;
top: 0;
z-index: 999;
}
.header .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
color: #007bff;
font-weight: bold;
}
.nav ul {
list-style: none;
display: flex;
gap: 30px;
}
.nav a {
font-size: 16px;
font-weight: 500;
}
.nav a:hover {
color: #007bff;
border-bottom: 2px solid #007bff;
}
/* 主体样式 */
.main {
padding: 30px 0;
display: flex;
gap: 20px;
}
/* 文章列表样式 */
.article-list {
flex: 7;
}
.article-list h2 {
font-size: 22px;
color: #007bff;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
/* 文章项样式 */
.article-item {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
margin-bottom: 20px;
}
.article-item h3 {
font-size: 18px;
margin-bottom: 10px;
}
.article-item h3 a:hover {
color: #007bff;
}
.article-item .desc {
color: #666;
margin-bottom: 15px;
}
.article-item .meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
color: #999;
}
.article-item .like-btn {
display: flex;
align-items: center;
gap: 5px;
color: #ff4444;
background: transparent;
border: none;
padding: 5px 0;
}
/* 侧边栏样式 */
.sidebar {
flex: 3;
}
.search-box, .hot-tags {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
margin-bottom: 20px;
}
.search-box h3, .hot-tags h3 {
font-size: 16px;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.search-box .search-input-wrap {
display: flex;
gap: 10px;
}
#searchInput {
flex: 1;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
#searchBtn {
padding: 8px 16px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
}
/* 热门标签样式 */
#hotTagsList {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag-item {
padding: 5px 12px;
background-color: #e9ecef;
border-radius: 20px;
font-size: 14px;
color: #666;
cursor: pointer;
transition: all 0.3s;
}
.tag-item:hover {
background-color: #007bff;
color: #fff;
}
/* 留言板样式 */
.message {
padding: 20px 0;
}
.message h2 {
font-size: 22px;
color: #007bff;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
#messageForm {
background-color: #fff;
padding: 25px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
margin-bottom: 30px;
}
#messageForm label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
#msgName, #msgContent {
width: 100%;
max-width: 600px;
padding: 10px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
margin-bottom: 5px;
}
#msgContent {
resize: vertical;
min-height: 120px;
}
.tips {
color: #dc3545;
font-size: 12px;
display: block;
margin-top: 5px;
}
#messageForm button {
padding: 10px 20px;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 4px;
font-size: 16px;
}
/* 留言列表样式 */
.msg-item {
background-color: #fff;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
margin-bottom: 15px;
}
.msg-item .msg-name {
font-weight: bold;
color: #007bff;
margin-bottom: 5px;
}
.msg-item .msg-time {
font-size: 12px;
color: #999;
margin-bottom: 10px;
}
/* 底部样式 */
.footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 25px 0;
margin-top: 40px;
font-size: 14px;
}
/* 响应式样式(平板/手机) */
@media (max-width: 768px) {
/* 主体改为纵向排列 */
.main {
flex-direction: column;
}
/* 导航项间距减小 */
.nav ul {
gap: 15px;
}
/* 文章列表/侧边栏宽度100% */
.article-list, .sidebar {
flex: 100%;
}
/* Logo字体缩小 */
.logo {
font-size: 20px;
}
}
样式核心亮点
- 响应式布局:用
@media适配768px以下屏幕,主体从横向变为纵向; - 交互体验:所有链接/按钮添加hover效果、过渡动画,提升流畅度;
- 视觉层次:用阴影、边框、间距区分不同模块,重点内容(标题)用蓝色突出;
- 粘性导航:头部设置
position: sticky,滚动时导航固定在顶部,提升体验。
验证效果
- 电脑端:主体分为文章列表(70%)和侧边栏(30%),布局清晰,hover效果正常;
- 平板/手机端(缩小浏览器窗口):主体变为纵向排列,导航间距减小,适配小屏幕;
- 滚动页面:头部导航固定在顶部,不会被遮挡;
- 所有按钮/链接hover时,颜色/透明度变化,反馈明显。
课时76:实战4——编写JS交互功能(简易个人博客)
学习目标
整合JS核心知识,实现博客的核心交互:动态渲染文章/标签、搜索筛选、文章点赞、留言提交。
完整JS代码(替换script标签内内容)
// 1. 模拟数据
const articles = [
{
id: 1,
title: 'HTML语义化的重要性',
desc: '语义化标签让页面结构更清晰,利于SEO和维护。常用的语义化标签有header、main、aside、footer、article等,替代传统的div布局。',
time: '2025-01-01',
like: 12,
tag: ['HTML', '基础']
},
{
id: 2,
title: 'Flex布局快速上手',
desc: 'Flex布局是前端必备的布局方式,轻松实现水平垂直居中、等分布局、响应式布局。核心是容器的display: flex,以及justify-content、align-items等属性。',
time: '2025-01-05',
like: 28,
tag: ['CSS', '布局']
},
{
id: 3,
title: 'JS DOM操作核心技巧',
desc: '获取元素、修改样式、绑定事件,是DOM操作的核心知识点。推荐使用querySelector/querySelectorAll,比传统的getElementById更灵活。',
time: '2025-01-10',
like: 45,
tag: ['JS', 'DOM']
}
];
const hotTags = ['HTML', 'CSS', 'JS', 'Flex', 'DOM', '表单验证'];
// 2. 初始化页面(渲染文章、标签)
window.addEventListener('DOMContentLoaded', function() {
renderArticles(articles); // 渲染所有文章
renderHotTags(); // 渲染热门标签
});
// 3. 渲染文章列表
function renderArticles(articleList) {
const articleItems = document.getElementById('articleItems');
articleItems.innerHTML = ''; // 清空原有内容
if (articleList.length === 0) {
articleItems.innerHTML = '<p style="text-align: center; color: #999; padding: 20px;">未找到相关文章</p>';
return;
}
articleList.forEach(article => {
// 创建文章项
const articleItem = document.createElement('div');
articleItem.className = 'article-item';
articleItem.innerHTML = `
<h3><a href="#">${article.title}</a></h3>
<p class="desc">${article.desc}</p>
<div class="meta">
<span>${article.time}</span>
<button class="like-btn" data-id="${article.id}">
<span>👍</span>
<span class="like-count">${article.like}</span>
</button>
</div>
`;
articleItems.appendChild(articleItem);
});
// 绑定点赞事件
bindLikeEvent();
}
// 4. 渲染热门标签
function renderHotTags() {
const hotTagsList = document.getElementById('hotTagsList');
hotTagsList.innerHTML = '';
hotTags.forEach(tag => {
const tagItem = document.createElement('span');
tagItem.className = 'tag-item';
tagItem.innerText = tag;
// 标签点击筛选(可选)
tagItem.addEventListener('click', function() {
const filtered = articles.filter(article => article.tag.includes(tag));
renderArticles(filtered);
});
hotTagsList.appendChild(tagItem);
});
}
// 5. 文章点赞功能
function bindLikeEvent() {
const likeBtns = document.querySelectorAll('.like-btn');
likeBtns.forEach(btn => {
btn.addEventListener('click', function() {
const articleId = parseInt(this.dataset.id);
const likeCount = this.querySelector('.like-count');
// 找到对应文章,更新点赞数
const article = articles.find(item => item.id === articleId);
if (article) {
article.like += 1;
likeCount.innerText = article.like;
// 点赞动画(优化体验)
this.style.transform = 'scale(1.2)';
setTimeout(() => {
this.style.transform = 'scale(1)';
}, 300);
}
});
});
}
// 6. 搜索功能
const searchInput = document.getElementById('searchInput');
const searchBtn = document.getElementById('searchBtn');
// 搜索按钮点击
searchBtn.addEventListener('click', function() {
searchArticles();
});
// 回车搜索
searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
searchArticles();
}
});
// 搜索逻辑
function searchArticles() {
const keyword = searchInput.value.trim().toLowerCase();
if (!keyword) {
// 空关键词显示所有文章
renderArticles(articles);
return;
}
// 筛选标题包含关键词的文章
const filtered = articles.filter(article => {
return article.title.toLowerCase().includes(keyword);
});
renderArticles(filtered);
}
// 7. 留言功能
const messageForm = document.getElementById('messageForm');
const msgName = document.getElementById('msgName');
const msgContent = document.getElementById('msgContent');
const msgNameTips = document.getElementById('msgNameTips');
const msgList = document.getElementById('msgList');
// 存储留言数据(临时,刷新后丢失)
let messages = [];
messageForm.addEventListener('submit', function(e) {
e.preventDefault(); // 阻止默认提交
const name = msgName.value.trim();
const content = msgContent.value.trim();
let isPass = true;
// 验证姓名
if (name === '') {
msgNameTips.innerText = '姓名不能为空!';
isPass = false;
msgName.focus();
} else if (name.length < 2) {
msgNameTips.innerText = '姓名至少2个字!';
isPass = false;
msgName.focus();
} else {
msgNameTips.innerText = '';
}
// 验证留言内容
if (content === '') {
if (isPass) {
alert('留言内容不能为空!');
msgContent.focus();
}
isPass = false;
}
// 提交成功
if (isPass) {
const now = new Date();
const time = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
// 添加留言数据
const newMsg = {
id: Date.now(), // 用时间戳做唯一ID
name: name,
content: content,
time: time
};
messages.unshift(newMsg); // 新增留言放最前面
// 渲染留言列表
renderMessages();
// 重置表单
this.reset();
alert('留言提交成功!');
}
});
// 渲染留言列表
function renderMessages() {
msgList.innerHTML = '';
if (messages.length === 0) {
msgList.innerHTML = '<p style="text-align: center; color: #999; padding: 20px;">暂无留言,快来第一条留言吧!</p>';
return;
}
messages.forEach(msg => {
const msgItem = document.createElement('div');
msgItem.className = 'msg-item';
msgItem.innerHTML = `
<div class="msg-name">${msg.name}</div>
<div class="msg-time">${msg.time}</div>
<div class="msg-content">${msg.content}</div>
`;
msgList.appendChild(msgItem);
});
}
// 初始化留言列表
renderMessages();
交互功能说明
| 功能 | 核心逻辑 |
|---|---|
| 动态渲染文章 | 遍历文章数组,创建DOM元素,添加到页面;支持空结果提示 |
| 热门标签 | 渲染标签列表,点击标签筛选对应文章(按tag匹配) |
| 文章点赞 | 点击点赞按钮,更新对应文章的点赞数,添加缩放动画反馈 |
| 搜索功能 | 按标题关键词筛选文章(不区分大小写),支持按钮点击/回车搜索,空关键词显示全部 |
| 留言功能 | 验证姓名/留言内容,提交成功后添加到留言数组,渲染留言列表(新增留言置顶) |
易错点提醒
- ❌ 搜索功能区分大小写:未将标题和关键词转为小写,导致“HTML”和“html”不匹配;
- ❌ 点赞数刷新后重置:用临时数组存储数据,未做本地存储(进阶可加localStorage);
- ❌ 留言时间格式错乱:月份/日期/小时未补0(比如1月显示为1,应显示为01),用
padStart(2, '0')解决; - ❌ 动态元素事件失效:渲染文章后未重新绑定点赞事件(renderArticles中调用bindLikeEvent)。
验证效果
- 页面加载后,自动渲染3篇文章和6个热门标签;
- 点击标签(如“JS”),筛选出包含该标签的文章;
- 点击文章点赞按钮,点赞数+1,有缩放动画;
- 在搜索框输入关键词(如“布局”),筛选出标题包含该词的文章,空关键词显示全部;
- 留言板输入姓名(≥2字)和内容,提交后显示在留言列表顶部,空值会提示错误。
课时77:实战5——测试与优化(简易个人博客)
学习目标
系统测试博客功能,修复常见bug,优化用户体验和基础性能,让项目更稳定、易用。
一、完整测试清单(逐项验证)
| 测试类型 | 测试项 | 预期结果 | 测试方法 |
|---|---|---|---|
| 功能测试 | 文章列表渲染 | 页面加载后显示3篇文章,格式正确 | 打开页面直接查看 |
| 热门标签筛选 | 点击标签(如CSS),只显示对应标签的文章 | 点击不同标签,检查文章列表变化 | |
| 文章点赞 | 点击点赞按钮,数字+1,有缩放动画 | 点击点赞按钮,观察数字和动画 | |
| 搜索功能(关键词/回车) | 输入关键词筛选文章,回车/点击按钮都生效 | 输入“HTML”“布局”等关键词测试 | |
| 留言提交(空值/短姓名/正常) | 空值提示错误,正常输入提交成功并显示 | 分别测试空姓名、1字姓名、正常输入场景 | |
| 兼容性测试 | 电脑端布局 | 文章列表+侧边栏横向排列,无错乱 | 浏览器窗口宽度≥768px时查看 |
| 手机端布局 | 主体纵向排列,导航适配,无内容溢出 | 浏览器窗口宽度≤768px或手机模拟器查看 | |
| 体验测试 | 导航粘性 | 滚动页面时导航固定在顶部 | 向下滚动页面,观察导航位置 |
| 按钮/链接hover效果 | 颜色/透明度变化,反馈明显 | 鼠标移到按钮/链接上查看 | |
| 空状态提示(搜索无结果) | 显示“未找到相关文章”,而非空白 | 输入不存在的关键词(如“Python”)测试 |
二、常见bug修复(实战高频问题)
1. 问题:搜索关键词为空时,仍显示“未找到相关文章”
- 原因:搜索逻辑中未正确判断空关键词
- 修复代码(修改
searchArticles函数):
function searchArticles() {
const keyword = searchInput.value.trim().toLowerCase();
// 修复:空关键词直接渲染所有文章
if (!keyword) {
renderArticles(articles);
searchInput.value = ''; // 清空输入框(优化)
return;
}
const filtered = articles.filter(article => {
return article.title.toLowerCase().includes(keyword);
});
renderArticles(filtered);
}
2. 问题:留言时间显示为“2025-1-5”(月份/日期未补0)
- 原因:日期格式化时未处理个位数
- 修复代码(修改留言提交的时间生成逻辑):
// 封装日期格式化函数(复用)
function formatDate(date) {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
}
// 留言提交时调用
const time = formatDate(now);
3. 问题:手机端输入框宽度溢出
- 原因:输入框最大宽度限制未适配手机
- 修复CSS(添加到响应式样式中):
@media (max-width: 768px) {
#msgName, #msgContent {
max-width: 100%; // 手机端输入框占满宽度
}
}
4. 问题:点赞动画重复触发导致样式异常
- 原因:未清除之前的动画定时器
- 修复代码(修改
bindLikeEvent函数):
btn.addEventListener('click', function() {
const articleId = parseInt(this.dataset.id);
const likeCount = this.querySelector('.like-count');
const article = articles.find(item => item.id === articleId);
if (article) {
// 修复:先清除之前的动画,避免叠加
clearTimeout(this.animateTimer);
article.like += 1;
likeCount.innerText = article.like;
this.style.transform = 'scale(1.2)';
this.animateTimer = setTimeout(() => {
this.style.transform = 'scale(1)';
}, 300);
}
});
三、用户体验优化(低成本高收益)
1. 本地存储留言(刷新后不丢失)
- 核心逻辑:用
localStorage存储留言数据,初始化时读取 - 修复代码:
// 初始化留言数据(从本地存储读取)
let messages = JSON.parse(localStorage.getItem('blogMessages')) || [];
// 渲染留言列表函数末尾添加:
localStorage.setItem('blogMessages', JSON.stringify(messages));
// 留言提交成功后,更新本地存储:
function renderMessages() {
// ... 原有渲染逻辑 ...
// 新增:存储到localStorage
localStorage.setItem('blogMessages', JSON.stringify(messages));
}
2. 搜索框输入提示(placeholder优化)
- 修改HTML中搜索框的placeholder:
<input type="text" id="searchInput" placeholder="输入标题关键词(如HTML、布局)" style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
3. 按钮禁用状态(表单提交时防止重复点击)
- 修改留言提交逻辑:
messageForm.addEventListener('submit', function(e) {
e.preventDefault();
const submitBtn = this.querySelector('button[type="submit"]');
// 禁用按钮,防止重复提交
submitBtn.disabled = true;
submitBtn.innerText = '提交中...';
// 原有验证逻辑...
if (isPass) {
// ... 原有提交逻辑 ...
// 恢复按钮状态
submitBtn.disabled = false;
submitBtn.innerText = '提交留言';
} else {
// 验证失败也恢复按钮
submitBtn.disabled = false;
submitBtn.innerText = '提交留言';
}
});
4. 平滑滚动(页面内跳转更流畅)
- 添加全局CSS:
html {
scroll-behavior: smooth;
}
四、基础性能优化(新手版)
- 减少DOM操作次数:渲染列表时先拼接HTML字符串,再一次性插入(替代多次
appendChild),示例:
function renderArticles(articleList) {
const articleItems = document.getElementById('articleItems');
if (articleList.length === 0) {
articleItems.innerHTML = '<p style="text-align: center; color: #999; padding: 20px;">未找到相关文章</p>';
return;
}
// 优化:拼接HTML字符串,减少DOM操作
let html = '';
articleList.forEach(article => {
html += `
<div class="article-item">
<h3><a href="#">${article.title}</a></h3>
<p class="desc">${article.desc}</p>
<div class="meta">
<span>${article.time}</span>
<button class="like-btn" data-id="${article.id}">
<span>👍</span>
<span class="like-count">${article.like}</span>
</button>
</div>
</div>
`;
});
articleItems.innerHTML = html;
bindLikeEvent();
}
- 图片优化(若后续添加文章封面):添加
loading="lazy"实现懒加载; - 事件委托(进阶,优化标签点击事件):将标签的点击事件绑定到父元素,减少事件绑定数量。
验证优化效果
- 刷新页面后,之前提交的留言仍存在;
- 快速点击点赞按钮,动画不会错乱;
- 手机端输入框占满宽度,无溢出;
- 提交留言时按钮禁用,防止重复提交;
- 页面内跳转(如导航点击)有平滑滚动效果。
课时78:项目复盘——从0到1开发个人博客的核心思路
学习目标
总结个人博客项目的开发流程,提炼通用的前端开发思路,让你能复用这套逻辑开发其他项目。
一、前端项目通用开发流程(新手版)
需求分析 → 结构规划 → 样式开发 → 交互开发 → 测试优化 → 上线(可选)
1. 需求分析:明确“做什么”
- 核心:把模糊需求拆成“可落地的功能点”,比如“博客”拆成: ✅ 展示文章列表 ✅ 标签筛选 ✅ 搜索 ✅ 点赞 ✅ 留言
- 新手技巧:只做“核心功能”,先跑通再拓展(比如先实现文章渲染,再做点赞)。
2. 结构规划:明确“页面有什么”
- 核心:用语义化HTML搭建骨架,区分“静态区域”和“动态区域”:
- 静态区域:导航、底部、标题(固定不变);
- 动态区域:文章列表、留言列表(JS渲染);
- 新手技巧:先写死静态内容,确认布局没问题后,再用JS替换为动态数据。
3. 样式开发:明确“页面长什么样”
- 核心:先写全局样式(重置、基础字体、颜色),再写模块样式(文章、侧边栏),最后写响应式;
- 新手技巧:
✅ 用
flex布局替代浮动,减少兼容性问题; ✅ 先做电脑端,再用@media适配手机端; ✅ 统一配色(比如主色#007bff,辅助色#28a745),避免杂乱。
4. 交互开发:明确“页面能做什么”
- 核心:数据驱动页面,逻辑封装复用:
- 定义模拟数据(数组/对象);
- 封装渲染函数(如
renderArticles),数据变了就重新渲染; - 事件绑定:优先封装成函数(如
bindLikeEvent),动态元素渲染后重新绑定。 - 新手技巧:
❌ 不要把所有代码写在一起,按功能拆分(比如渲染、事件、工具函数分开);
✅ 用
console.log调试(比如渲染前打印数据,确认数据正确)。
5. 测试优化:明确“页面好不好用”
- 核心:按“测试清单”逐项验证,修复bug+优化体验;
- 新手技巧: ✅ 先自测(比如自己点一遍所有按钮); ✅ 用浏览器“手机模拟器”测试响应式; ✅ 优先修复“影响使用的bug”(比如搜索失效),再优化体验(比如动画)。
二、项目中踩过的坑 & 避坑技巧
| 常见坑 | 避坑技巧 |
|---|---|
| 动态元素事件绑定无效 | 渲染后再绑定事件,或用事件委托 |
| 响应式布局错乱 | 用flex+max-width,少用固定宽度 |
| 表单重复提交 | 提交时禁用按钮,成功/失败后恢复 |
| 数据刷新后丢失 | 用localStorage存储关键数据(留言、点赞) |
| 样式优先级冲突 | 少用!important,用类名精准定位元素 |
三、可拓展的功能(学完后可以尝试)
- 文章详情页:点击文章标题跳转到详情页(用
location.href传参); - 点赞数持久化:把文章点赞数也存到
localStorage; - 留言删除功能:给每条留言加删除按钮,点击后删除并更新列表;
- 夜间模式:添加切换按钮,修改
body的背景色/文字色。
四、新手开发建议
- 不要“抄代码”:理解每一行代码的作用,比如
e.preventDefault()是阻止表单默认提交; - 遇到问题先自查:
- 看浏览器控制台(F12)有没有报错;
- 打印关键变量(比如
console.log(articles)),确认数据正确; - 复用代码:比如博客的“渲染列表”逻辑,可直接复用在“商品列表”“新闻列表”项目中。
课时79:前端学习路径规划(新手友好版)
学习目标
基于个人博客项目的学习经验,规划后续的前端学习路线,让你知道“接下来该学什么”。
一、新手前端学习路径(6-12个月)
| 阶段 | 学习内容 | 核心目标 | 实战项目 |
|---|---|---|---|
| 基础阶段(1-2月) | HTML(语义化)、CSS(flex/grid)、JS(DOM/事件) | 能做静态页面+简单交互 | 个人主页、博客、登录页 |
| 进阶阶段(2-3月) | ES6+(箭头函数/解构/模块化)、Ajax(接口请求)、本地存储 | 能对接后端接口,动态获取数据 | 带接口的博客、天气查询、待办清单 |
| 框架阶段(3-4月) | Vue3(或React)、Vite、组件化开发 | 能开发复杂交互的单页应用 | 电商首页、管理后台、个人博客2.0 |
| 工程化阶段(2-3月) | Git、打包优化、移动端适配、小程序(可选) | 能规范开发、上线项目 | 上线个人博客、小程序版博客 |
二、当前阶段(基础阶段)的重点任务
- 巩固JS核心:
- 重点学:数组方法(forEach/filter/map)、函数封装、DOM操作;
- 练习:把博客的留言功能改成“可删除/编辑”,加深对数组操作的理解。
- 补充CSS知识:
- 学习
grid布局(适合复杂网格,比如相册); - 学习CSS变量(统一管理配色,比如
:root { --main-color: #007bff; })。 - 做“多案例练习”:
- 仿写:登录页、新闻列表、商品卡片;
- 核心:复用“数据渲染+事件绑定”的逻辑,熟练后会形成肌肉记忆。
三、新手学习技巧(避坑)
- 不要贪多:先学Vue3或React其中一个,学透再学另一个;
- 多动手少看视频:每学一个知识点,立刻写代码验证(比如学了
filter,就用它做标签筛选); - 善用工具:
- 查API:MDN(https://developer.mozilla.org/zh-CN/);
- 找样式:Bootstrap(https://getbootstrap.com/)(可直接用现成样式,减少重复造轮子);
- 调试:浏览器F12(Elements看结构、Console看报错、Network看接口)。
四、下一步学习建议(基础阶段→进阶阶段)
- 学习ES6+:重点掌握箭头函数、解构赋值、模板字符串、模块化(import/export);
- 学习Ajax:用
fetch或axios请求后端接口(比如用免费的测试接口https://jsonplaceholder.typicode.com/); - 把博客改造为“接口版”:用Ajax获取文章数据,替代本地模拟数据。
课时80:最终总结与常见问题答疑
学习目标
总结整个前端基础学习的核心知识点,解答新手高频问题,给你后续学习的信心和方向。
一、前端基础核心知识点总结
| 技术 | 核心掌握点 | 一句话概括 |
|---|---|---|
| HTML | 语义化标签、表单、链接、图片 | 搭建页面骨架,让页面“有结构” |
| CSS | flex/grid布局、响应式、选择器、样式优先级 | 美化页面,让页面“好看” |
| JavaScript | 变量/数据类型、数组/对象、函数、DOM/事件、本地存储 | 实现交互,让页面“能动” |
二、新手高频问题答疑
1. 学了记不住怎么办?
- 核心:“用进废退”,不是记不住,是用得少。
- 解决: ✅ 每天花30分钟写代码(比如给博客加一个小功能); ✅ 把常用代码(比如渲染列表、表单验证)整理成“代码模板”,下次直接复用。
2. 遇到bug解决不了怎么办?
- 核心:把“大问题拆成小问题”,逐步排查。
- 解决步骤:
- 看控制台报错(F12→Console),报错信息会告诉你“哪一行错了”“错在哪”;
- 打印关键变量(
console.log),确认数据是否正确; - 简化代码:把无关代码注释掉,只留出问题的部分,比如先注释点赞动画,看点赞数是否正常;
- 查资料:把报错信息复制到百度/MDN,找解决方案。
3. 什么时候学框架(Vue/React)?
- 核心:基础打牢再学框架,框架是“简化JS交互的工具”,不是“替代JS”。
- 判断标准: ✅ 能独立开发像“个人博客”这样的项目,不用抄代码; ✅ 理解“数据驱动页面”的逻辑(比如改数据→重新渲染); ✅ 知道DOM操作、事件绑定、本地存储的核心用法。
4. 新手需要学后端吗?
- 核心:前期不用深学,够用就行。
- 建议: ✅ 先学“前端能接触到的后端知识”:比如用免费的测试接口(jsonplaceholder)模拟数据; ✅ 后期想做完整项目,可学Node.js(和JS语法一致,新手友好),或用PHP/Python写简单接口。
5. 项目怎么上线?
- 核心:新手用“免费平台”快速上线,体验完整流程。
- 步骤(简易版):
- 把项目文件(HTML/CSS/JS)整理好;
- 注册免费托管平台(比如GitHub Pages、Netlify、Vercel);
- 上传文件,获取在线访问地址。
三、最后寄语
- 前端学习是“循序渐进”的,不要着急:从静态页面→简单交互→复杂项目,每一步都有收获;
- 不要怕写bug:每个前端工程师都是在改bug中成长的,bug是最好的老师;
- 多做项目:最好的学习方式是“做项目”,哪怕是模仿别人的项目,做一遍也比看十遍视频有用;
- 保持好奇心:遇到好看的网页,右键→“检查”,看看别人是怎么写的,学习优秀的代码思路。