学完这篇,你能用 CSS 精确控制打印时哪里必须换页、哪里绝不能被切断,以及纸张尺寸、页边距、页码怎么设置,不再靠"打出来看看再调"。
第一步:先分清两套属性名
分页控制有两代属性,老的那套叫 page-break-*,新的叫 break-*。它们做的是同一件事,只是新名字把适用范围从"页"扩展到了"栏"。
| 老属性 | 新属性 | 作用 |
|---|
page-break-before | break-before | 元素之前分页 |
page-break-after | break-after | 元素之后分页 |
page-break-inside | break-inside | 元素内部是否允许断开 |
新手最省事的做法是两个都写,一行里并排:
.chapter {
page-break-before: always;
break-before: page;
}
注意:新属性的取值是 page,不是 always。写成 break-before: always 是无效的,排查半天找不到原因多半就是这个。
第二步:强制分页,让每章从新页开始
最常见的需求是"每章另起一页"。给章节标题加一条规则即可:
@media print {
h2.chapter {
page-break-before: always;
break-before: page;
}
}
如果是想在某段内容之后换页(比如封面页),用 page-break-after: always,并且记得给封面页一个独立 class,不要给通用元素加,否则每页都断。
还有一个常被忽略的细节:给最后一个元素加 page-break-after: always 会产生一张空白页。解决办法是加一条 :last-child { page-break-after: auto; }。
第三步:防止内容被切断
这是打印排版里最影响观感的一项。段落被切成两半、图片只剩上半截、代码块断在中间——全都靠 break-inside 解决:
@media print {
p, li, blockquote, pre, img, figure, tr {
page-break-inside: avoid;
break-inside: avoid;
}
}
注意:break-inside: avoid 对 flex 容器和 grid 容器的子元素无效。如果你用 flex 做卡片布局,卡片照样会被切成两半。打印样式中要把这类容器改回 display: block,或者改用 float/表格布局。
@media print {
.card-list { display: block !important; }
.card { display: block; margin-bottom: 12px; }
}
第四步:表格跨页时重复表头
长表格翻页后表头消失,是打印文档的经典痛点。浏览器原生支持,只要结构写对:
<table>
<thead><tr><th>项目</th><th>金额</th></tr></thead>
<tbody>...</tbody>
</table>
@media print {
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr { page-break-inside: avoid; }
}
thead 默认就是 table-header-group,但很多 CSS reset 会把它改成 block,所以显式写回来更保险。
第五步:用 @page 控制纸张与页边距
@page 是唯一能设置纸张大小和页边距的地方,注意它写在 @media print 外面(写里面也生效,但没必要嵌套):
@page {
size: A4 portrait; /* 或 A4 landscape */
margin: 18mm 16mm;
}
@page :first {
margin-top: 40mm; /* 封面页单独设置 */
}
size 还支持 size: 210mm 297mm 这种自定义尺寸。Chrome、Edge、Safari 都支持 size 和 margin。
不过,@top-center、@bottom-right 这类页边距盒子(用来放页眉页脚和页码)浏览器目前不支持,只有 WeasyPrint、Prince、Paged.js 这类排版引擎认。想在浏览器打印里加页码,只能靠浏览器自带的"页眉页脚"勾选项,或者用 Paged.js 在前端做分页渲染。
/* 只在 WeasyPrint / Prince 这类引擎里有效 */
@page {
@bottom-center { content: counter(page) " / " counter(pages); }
}
注意:不同浏览器对 size 的响应不同,用户如果在打印对话框里手动选了纸张,CSS 的 size 会被覆盖。要真正稳定,建议在页面上提示用户"请选择 A4、边距设为无"。
第六步:控制孤行与寡行
一段文字被拆开时,最后一行单独落在下一页叫"寡行",第一行单独留在上一页叫"孤行"。用 orphans 和 widows 限制最少行数:
@media print {
p {
orphans: 3; /* 页尾至少留 3 行 */
widows: 3; /* 页首至少留 3 行 */
}
}
这两个属性在 Chrome 里支持一般,Firefox 支持较好。如果对排版要求高,配合 break-inside: avoid 用在小段落上更可靠。
第七步:调试方法
调打印样式别靠反复真打,用浏览器自带的模拟:
- Chrome 按
F12 → Ctrl+Shift+P → 输入 Rendering → 勾选 Emulate CSS media type: print
- 或者直接在打印预览(
Ctrl+P)里看,预览就是最终分页结果,比模拟更准
- 排查元素被切断时,在打印预览里给可疑元素临时加
outline: 1px solid red,能看清实际盒子边界
注意:position: absolute 和 position: fixed 的元素在分页时行为很怪,可能只出现在第一页或者跑到内容外面。打印样式里把侧边栏、悬浮按钮一律 display: none,别指望它们能正确定位。
小结
- 分页三件套:
break-before / break-after / break-inside,老写法 page-break-* 一起写上兼容性最好
- 强制分页的值是
page(新)和 always(老),别混
- 防切断首选
break-inside: avoid,但 flex/grid 子元素不吃这套,打印时改回 block 布局
- 表格跨页重复表头靠
thead { display: table-header-group } 加上正确的 HTML 结构
- 纸张和页边距只认
@page,页边距盒子(页码页眉)浏览器不支持,需要排版引擎
- 用打印预览当唯一真相,模拟媒体查询只能辅助定位