这是一个用来整理CSS布局系统核心知识的手册.
跳转目录
| 概念 | 能解决的问题 |
|---|---|
box model |
判断元素为什么大小不对 |
position |
理解脱离文档流的行为(absolute/fixed) |
display |
决定元素是否是块、内联、栅格、弹性盒 |
flex/grid |
布局系统,决定空间如何分配 |
Box Model - 元素的边界定义
已经很熟悉的概念,chrome f12中的elements中有直观的画图演示。不熟悉的是其他属性对Box的影响。
你真的熟悉容器吗?
内容适应 div 宽度
img, video, iframe {
max-width: 100%;
height: auto;
}
.long-text-no-overflow {
word-break: break-word;
overflow-wrap: break-word;
}
pre {
overflow-x: auto;
max-width: 100%;
/* exactly what i did with this .md style */
}
div 宽度适应内容
.small-buttons-stuff {
display: inline-block;
}
div {
width: fit-content;
max-width: 100%;
}
/* if it's a flex box */
.flex-fit-content {
display: flex;
width: max-content;
/* or */
align-self: flex-start;
/* 后面sticky失效的元凶之一 */
}
.or-the-flex-child-goes {
flex: none;
}
Position - 不同的坐标系
position就像不同维度的坐标系统。
position 值 |
类比数学里的坐标系 | 结果 |
|---|---|---|
static(默认) |
全局坐标系(世界坐标) | 不能用 top/left 移动 |
relative |
局部坐标系 | 参照元素原本位置偏移(但不脱离文档流) |
absolute |
定点系,但基于最近的定位祖先 | 完全脱离文档流,可任意定位 |
fixed |
屏幕系(视口固定坐标) | 页面滚动时也保持原地 |
sticky |
条件固定 | 滚动到某处后“贴住”视口 |
它到底影响了什么?
首先它影响了坐标系统,决定你设置top / left / right / bottom是相对于谁的。默认static是完全不能用这些属性的。
其次它影响元素是否还在“文档流”里,static / relative 在。absolute / fixed不在,不占位,脱离正常布局。
最后,它影响z-index(图层) 没有position就不能使用z-index,设置了position(除了static),你就有资格开始叠叠乐。
Position和元素尺寸有些间接关系:
| 位置类型 | 尺寸计算方式 | 会不会影响外层容器高度 |
|---|---|---|
static |
按内容自动撑开 | 会影响 |
relative |
同上 + 视觉偏移(不影响其他元素位置) | 会影响 |
absolute |
必须显式指定或由子元素撑开 | 不会影响 |
fixed |
同上 | 不会影响 |
sticky |
和relative基本一样,但条件触发变fixed | 会影响 |
| 所以absolute/fixed的元素,尺寸不再由周围内容影响,需要手动控制。 |
常见bug和应用
absolute/fixed适用于精确控制位置(弹窗、提示框、工具栏)
relative适用于相对偏移但不打乱结构,比如按钮内文字偏左点. 对position: relative的元素设top/left,它会移动位置,但仍占原位置的空间(static就无效了)
sticky适用于滚动吸附,如头部导航固定、吸底操作栏(移动端)
| 遇到的痛点 | 可能的解法 |
|---|---|
| 宽度/高度没对齐 | position可能让它脱离流 → 它不会撑开父容器了 |
| “贴到哪里”不对 | 检查是否有最近的position: relative的祖先 |
| 鼠标被遮住 | 没有设置z-index,或外层容器层级影响它,或者被固定元素遮盖(把固定元素的鼠标事件取消) |
豆知识:position: sticky就像是“条件fixed”:
默认是relative
滚动到某个点时自动变成fixed
.sticky-header {
position: sticky;
top: 0; /* 到这里不动了 */
}
sticky not working
两个主要原因:sticky元素比parent元素高,没有空间scroll再sticky;flex或grid布局问题,默认align-items: strench;,子元素适应parent高度,无空间以scroll(对sticky加align-self: flex-start; (or center, flex-end, etc., to create space) 。
Display
核心问题:一个元素的显示方式是块状、内联,还是栅格 / 弹性布局容器?它能不能包住子元素?子元素的布局规则是什么?
| 值 | 作用 | 典型用途 |
|---|---|---|
block |
占据整行,可设宽高,自动换行 | <div>, <section>, <p>, <h1> |
inline |
行内显示,不能设宽高,不换行 | <span>、文本装饰等 |
inline-block |
行内排布,但可设宽高,不换行 | 小按钮、icon等 |
flex |
变成弹性布局容器,子元素可弹性分配空间 | 卡片容器、导航栏、对齐等 |
grid |
变成网格布局容器,子元素可占格子 | 复杂的界面网格,比如画廊、控制面板 |
none |
不显示,且从文档流中移除 | 暂时隐藏元素(但注意不同于 visibility: hidden,none的元素不占据位置,hidden的元素还会占据位置) |
Quick Checklist
- [x] content(width 默认只计算 content 部分),
box-sizing默认是content-box,要手动设为border-boxwidth才会包含padding和border - [x] inline元素不支持设置
widthheight(需要转为inline-block才生效) - [x] margin collapse发生在垂直方向的
block元素之间,当两个上下相邻的 block 元素之间都有margin-top/margin-bottom,它们不会“加起来”,而是合并成一个更大的值。
排查手册
为什么设置了width没效果?:是不是display: inline?
为什么两个按钮换行了?:可能是display: block或默认布局规则
为什么排不齐?:是不是需要改成flex或grid?
Flex
用一句话总结:flex 是“空间分配器”。它能解决的是一类问题:“几个元素横着(或竖着)排,怎么让它们刚好排好、对齐、拉伸、靠边、等分、缩放?”
结构
在 Flex 世界里,有两个层次:
外层容器(flex container)加 display: flex
子元素(flex items)自动变成“能被分配空间的对象”
一个 flex 容器可以控制两个方向:
| 方向 | 属性名 | 控制啥 |
|---|---|---|
| 主轴(横or竖) | flex-directionjustify-content |
控制子元素如何沿主轴排列 |
| 交叉轴 | align-itemsalign-self |
控制子元素在副轴上的对齐 |
主轴默认为横向:row,所以justify控横,align控竖。
flex:
- flex-grow:有剩余空间时,谁多长一些?
flex-grow: 0; /* 不长 */
flex-grow: 1; /*分到等量空间*/
flex-grow: 2; /* 是别人的两倍 */
- flex-shrink:空间不够时,谁先变小?
flex-shrink: 0; /* no shrinking */
flex-shrink:1; /* shrink according to scale */
flex-shrink: 2; /* can shrink more */
- flex-basis:原始大小
flex-basis: 200px; /* require at least 200px width */
flex-basis会覆盖width
所以flex: 1是flex: 1 1 0%,等比例长大缩小,从0开始分配剩余空间
几个常见问题的flex解法模型
| 想要的效果 | 设置 |
|---|---|
| 全部等宽 | flex: 1 |
| 固定左侧 + 自适右侧 | .left {flex: 0 0 100px}.right {flex: 1} |
| 不让缩小 | flex-shrink: 0 |
| 固定宽度优先,空间大了再变大 | flex: 1 1 200px |
flex后记
我喜欢用百分比宽高,因为比较直观,而且看起来很响应(。。。)但百分比在flex里经常失效,变成内容实际高。因为%依赖parent的确定高度,如果想要百分比的固定高度,改成vh,因为这实际上是视窗布局而非parent比例布局。
Grid
与flex不同,grid有横竖两个主轴。
grid-template-row/column控制几行/几列;gap控制每个格子间距。
可以自定义比例:
grid-template-columns: 1fr 2fr 1fr;
grid-template-columns: 100px auto 200px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); /* auto fit */
子项跨格:
grid-column: 1 / 3; /* takes up col 1-3 (3rd excluded) */
grid-area: 2 / 1 / 4 / 3; /* row-start / column-start / row-end / column-end */