打印样式表实战:让文章打出来还能用

几乎每个内容站都有一个被忽略的出口:打印。用户按下 Ctrl+P 之后,看到的是导航栏、侧边栏、广告位和分享按钮一起被塞进纸上,正文被挤到第二页,代码块横向被裁掉一半,链接地址完全丢失。这类页面在"我要把这篇教程打出来照着做"的场景下基本不可用。

而修它的成本非常低——一张打印样式表,一两百行 CSS,不需要改任何模板结构。这篇文章讲清楚打印样式该写什么、哪些写法在浏览器里真的生效、以及怎么调试(这部分比写更麻烦)。

一、打印样式到底解决什么问题

先说清楚目标,不然容易写成"把屏幕样式缩小一点"这种没用功。一个好的打印版应该做到四件事:

一、去掉所有非内容元素。导航、侧栏、相关推荐、评论、广告、浮动按钮、返回顶部,一个都不该出现在纸上。纸只有 A4 那么大,每一平方厘米都应该给内容。

二、保住内容的完整性。代码块不能被横向裁断,宽表格要能放下,长 URL 要能换行而不是溢出到纸外。

三、补上纸上才需要的信息。屏幕上链接点一下就到,纸上必须把 URL 写出来;屏幕上有站点 logo,纸上需要明确的站点名和文章标题。

四、控制分页。不要把小标题留在页底、正文翻到下一页;不要把一个代码块从中间劈开。

这四件事都做到了,打印版就是可用的。

二、三种接入方式,选一种就够

最标准的方式是独立的打印样式表:

<link rel="stylesheet" href="/css/print.css" media="print">

给 link 加 media="print",浏览器只在打印时下载和应用它,对屏幕渲染零影响,也不占首屏带宽(部分浏览器会延迟到打印时才请求)。这是首选。

第二种是写在主样式表里的 @media print 块:

@media print {
  .sidebar, .navbar, .comments { display: none; }
}

好处是不增加请求,坏处是主样式表会变大一点点,而且和屏幕样式混在一起不好维护。个人站如果打印样式不多(五十行以内),直接写在主表末尾完全可以。

第三种是 media="all" 加 @media print 内联在模板 head 里。不推荐,因为它会拖慢每个页面的首屏,而打印是低频行为。

有一个历史写法要避开:<link media="print" onload="this.media='all'">。这是早年用来异步加载 CSS 的 hack,用在打印样式上会导致打印样式在所有屏幕上生效,页面直接崩掉。这个写法现在还能在不少老主题里看到,见到就删。

三、隐藏:从最外层开始砍

打印样式最有效的一步就是 display:none,但关键是砍对层级。新手常犯的错是逐个去隐藏侧栏里的每个模块,写了三十行;正确做法是找到布局容器,一刀砍掉。

@media print {
  header nav, .navbar, .sidebar, .widget-area,
  .comments, .related-posts, .share-bar, .breadcrumb,
  .adsbygoogle, ins.adsbygoogle, .back-to-top,
  .pagination, .post-nav, footer .social { display: none !important; }

  .container, .main, .content { width: auto !important; margin: 0 !important; padding: 0 !important; float: none !important; }
}

三个要点:

第一,用 !important 是合理的。平时写 CSS 我很反对滥用 !important,但打印样式是唯一例外——它要覆盖屏幕样式里大量可能存在的 display:flex、position:fixed,靠优先级去拼会非常痛苦。打印表就当作最终覆盖层,统一加。

第二,必须重置宽度和浮动。很多主题的正文是 width: 70%; float: left,侧栏隐藏后正文仍然只占七成宽,右边空一大块。所以容器要显式写 width: auto; float: none。

第三,别忘了 ins.adsbygoogle。AdSense 的容器在打印时如果不隐藏,会留下一块空白,有时还会触发一次无效展示计费。这个类名要单独写进选择器。

四、用 pt 而不是 px,用黑白而不是彩色

打印的尺寸单位和屏幕不同。屏幕上 16px 是标准正文,但纸上的 px 会被换算,结果往往偏小。打印样式里正文用 10.5pt 到 12pt(约等于 Word 的小四到五号),标题用 16pt 到 22pt,行高 1.5。这样的字号在 A4 上读起来最舒服,也最省纸。

