整理一些 HTML 和 CSS 中常用的常用概念—— 盒模型、BFC、margin 行为、圣杯/双飞翼布局、Flex、定位和响应式方案。这些点面试和日常开发中都容易出现。
HTML 语义化
用对的标签表达结构,而非全用 <div>:
<body>
<header><nav></nav></header>
<main>
<article><section></section></article>
<aside></aside>
</main>
<footer></footer>
</body>语义化标签举例:
| 标签 | 用途 |
|---|---|
<header> | 页头或区块头部 |
<nav> | 导航链接 |
<main> | 主内容(页面唯一) |
<article> | 独立可分发的内容(文章、帖子) |
<section> | 文档中的独立区块 |
<aside> | 侧栏、附加信息 |
<footer> | 页脚或区块底部 |
<figure> / <figcaption> | 插图及其说明 |
<time> | 日期或时间 |
<mark> | 高亮/标记文本 |
好处:代码可读性、SEO(搜索引擎理解页面结构)、无障碍访问(屏幕阅读器依赖标签构建导航)。
CSS 盒模型
#div1 {
width: 100px;
padding: 10px;
border: 1px solid #ccc;
margin: 10px;
}content-box(默认):width只作用于内容区。offsetWidth= width + padding-left/right + border-left/right = 100 + 20 + 2 = 122px。margin不参与offsetWidth计算。border-box:width包含 padding 和 border。offsetWidth= 100px。
content-box: [margin] [border] [padding] [====content====] [padding] [border] [margin]
border-box: [margin] [=====border + padding + content=====] [margin]
常见的全局重置:
*,
*::before,
*::after {
box-sizing: border-box;
}border-box 让尺寸计算更符合直觉——设置了 width: 50% 就是占一半,不需要额外计算 padding 和 border。
margin 的行为
纵向重叠(Collapse)
相邻块级元素的垂直 margin 会折叠,取较大值而非相加:
p {
margin-top: 10px;
margin-bottom: 15px;
font-size: 16px;
line-height: 1;
}<p>AAA</p>
<p></p>
<p></p>
<p>BBB</p>AAA 和 BBB 之间的间距为 15px(取最大值)。即使中间有空 <p>、<div> 没有 border/padding,margin 也会穿透并参与折叠。
不重叠的情况:
- Flex 容器和 Grid 容器的直接子元素(它们处于不同的格式化上下文)
- 触发了 BFC 的元素(内部 margin 不穿透)
- 相邻元素之间有
border、padding、inline内容隔开 - 根元素(
<html>)的 margin 不参与
为什么 Flex 子元素不重叠?因为
display: flex会让容器进入 FFC(Flex Formatting Context),其直接子元素各自是独立的 BFC,BFC 之间不会发生 margin 折叠。
margin 负值
| 设置 | 效果 |
|---|---|
margin-top: -10px | 元素自身向上移动 |
margin-left: -10px | 元素自身向左移动 |
margin-right: -10px | 右侧元素向左移动,自身位置不变 |
margin-bottom: -10px | 下方元素向上移动,自身位置不变 |
margin-right 和 margin-bottom 的负值不影响自身——它们改变的是后续元素的排列起点。这个特性是圣杯布局和双飞翼布局能用 margin 负值实现三栏并排的关键。
BFC(Block Formatting Context)
BFC 是一块独立的渲染区域,内部元素的布局不会影响外部元素。可以理解为隔离的”结界”。
触发条件
| 条件 | 示例 |
|---|---|
float 不为 none | float: left / right |
position 为 absolute 或 fixed | position: absolute |
overflow 不为 visible | overflow: hidden / auto / scroll |
display 为 flex / grid / inline-block / table-cell / flow-root | display: flow-root —— 专门为创建 BFC 而设计的值 |
contain 为 layout / paint / strict / content | contain: layout |
display: flow-root(较新的属性)是只创建 BFC 而不引入其他副作用的方案,比overflow: hidden更语义化。
主要应用
1. 清除浮动(高度塌陷)
.container {
overflow: hidden; /* 触发 BFC,自动包含浮动的子元素 */
/* 或 display: flow-root; */
}没有 BFC 时,浮动的子元素会脱离文档流,父容器高度塌陷为 0。触发 BFC 后,容器会”感知”浮动子元素的存在并自动包裹。
2. 阻止 margin 折叠
两个相邻的块级元素如果有 margin,会折叠。把其中一个放入 BFC 容器,折叠就不再发生。
3. 自适应两栏布局
.left {
float: left;
width: 200px;
}
.right {
overflow: hidden; /* 触发 BFC,自动填充剩余空间 */
}右侧触发 BFC 后不会环绕左侧浮动元素,而是形成独立的列。
圣杯布局与双飞翼布局
目标:三栏布局,中间主内容最先渲染并自适应宽度,两侧固定宽度。两种方案都用 float + margin 负值,区别在于为两侧留白的方式。
| 方案 | 中间栏留白方式 | HTML 结构特点 |
|---|---|---|
| 圣杯 | 父容器 padding | 三栏平级,父级 padding 防止遮挡 |
| 双飞翼 | 中间栏内部多包一层 div,用 margin 留白 | 中间栏多一层包裹 |
/* clearfix —— 清除浮动的标准写法 */
.clearfix::after {
content: '';
display: table;
clear: both;
}
.clearfix {
*zoom: 1;
} /* IE6/7 兼容 */这两种布局是 PC 时代的经典方案,现在更常用 Flex 或 Grid 实现三栏布局。但在面试中,它们仍然被用来考察对 float、margin 负值和 BFC 的综合理解。
Flex 布局
容器属性
| 属性 | 作用 | 常用值 |
|---|---|---|
flex-direction | 主轴方向 | row(默认)/ column / row-reverse / column-reverse |
justify-content | 主轴对齐 | flex-start / center / flex-end / space-between / space-around / space-evenly |
align-items | 交叉轴对齐(单行) | flex-start / center / flex-end / stretch(默认)/ baseline |
align-content | 多行交叉轴对齐 | flex-start / center / space-between / stretch |
flex-wrap | 换行 | nowrap(默认)/ wrap / wrap-reverse |
gap | 间距 | 10px / 10px 20px(行 列) |
项目属性
| 属性 | 作用 |
|---|---|
flex-grow | 分配剩余空间的比例(默认 0) |
flex-shrink | 空间不足时的收缩比例(默认 1) |
flex-basis | 初始主轴尺寸(默认 auto) |
flex | 简写:flex: grow shrink basis |
align-self | 单个项目的交叉轴对齐,覆盖 align-items |
order | 排列顺序(默认 0,越小越靠前) |
三点骰子
.box {
display: flex;
justify-content: space-between; /* 1 号靠左,3 号靠右 */
}
.item:nth-child(2) {
align-self: center;
} /* 2 号垂直居中 */
.item:nth-child(3) {
align-self: flex-end;
} /* 3 号靠下 */Grid 布局
Grid 是二维布局系统,适合页面级布局,而 Flex 适合一维(行或列)的组件级排列。
.container {
display: grid;
grid-template-columns: 200px 1fr 200px; /* 三栏:侧栏固定,中间自适应 */
grid-template-rows: auto 1fr auto; /* 三行:header、内容、footer */
gap: 16px;
min-height: 100vh;
}常用属性:
| 容器属性 | 作用 |
|---|---|
grid-template-columns / rows | 定义轨道尺寸。1fr 按比例分配剩余空间 |
grid-template-areas | 命名区域布局 |
grid-auto-rows | 隐式创建的行高度 |
place-items | align-items + justify-items 简写 |
place-content | align-content + justify-content 简写 |
/* 经典圣杯布局用 Grid 实现 */
body {
display: grid;
grid-template-areas:
'header header header'
'nav main aside'
'footer footer footer';
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
}
header {
grid-area: header;
}
nav {
grid-area: nav;
}
main {
grid-area: main;
}
aside {
grid-area: aside;
}
footer {
grid-area: footer;
}定位
| 方式 | 参照物 | 脱离文档流 | 使用场景 |
|---|---|---|---|
static | 正常流(默认) | 否 | 默认定位 |
relative | 自身原始位置 | 否(保留占位) | 微调位置、为子元素 absolute 提供定位参照 |
absolute | 最近的非 static 祖先 | 是 | 弹窗、下拉菜单、定位到容器边缘 |
fixed | 视口(有 transform 祖先时例外) | 是 | 固定导航栏、悬浮按钮、遮罩层 |
sticky | 滚动容器 | 否(达阈值后固定) | 吸顶导航、表格冻结表头 |
fixed定位有个需要留意的例外:如果祖先元素有transform、perspective、filter等属性(非none),fixed的参照物会变成该祖先而非视口。这是 CSS Transforms 规范规定的。
居中对齐速查
水平居中:
inline/inline-block元素 → 父元素text-align: centerblock(定宽)→margin: 0 autoabsolute(定宽)→left: 50%; margin-left: -宽度/2flex→justify-content: centergrid→justify-items: center
垂直居中:
- 单行文本 →
line-height=height vertical-align: middle—— 仅适用于 inline/inline-block/table-cellabsolute(不定高)→top: 50%; transform: translateY(-50%)absolute(定高)→top: 50%; margin-top: -高度/2absolute(定宽高)→top: 0; right: 0; bottom: 0; left: 0; margin: autoflex→align-items: centergrid→align-items: center
水平垂直居中(一行搞定):
/* Flex */
.parent {
display: flex;
align-items: center;
justify-content: center;
}
/* Grid */
.parent {
display: grid;
place-items: center;
}
/* Absolute + transform */
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}响应式布局
单位
| 单位 | 相对 | 用途 |
|---|---|---|
px | 无(绝对) | 固定尺寸、边框、阴影 |
em | 父元素 font-size | 组件内部按比例缩放 |
rem | 根元素 <html> 的 font-size | 全局响应式 |
vw | 视口宽度的 1% | 全屏宽度 |
vh | 视口高度的 1% | 全屏高度 |
vmin | min(vw, vh) 的 1% | 保证在横屏和竖屏都不会太大 |
vmax | max(vw, vh) 的 1% | 保证覆盖最大维度 |
% | 父元素的对应属性 | 相对父级 |
line-height 的继承
body {
font-size: 20px;
line-height: 200%;
}
p {
font-size: 16px;
}- 写数字(如
1.5):子元素继承这个比例,p的line-height= 16 × 1.5 = 24px - 写百分比(如
200%):先计算(20 × 200% = 40px),子元素继承计算后的值 40px - 写长度(如
30px):子元素直接继承 30px
推荐写纯数字(如
line-height: 1.5),语义是”行高为字体大小的 1.5 倍”,子元素自动适配自己的字体大小。
两种响应式方案
方案一:media-query + rem
html {
font-size: 16px;
}
@media (max-width: 1200px) {
html {
font-size: 15px;
}
}
@media (max-width: 768px) {
html {
font-size: 14px;
}
}
@media (max-width: 480px) {
html {
font-size: 12px;
}
}优点:兼容性好,完全可控。缺点:阶梯式变化,断点之间不连续。
方案二:vw 动态缩放
1vw = 视口宽度的 1/100
优点:连续平滑,无需媒体查询。缺点:无法限制最大最小值,在超宽屏幕上字会过大,在手机上字会过小。
/* 现代方案:clamp() 同时设置最小、首选、最大值 */
.title {
font-size: clamp(16px, 4vw, 32px);
}
/* 不小于 16px,不大于 32px,之间按 4vw 缩放 */
/* 配合 calc 实现 rem 的动态基准 */
html {
font-size: clamp(14px, calc(14px + (20 - 14) * ((100vw - 375px) / (1440 - 375))), 20px);
}