HTML与CSS基础复习:从文档结构到盒模型居中与动画 📅 发布时间:2026/9/19 7:38:42 👁 浏览次数: 做前端的这些年我发现自己陷入过一个循环新框架出来就追组件库换了一茬又一茬可真到某个晚上要写一个不带任何框架的纯页面时反而被 HTML 和 CSS 的基础细节卡住了。尤其是!doctype html、html langzh-cn这种每天都会见到的代码片段背都背过可被问到“为什么必须这么写”时我竟然一时讲不清楚。于是我把 HTML 和 CSS 认认真真复习了一遍。这篇内容不是官方文档的搬运也不是什么速成指南而是我从自身踩坑角度整理出来的复习笔记。里面涉及文档结构、CSS定义与引用、盒模型、字体样式、居中方案、鼠标移入事件、动画效果、表格与段落分割线这些高频知识点也包含了很多搜遍全网都很少有人讲透的细节。无论你是刚学完基础准备进场的新人还是已经写了两年业务代码想要查漏补缺的老人这篇文章都值得耐心看完。1. 复习HTML时建议先从文档头结构看起1.1 到处都是那段样板代码但你真的理解了吗现在在搜索引擎里输入“html”出来最多的一串代码就是!doctype html html langzh-cn head meta charsetutf-8 ...。我最初看到这股搜索热象时还挺意外因为这段代码太基础了基础到很多编辑器都在自动补全。后来我才明白大家搜它不是因为不会写而是因为想搞懂每一行的含义。!doctype html是 HTML5 的文档类型声明。它的作用是告诉浏览器“请用标准模式来解析这个页面”而不是老旧的怪异模式quirks mode。在怪异模式下IE 时代的盒模型规则会被启用同样的 CSS 在不同浏览器里渲染结果可能完全不一样。所以这行声明不是给 SEO 看的也不是给人看的是给浏览器底层的解析器看的。少了它页面往往会给你一个措手不及的惊喜。html langzh-cn里的lang属性很多人以为只是标注语言方便翻译插件使用其实它更重要的价值是让屏幕阅读器等辅助工具能正确读出页面文字。对于中文网页来说写成zh-cn和zh-CN都可以主要看项目面向的地区习惯如果是面向简体中文用户显然没必要写en。meta charsetutf-8是字符编码声明它管的是浏览器以什么编码去解码 HTML 文件。如果文件本身是 UTF-8 编码但这里漏写了浏览器就会按照默认编码去猜结果中文全部变成乱码。这种情况我在老项目里见过太多次了修复方式也只是补一行代码而已但排查起来会让人极度崩溃。复习到这里的时候我特意提醒自己文档头里的每一行都不是“别人都这么写所以我也写”而是背后有明确的原因。1.2 常见标签的块级、行内、替换元素分类另一个值得复习的点是元素分类。HTML 标签表面看起来就是一堆名字实际上它们默认的显示方式决定了 CSS 样式能不能生效。块级元素像div、p、h1这类默认独占一行宽度默认填满父容器上下 margin 可以正常设置行内元素像span、a、strong这类默认在同一行排列宽度由内容撑开而且上下 margin、width、height 这些属性对它们无效。这里有个很容易踩的坑行内元素设置padding和border的时候视觉上能看到但它们不会影响周围元素的位置。打个比方一个span加了上下 padding视觉上文字周围有了一片背景区域可上面的文字并不会因此被推下去这就会造成一部分内容被遮挡或者重叠。解决思路很简单——要么把它变成行内块元素用display: inline-block要么直接改成块级元素display: block。还有一类替换元素比如img、input、iframe。它们比较特殊因为内容来自外部资源所以它们本身具有内在尺寸可以被设置宽高。复习的时候把这些元素分类整理一遍对后面理解 CSS 的居中、布局都特别有帮助。1.3 HTML到底是不是编程语言这不重要重要的是语义化复习的时候必须聊一下语义化。这个问题在很多新手群里吵得不可开交有人坚持说“HTML是标记语言不算编程”其实无所谓。你要关心的是用div一把梭写出来的页面和用header、nav、main、article、footer搭出来的页面代码可读性和 SEO 表现会有明显差距。语义化标签最大的好处是“自己会说话”。nav一看就是导航article一看就是文章主体。这样做既方便团队协作时同事快速理解页面结构也能帮助搜索引擎抓取重要内容。复习的时候我给自己定了一个标准能不写div就不写div除非这个区域确实没有更贴切的语义化标签可选。另外很多开发者会忽略label标签在表单中的价值。不只是点击文字能聚焦对应的输入框这么简单它还是无障碍访问的基础。复习 HTML 选择框的时候select、option、optgroup这三个标签也需要理清楚。optgroup可以给选项分组这在做商品分类、地区选择这类长列表时很实用。2. CSS定义与引用以及最重要的选择器优先级2.1 三种CSS引入方式的取舍CSS 定义与引用是一个老生常谈的问题但我发现很多刚接触前端的人对三种方式的适用场景是模糊的。第一种是行内样式直接写在元素的style属性里。这种方式优先级最高除了!important谁也压不住它但它让结构和样式高度耦合维护成本极高。第二种是内嵌样式写在head里的style标签中。适合单页面、小型的项目或者某些只需要在特定页面生效的样式。第三种是外部样式表用link引入.css文件这是实际项目里最推荐的方式因为可以复用、缓存也符合关注点分离的原则。我在复习的时候给自己列了一个决策表表格CSS引入方式对比引入方式写法位置优先级适用场景行内样式元素 style 属性最高动态数据驱动的临时样式、邮件模板内嵌样式head 内 style 标签较高单页应用入口、首屏关键样式外部样式link 引入 css 文件正常多页面项目、组件库、长期维护2.2 选择器优先级别再背什么“1000、100、10、1”了很多人复习 CSS 选择器的时候喜欢背一个权重公式内联样式算 1000ID 选择器算 100类选择器、属性选择器、伪类算 10标签选择器和伪元素算 1。这个公式用来应付面试确实够但实际开发中我更习惯用“比较规则”来理解。看优先级的时候先看有没有!important有的话它最大没有!important的话再看选择器里 ID 的个数ID 个数相同看类、属性、伪类的个数再相同看标签名和伪元素的个数。如果这些都一样那就看谁在样式表里出现的顺序更靠后后者覆盖前者。这样理解比死记数字更可靠遇到复杂选择器时也能快速判断到底谁会生效。通配符*也要单独说。好多参考书告诉我们“*可以选中所有元素”于是新手喜欢写* { margin: 0; padding: 0; }。这个写法确实能清掉浏览器默认边距方便起步但它的缺点也不能忽视。第一*会给页面里所有元素都加上声明包括未来动态插入的元素这在性能上并不是最优解虽然现代浏览器的处理速度已经很快了但在很大型的页面上仍然会造成没必要的开销。第二它破坏了浏览器默认样式的一致性比如p和ul本来有合理的上下 margin一条通配清除让所有元素全靠你自己重新定义间距这在开发后期很容易出现“这个元素怎么没有间隙”的疑问。我现在的建议是像 reset 这类需求尽量用更精确的body, h1, h2, h3, p, ul, ol, li { ... }这样的分组选择器或者直接引入成熟的 reset 方案。在复习 CSS 的时候你能理解*的优缺点比单纯复制一段 reset 代码要重要得多。2.3 原子性CSS的新视角“原子性CSS”这个词在热搜里也很常见。它到底是什么呢简单说把 CSS 属性按最小粒度拆分成独立的类比如.text-center { text-align: center; }、.mt-10 { margin-top: 10px; }然后 HTML 里通过组合这些类来搭出想要的样式。这种风格之前被很多人吐槽“回到了行内样式时代”但真实开发中它的逻辑并不是让你到处堆类而是面向高频复用的属性做统一管理。我在复习的时候重新思考了原子性 CSS 的优势它特别适合组件化开发。组件内多种状态切换时通过动态切换 class 比写一大段样式覆盖要干净得多同时重复样式被打包成原子类后代码体积也会明显下降。不过原子性 CSS 也有缺点——类名晦涩、复用组合导致可读性下降以及 HTML 结构里会出现一堆看起来“无意义”的类名。所以它并不是银弹更像是一种在特定项目里选择的风格。3. 盒模型、字体和文本样式细节决定质感3.1 盒模型复习从 content-box 到 border-box盒模型是整个 CSS 的基石。复习的时候千万别只看一张带 content、padding、border、margin 四层结构的图就完了你要重点理解box-sizing的影响。标准盒模型content-box下一个元素的宽度等于 content 的宽度而 padding 和 border 会向外扩展。这意味着你设width: 200px再设padding: 20px、border: 5px元素实际占用的宽度是 200 202 52 250px。这在栅格布局里非常容易导致计算错乱。怪异盒模型border-box下宽度是 content、padding、border 的总和。你设width: 200px无论 padding 和 border 怎么改元素的总宽度都是 200px只是内容区会被压缩。现代项目几乎清一色都在全局开启border-box就是因为它更符合直觉也更好管控。我建议复习完这个概念后用浏览器控制台亲手改一次box-sizing感受一下元素的占位变化这比盯着文档看来的效果好得多。盒模型不是只在面试题里出现的概念它在日常布局中几乎每天都在起作用。3.2 CSS字体相关删除线、字体渐变、外描边CSS 字体相关的样式也是复习重点。先说最容易理解的text-decoration: line-through这就是删除线。做商品原价显示、任务列表的完成态标记时用它比画一条背景线要干净。如果你想控制删除线的颜色和粗细可以写成text-decoration: line-through 2px red这是简短写法也支持分开写。字体渐变是个被低估的技巧。通常我们提到渐变就想到背景background-image: linear-gradient(...)但要让文字本身有渐变效果需要配合background-clip: text和color: transparent。思路是先给文字元素加背景渐变再把背景裁切到文字区域最后把字体的默认颜色变成透明渐变就露出来了。兼容性在现代浏览器里已经完全没问题老掉牙的 IE 就忽略好了。字体外描边实现方式有很多最常见的是用-webkit-text-stroke直接给文字描边。但要注意它会从文字中心线向外扩张一半导致字看起来偏细。如果不想影响字面可以用text-shadow的四个方向各描一遍来模拟外描边。比如text-shadow: 1px 0 0 #000, -1px 0 0 #000, 0 1px 0 #000, 0 -1px 0 #000。这套写法虽然朴素但兼容面广在控制字体粗细场景下很实用。另外还需要复习一下font-family。不同操作系统默认字体不同所以中文网页通常写一长串回退字体列表比如font-family: system-ui, -apple-system, Segoe UI, PingFang SC, Microsoft YaHei, sans-serif。系统字体是什么就按顺序优先匹配。这个知识点看似简单实际影响整个页面的观感和性能。3.3 文本溢出省略单行与多行的正确姿势文本溢出是高频需求也是大家抄代码经常抄出问题的点。单行省略的标准三件套是white-space: nowrap强制不换行、overflow: hidden隐藏溢出部分、text-overflow: ellipsis显示省略号。这三样缺一不可少一个效果都可能出不来。两行省略就需要用-webkit-line-clamp了。它本身是一个非标准属性但现代浏览器都支持。典型写法是.ellipsis-2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }这里的关键是display: -webkit-box配合-webkit-box-orient: vertical让容器内部以垂直方向排列子元素然后-webkit-line-clamp限制显示行数。如果你把容器改成display: flex这个方案就失效了因为布局模式已经改变。所以复习的时候更重要的不是复制这一段代码而是理解-webkit-box和flex各自的工作机制。3.4 段落分割线hr还是border“CSS实现段落分割线”是热搜里的高频词。做法通常两种。第一种是直接用hr标签它自带语义“故事转折或主题切换”默认样式是水平线你可以通过 CSS 调整颜色、粗细和虚线效果。第二种是用元素的底边框比如给段落加border-bottom: 1px solid #e5e5e5。这两种方案的语义完全不一样。如果分割线只是想做一个视觉上的分隔用border-bottom更灵活不会额外增加 DOM 节点如果分割线代表内容层面的主题变化hr更符合 HTML 语义化要求。复习的时候我建议把这两种做法都掌握根据场景选择。4. 让人头疼的居中问题一次理清楚4.1 容器里的文本位置怎么调才合理“怎么调整css容器里的文本位置”这个搜索词本质是在问文本的对齐方式。水平方向有text-align: left/center/right垂直方向则要分情况。如果容器是单行文本直接设置容器的line-height等于容器高度就能实现垂直居中如果容器的文本可能换行line-height这种方案就不行了你应该用display: flex; align-items: center或者padding来让文本在垂直方向上有合理的间距。这里很容易犯的错是拿设置line-height的方式去处理多行文本。比如一个容器高度 60px里面有三行文本每行行高 20px你把line-height设成 60px 后三行文本就全重叠了因为它会让每一行都独占 60px 的高度。多行文本的正确做法是设置适中的line-height再用padding或者 flex 去调整整体位置。4.2 块级元素水平垂直居中的几种方案居中问题绝对是 CSS 里的重灾区。我把常用方案整理成了一张表每次忘记的时候直接翻表格块级元素居中方案对比方案核心代码优点限制margin automargin: 0 auto简单直接仅水平居中需定宽text-aligntext-align: center适合行内元素不适合块级元素宽度flexdisplay: flex; justify-content: center; align-items: center最推荐父容器需要确定的宽高或由内容撑开griddisplay: grid; place-items: center代码最短需要理解 grid 布局绝对定位transformposition: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%)兼容性好需要父容器相对定位开发里我用的最多的就是 flex 和 grid。flex 适用于大多数常规场景grid 适合二维布局时顺便做居中。绝对定位加 transform 适合弹窗、悬浮层这类需要脱离文档流的元素。4.3 input居中和body居中为什么总出问题“css中怎么把input居中”是另一个高频搜索。如果你把input当成普通块级元素处理只给父容器加text-align: center输入框并不会居中因为表单控件在浏览器里默认带有内联块特性但宽度和 margin 行为比较特殊。想让 input 居中一种方式是把 input 设为块级元素并给一个宽度然后用margin: 0 auto另一种是父容器用 flex 布局justify-content: center这样无论 input 宽度如何都能居中。“css body居中”的说法也经常出现。很多人问的是“怎么让整个页面的主体内容居中”这时候你要区分到底是让 body 的背景和内容一起居中还是让 body 里某一层容器居中。通常做法是给最外层容器设置一个最大宽度比如max-width: 1200px再配合margin: 0 auto。如果你直接给 body 设置text-align: center那只是把所有文本和行内元素居中块级元素根本不鸟这个设置这也是很多人疑惑的根源。4.4 让table水平居中还有一个历史悠久的问题table怎么居中。很多人在 table 上设置margin: 0 auto结果没有生效。原因是 table 的默认显示方式是display: table它的宽度是由内容决定的如果没有设置 width它在某些情况下的确可以自动收缩但这取决于 table 的宽度是否小于父容器以及父容器的布局模式。更稳妥的做法是把 table 的display改成block或者用父容器控制。如果你希望表格保持语义化且不破坏表结构最简单的方案是给 table 设置margin-left: auto; margin-right: auto同时确保 table 的 width 小于父容器宽度。如果 table 的 width 是 100%那它本身就占满了父容器根本没有居中的余地。顺便说一句在移动端布局里table 居中反而是次要问题优先考虑的是如何让表格在窄屏上不溢出。这里有个常用技巧是把 table 包在一个拥有overflow-x: auto的容器里让宽表格可以横向滚动。5. 从hover到涟漪光圈交互效果的复习路线5.1 鼠标移入事件在CSS里其实就是这两件事“css 鼠标移入事件”这个搜索词说到底是两个东西:hover伪类和与之搭配的transition。很多人以为鼠标移入就要写 JavaScript其实纯 CSS 就能处理大量交互状态。:hover的作用是当鼠标指针悬停在元素上时应用一组新的样式。常见用法是改变背景色、边框、阴影、透明度或者让元素产生位移。但这里有一个细节如果 hover 状态变化得太突然用户会觉得页面很生硬。所以通常要配合transition属性让这些样式的变化过程平滑发生。transition可以拆成transition-property、transition-duration、transition-timing-function和transition-delay四个子属性。实际开发里我习惯简写transition: all 0.3s ease不过用all会让所有可过渡属性都参与动画性能上不如指定transition: background-color 0.3s ease, transform 0.3s ease这么精准。:hover 并不是唯一与鼠标有关的伪类。比如:active是鼠标按下到触发之间的状态常用于按钮点击效果:focus用于表单元素或可聚焦元素获取焦点时对键盘用户和无障碍支持至关重要。5.2 CSS动画基础与涟漪光圈扩散效果提到 CSS 动画很多人会直接联想到keyframes。复习的时候核心要理解animation属性的构成动画名称、时长、速度曲线、延迟、播放次数和方向。典型写法keyframes ripple { 0% { transform: scale(0); opacity: 1; } 100% { transform: scale(4); opacity: 0; } } .ripple-effect { animation: ripple 0.6s ease-out forwards; }这段代码里forwards表示动画结束后保持最后一帧的状态。如果不写这个关键字动画结束后元素会立刻回到初始状态这是新手最容易疑惑的点。涟漪光圈扩散效果是很多设计稿里的常客。实现思路通常是一个相对定位的父元素里面放一个绝对定位的圆形子元素初始尺寸很小、透明度较高动画让它逐渐放大同时透明度降到 0。这样看起来就像水面上的涟漪逐渐向外扩散消失。如果你想要多个光圈依次扩散可以复制多个子元素分别设置不同的动画延迟时间让它们交错出现。我实际测试下来这种特效对性能有一定要求所以不要让涟漪元素的个数太多。同时记得给执行动画的元素加上will-change: transform; opacity这能告诉浏览器提前做好优化准备。不过will-change也不要滥用它本身会占用浏览器资源。5.3 最酷的css tab控件其实原理没那么神秘“最酷的css tab控件”也出现在热搜里。很多 tab 切换效果看起来复杂本质就是radio加label的巧妙组合。用input typeradio的选中状态配合:checked选择器可以实现纯 CSS 的选项卡切换。基本结构是一组 label 对应一组 radio内容面板通过兄弟选择器或者相邻选择器来展示。当某个 radio 被选中时对应的内容面板显示其他面板隐藏。这套方案的优点是不需要一行 JavaScript缺点是 radio 一旦选中就无法取消所以它更适合“必选其一”的场景。如果真的需要可切换但也可取消的场景还是用 JS 更方便。如果你想要更流畅的切换动画可以在内容面板上使用 opacity 和 transform 的组合。比如选中时面板透明度从 0 变到 1同时位移一点点视觉上会更柔和。5.4 transition 和 animation 怎么选复习到动画这里很多人会纠结同一个效果用transition还是animation。我的经验是如果你的交互只有两个状态比如默认状态和 hover 状态用transition就够了如果交互中间有多个关键帧比如做了三个阶段的变形那用keyframes的animation更合适。transition还经常用来定义“从 A 到 B”的过渡animation则更适合“A → B → C → D”这样更复杂的路径。二者不冲突实际项目里常混用。6. 一些零散但特别实用的HTML和CSS技巧6.1 一键返回顶部结构与滚动的配合“html一键返回顶部算法”这个词其实不涉及什么高深算法它更像是一个交互组件的设计。返回顶部按钮一般采用position: fixed定位在页面右下角点击后让页面滚动回顶部。HTML 结构可以是a href#top classback-to-top返回顶部/a这种方式利用了锚点定位点击后页面跳到 id 为 top 的元素位置但地址栏的 URL 会出现#top后缀。如果你不想改变 URL可以用 JavaScriptdocument.querySelector(.back-to-top).addEventListener(click, function () { window.scrollTo({ top: 0, behavior: smooth }); });behavior: smooth可以产生平滑滚动效果。如果只写window.scrollTo(0, 0)则是瞬间跳回体验偏硬。另外这个按钮最好在用户向下滚动超过一定距离后才显示避免一进页面就看到一个无用的按钮。这个显示逻辑可以监听window.onscroll判断window.scrollY是否大于某个阈值。6.2 关于html转为md、pyqt5显示html以及邮件模板搜热词里还有几个也很有意思。html转为md的问题本质上是想要把网页内容转换成易于编辑和版本管理的 Markdown 格式。服务端可以用现成的解析库比如把 HTML 字符串转成 AST再序列化成 Markdown。前端浏览器环境里也可以直接对用户选中的内容或者整个页面进行转换。这个需求常见于博客编辑器、运营后台、知识库系统。pyqt5显示html是桌面开发里的常见需求。PyQt5 的QTextBrowser或QWebEngineView可以直接渲染 HTML但要注意它们的 CSS 支持程度和浏览器不一样。做桌面应用内嵌页面的时候样式表尽量保持简单避免使用一些较新的 CSS 特性否则渲染出来会跟预期差很远。这里也提醒了我HTML 和 CSS 不只是浏览器的专利很多桌面框架也用它做界面描述语言但兼容性差异是绕不开的坑。邮件模板则是另一个特殊场景。写 HTML 邮件的时候很多人直接套用网页里那套布局方式结果在 Outlook 里直接翻车。邮件客户端对 CSS 的支持极其有限很多还停留在 IE 时代的渲染引擎。因此做邮件模板时要用 table 布局、行内样式图片要写清楚宽高。这跟我们平时做网页的思维完全相反复习的时候如果要兼顾这块业务必须单独拎出来专门整理。6.3 打包多个html、商品详情页、网页制作的整体思路搜热词里还有html css js商品详情、html简单网页代码、打包多个html这些其实都指向同一个问题你的项目应该如何组织和发布。商品详情页是典型的多区域页面通常包含商品图片、标题、价格、规格、详情介绍、评价等模块。纯手写时我会先把这些区域用语义化标签搭好骨架再逐步完善样式。这里的核心不是复制代码而是形成“内容优先、结构次之、样式最后”的开发顺序。先把页面里的所有信息列出来确定主次关系再写与之匹配的 HTML 结构最后用 CSS 美化。这套流程听起来慢实际上是最稳的。打包多个html一般是构建工具的问题。如果你是纯静态页面多个 html 之间直接通过相对路径互相跳转就行如果用打包工具比如 Vite 或 Webpack则要考虑多页面入口的配置。没有专门的插件直接按pages目录下每个 html 配一个入口文件即可。对于新项目我更推荐直接在浏览器里确认好页面结构和样式再决定是否引入构建工具避免无谓的复杂度。6.4 原子性的日常应用、选择框、input和表单样式回到日常开发里html输入框、html选择框这类表单元素才是最让人头疼的。表单控件在不同浏览器下的默认样式差异非常大尤其是select的下拉箭头Chrome 和 Firefox 画得都不一样input typecheckbox和input typeradio在 Windows 和 macOS 下面更是两种审美。想要统一这些控件的样式需要先清除默认外观使用appearance: none再绘制自己的样式。比如自定义 checkbox 的常见做法是隐藏原生 input再用::before画一个方框把勾选图标用伪元素加上去。复习这一块时一定要多浏览器测试因为表单控件是最容易出现兼容性差异的地方。还有 input 的各种类型尤其是typeemail、typenumber、typetel它们虽然能触发移动端的对应键盘但也带来了额外的校验逻辑。比如typenumber在 Safari 上表现跟 Chrome 不同处理不好反而给用户添麻烦。复习到这里我最大的感受是表单控件的样式统一比很多花哨的交互效果更考验 CSS 基础功。7. 复习完以后我的实际体会和一个小技巧这次复习 HTML 和 CSS我最大的体会是很多问题层出不穷不是因为知识有多难而是因为我们太依赖编辑器自动补全和组件库封装了。真正把这些基础重新过一遍后我发现许多以前需要搜来搜去的问题变成了潜意识里的肌肉记忆。最后分享一个我在复习过程中发现的实用小技巧。写 CSS 的时候尽量少用!important但如果某个第三方组件的样式确实覆盖不掉可以用!important做最坏打算。真正让样式覆盖可控的办法是提升选择器的具体程度比如给目标容器加一个父级类名写成.my-component .target-class { ... }这比!important可维护得多。另一个小技巧是学会利用浏览器的开发者工具。打开 DevTools选中元素后可以在 Styles 面板里直接看到所有匹配的规则按优先级排序被划掉的样式就是没生效的。排查 CSS 问题时先看这个面板往往比瞎猜快得多。复习完基础后再配合观察工具去理解每个属性带来的影响效果会非常明显。希望这篇复习笔记也能帮你把那些浮在表面的前端知识重新沉淀下来。