@media print {
  body { font: 11pt/1.5 "Times New Roman", serif; color: #000; background: #fff; }
  h1 { font-size: 20pt; margin: 0 0 8pt; }
  h2 { font-size: 15pt; margin: 14pt 0 6pt; }
  h3 { font-size: 12.5pt; margin: 12pt 0 5pt; }
  p, li { orphans: 3; widows: 3; }
}

字体用衬线体(Times、宋体)在纸上比无衬线体省墨、易读,这是排版常识。颜色一律黑色:彩色打印一篇长文非常费墨,而绝大多数打印机默认黑白,彩色在灰度化之后常常变成一片看不清的浅灰。尤其要注意不要依赖颜色传达信息——屏幕上的红色警告在黑白打印后可能只是浅灰,所以警告文字本身要写"注意:"而不是靠红字。

背景色和背景图要显式关掉,否则有些浏览器即使关闭了"打印背景"选项,元素背景仍可能带出来:

@media print {
  * { background: transparent !important; box-shadow: none !important; text-shadow: none !important; }
}

五、代码块和表格:最容易出问题的地方

技术教程打印出来最常坏的两种元素就是代码块和宽表格。

代码块的问题有两个。一是被横向裁断:屏幕上 overflow-x: auto 可以滚动,纸上滚不了,超出的部分直接被截掉看不见,而且不会有任何提示。二是深色背景把整块纸涂黑,费墨且看不清。

@media print {
  pre, code {
    background: #f5f5f5 !important;
    color: #000 !important;
    border: 1px solid #ccc;
    font-family: "Courier New", monospace;
    font-size: 9pt;
  }
  pre {
    white-space: pre-wrap !important;
    word-wrap: break-word;
    overflow: visible !important;
    page-break-inside: avoid;
  }
}

white-space: pre-wrap 是关键:它保留缩进(这是代码可读性的命脉),同时允许长行折行,不会横向溢出。page-break-inside: avoid 让整个代码块尽量不跨页——不过要提醒一句,这个属性对高度超过一页的块是无效的,浏览器必然会切开它,所以别指望它解决所有问题,长的代码块该拆就拆。

表格的问题是宽度。宽表格在 A4 竖版上几乎必然溢出。两个办法:一是减小字号到 8pt,二是如果有条件,把表格设为横版页面。CSS 里可以用命名页做到:

@page landscape { size: A4 landscape; }
.wide-table { page: landscape; }
.wide-table table { width: 100%; font-size: 8.5pt; border-collapse: collapse; }
.wide-table td, .wide-table th { border: 1px solid #999; padding: 3pt 4pt; }

@page 命名页的浏览器支持在近几年才稳定,Chrome 与 Firefox 可用,Safari 上表现不一致。用在关键内容上要先测。更稳妥的兜底是给表格套一个容器,打印时允许它 overflow: visible 并缩小字号,接受拥挤但完整的排版。

六、分页控制:让纸上的阅读顺序不被打断

分页控制是打印样式的第二大类。核心是三个属性,注意新旧语法都要写(旧语法兼容老浏览器,新语法是标准):

@media print {
  h1, h2, h3, h4 { page-break-after: avoid; break-after: avoid; }
  h1, h2, h3 { page-break-inside: avoid; break-inside: avoid; }
  pre, table, figure, blockquote { page-break-inside: avoid; break-inside: avoid; }
  img { page-break-inside: avoid; break-inside: avoid; max-width: 100%; }
  .page-before { page-break-before: always; break-before: page; }
}

page-break-after: avoid 加在标题上,意思是"这个标题后面不要分页",效果是标题不会孤零零留在页底——这是打印排版里最常见也最难看的问题,一条规则就能解决。

orphans 和 widows 控制段落的最小行数:orphans 是段落开头至少要留在页底的行数,widows 是段落结尾至少要带到下一页的行数,都设成 3 就不再出现"一段话只有一行翻到下一页"的窘况。

如果确定某篇长文要在某个位置切页,可以在模板里放一个 <div class="page-before"></div>,不过个人站一般不需要这么细的控制。

七、把 URL 印出来:screen-only 的反向技巧

纸上没法点链接,所以必须把地址写出来。标准写法是用伪元素:

@media print {
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #555;
    word-break: break-all;
  }
  a[href^="#"]::after, a[href^="javascript"]::after { content: ""; }
  .no-print-url a::after { content: ""; }
}

三个细节:

第一,a[href^="http"] 只选外链,站内相对链接不印——站内链接印出来是"/tag/nginx/",没有意义还占地方。如果站点用的是绝对 URL 的内链,那就再加一条排除规则。

第二,word-break: break-all 必须有。长 URL 不会自动换行,会直接溢出到纸外,被裁掉一半。

第三,导航区已经 display:none 了,所以那里的链接不会被印出来,不用额外处理;但如果正文里有一段密集的链接列表(比如资源汇总),印出来会非常啰嗦,给那个容器加个 class 排除掉。

反过来,也有"只打印需要看的东西"的用法:如果正文里嵌了可交互的演示、视频播放器、地图,打印时应该用一个静态替代说明代替它们,比如 .video-embed::after { content: "(视频见网页版)"; },不然纸上会留下一块空白框。

八、页眉页脚:用 @page 而不是手写

打印时的页眉页脚(页码、URL、日期)由浏览器在打印对话框里控制,默认会带上 URL 和日期,这两个常常又长又难看。你没法通过 CSS 强制关掉它们,但可以用 @page 设置页边距,并通过 @page 的 margin box 加自己的内容(支持有限):

@page {
  size: A4;
  margin: 18mm 16mm 16mm;
}
@page {
  @bottom-center { content: counter(page) " / " counter(pages); font-size: 9pt; color: #666; }
}

margin box 的支持度一般(Chrome 基本不支持自定义 margin box,Firefox 部分支持),所以实用建议是:不要过度设计页眉页脚,把页边距设好(上下 16~18mm,左右 15~16mm,留给装订和打印机不可打印区),页码交给浏览器,然后在文章开头用一个只在打印时显示的块把站点名和标题写清楚:

<div class="print-header">zz1984.com · 一个草根站长的博客</div>

@media screen { .print-header { display: none; } }
@media print  { .print-header { display: block; font-size: 9pt; color: #666; border-bottom: 1px solid #ccc; padding-bottom: 4pt; margin-bottom: 10pt; } }

"只在屏幕上显示"的元素也同理反向处理,比如"本文链接"之类的分享按钮,屏幕上显示、打印时隐藏。

九、怎么调试打印样式(这步最难)

打印样式的调试之所以麻烦,是因为你不能像平时那样边改边看。三个可用手段:

手段一:Chrome DevTools 的打印模拟。打开开发者工具,按 Ctrl+Shift+P 打开命令面板,输入 "Rendering",在 Rendering 面板里把 "Emulate CSS media type" 切到 print。这时页面会按打印样式渲染,而且能正常审查元素、改 CSS 看效果。这是最常用的方式,缺点是它不模拟分页——你看不到哪里会被分页切断。

手段二:真的打印成 PDF。Ctrl+P,目标选"另存为 PDF",勾选"背景图形"和不勾选各试一次。这一步能看到真实分页,是唯一能验证分页规则的办法。建议在打印样式开发阶段固定用同一个浏览器出 PDF 做对比。

手段三:用 headless 浏览器批量出 PDF。如果你有几十篇文章想批量检查,可以写脚本:

for u in $(cat urls.txt); do
  chromium --headless --disable-gpu --no-pdf-header-footer --print-to-pdf=/tmp/mock.pdf "$u"
done

批量出 PDF 之后,重点看三类文章:含长代码块的、含宽表格的、图片多的。这三类覆盖了九成的打印问题。

还有一个特别容易漏的点:打印样式也会影响 PDF 导出功能。很多站点提供"下载 PDF"按钮,底层就是调浏览器打印。所以把打印样式做好,等于免费把 PDF 导出也做好了。

十、一张可以直接用的最小打印样式表

最后给一份可以直接抄的完整版本,覆盖前面所有要点,五十行左右:

@media print {
  * { background: transparent !important; box-shadow: none !important; text-shadow: none !important; }
  header nav, .navbar, .sidebar, .widget-area, .comments, .related-posts,
  .share-bar, .breadcrumb, ins.adsbygoogle, .pagination, .back-to-top,
  .post-nav, footer { display: none !important; }
  .container, .main, .content { width: auto !important; margin: 0 !important; padding: 0 !important; float: none !important; }
  body { font: 11pt/1.5 "Times New Roman", serif; color: #000; }
  h1 { font-size: 20pt; margin: 0 0 8pt; }
  h2 { font-size: 15pt; margin: 14pt 0 6pt; page-break-after: avoid; break-after: avoid; }
  h3 { font-size: 12.5pt; margin: 12pt 0 5pt; page-break-after: avoid; break-after: avoid; }
  p, li { orphans: 3; widows: 3; }
  pre, code { background: #f5f5f5 !important; color: #000 !important;
              border: 1px solid #ccc; font: 9pt "Courier New", monospace; }
  pre { white-space: pre-wrap !important; overflow: visible !important;
        page-break-inside: avoid; break-inside: avoid; }
  table { width: 100%; font-size: 8.5pt; border-collapse: collapse; page-break-inside: avoid; }
  td, th { border: 1px solid #999; padding: 3pt 4pt; }
  img { max-width: 100% !important; height: auto !important; page-break-inside: avoid; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #555; word-break: break-all; }
  a[href^="#"]::after { content: ""; }
  .print-header { display: block; font-size: 9pt; color: #666;
                  border-bottom: 1px solid #ccc; padding-bottom: 4pt; margin-bottom: 10pt; }
  @page { size: A4; margin: 18mm 16mm 16mm; }
}

把它存成 print.css,在模板 head 里加一行 media="print" 的 link,然后挑三篇典型文章出 PDF 看一遍,基本就完事了。整个工作量不到半小时,但从此以后每一个想把你文章打出来的人都会感谢你——尤其当教程类内容被打印出来照着操作的时候。

Last modification:October 27th, 2026 at 07:26 pm

Leave a Comment