2018 年打印样式表状态指南
已发表: 2022-03-10今天,我想回到过去在 Smashing Magazine 中已经涵盖的主题——打印样式表的主题。 在这种情况下,我说的是直接从浏览器打印页面。 这种体验可能会导致打印出大量图像(甚至是广告)而感到沮丧。 但是,有时,当使用最少的墨水和纸张从打印机打印出经过优化的页面并确保所有内容都易于阅读时,它会增加一点乐趣。
本文将探讨我们如何才能最好地创造第二种体验。 我们将看看我们应该如何在我们的网页中包含打印样式,并看看在打印后真正进入它们自己的规范。 我们将了解浏览器支持的状态,以及如何最好地测试我们的打印样式。 然后,我将为您提供一些指示,说明当打印样式表不足以满足您的打印需求时该怎么做。
打印支持的关键场所
如果您还没有在您的网站上实施任何打印样式,那么在几个关键的地方,可靠的打印体验将对您的用户有所帮助。 例如,即使您将通过电子邮件发送详细信息,许多用户仍希望在购买或预订后打印交易确认页面。
您的访问者在离开计算机时可能想要使用的任何信息也是打印样式表的良好候选者。 我打印的最常见的东西是食谱。 我可以将它们加载到我的 iPad 上,但通常更方便的是在我烹饪时简单地打印食谱以弹出到冰箱门上。 其他此类示例可能是方向或旅行信息。 当出国旅行时,并不总是能够访问数据,这些打印输出可能非常宝贵。
任何种类的参考资料也经常被印刷。 对于许多人来说,能够在纸质副本上做笔记是他们最好的学习方式。 同样,这意味着信息可以离线格式访问。 我们很容易想知道人们为什么要打印网页,然而,我们的工作通常是让内容易于访问——以对我们的访问者最好的格式。 如果最好的格式打印在纸上,那么我们该争论谁呢?
为什么要打印此页?
在决定要在打印样式表中包含或隐藏的内容时要问的一个好问题是:“用户为什么要打印此页面?” 好吧,也许他们在厨房做饭时想遵循一个食谱,或者在购物购买食材时随身携带。 或者他们想在购买机票后打印确认页面作为预订证明。 或者他们可能希望打印收据或发票(或打印为 PDF),以便将其以纸质或电子方式存储在帐户中。
考虑使用打印文档可以帮助您生成内容的打印版本,该版本在用户引用该打印输出时所处的上下文中最有用。
工作流程
一旦我们决定在我们的 CSS 中包含打印样式,我们需要将它们添加到我们的工作流程中,以确保当我们对布局进行更改时,我们也会在打印版本中包含这些更改。
将打印样式添加到页面
为了启用“打印样式表”,我们正在做的是告诉浏览器这些 CSS 规则在文档打印时的用途。 这样做的一种方法是使用<link>
元素链接附加样式表。
<link rel="stylesheet" media="print" href="print.css">
这种方法确实使您的打印样式与您可能认为更整洁的所有其他内容分开,但是,它有缺点。
链接的样式表会向服务器创建一个附加请求。 此外,打印样式与其他样式的漂亮、整洁的分离可能有一个缺点。 虽然您可能会在上线之前注意更新单独的样式,但样式表可能会因为看不见而变得心烦意乱——最终变得无用,因为功能被添加到网站但没有反映在打印样式中。
包含打印样式的另一种方法是使用@media
,就像在响应式设计中为某些断点包含 CSS 一样。 此方法将所有 CSS 保存在一起以形成一个功能。 窄到宽断点的样式和打印样式。 除了使用@supports
进行功能查询外,这还鼓励一种开发方式,以确保设计功能的所有 CSS 都被保存和维护在一起。
@media print { }
覆盖屏幕 CSS 或创建单独的规则
很多时候,您可能会发现用于屏幕显示的 CSS 只需进行一些小的调整即可用于打印。 因此,您只需要编写用于打印的 CSS,以更改该基本 CSS。 您可能会覆盖字体大小或字体系列,而只保留 CSS 中的其他元素。
如果您真的想拥有完全独立的打印样式并从空白开始,那么您需要使用 screen 关键字将其余站点样式包装在 Media Query 中。
@media screen { }
需要注意的是,如果您在响应式设计中使用媒体查询,那么您可能已经为屏幕编写了它们。
@media screen and (min-width: 500px) { }
如果您希望在打印时使用这些样式,则应删除screen
关键字。 然而,在实践中,我经常发现如果我“移动优先”,单列移动布局是我打印布局的一个非常好的起点。 通过让媒体查询只为屏幕带来更复杂的布局,我对打印样式的覆盖要少得多。
将您的打印样式添加到您的图案库和样式指南
为了帮助确保您的打印样式被视为网站设计的一个组成部分,请将它们添加到您的网站样式指南或模式库中(如果有的话)。 这样,总是会提醒您存在打印样式,并且创建的任何新模式都需要具有等效的打印版本。 通过这种方式,您将打印样式作为设计系统的一等公民提供可见性。
用于打印的 CSS 基础知识
在创建用于打印的 CSS 时,您可能会发现自己在做三件事。 您将希望隐藏而不是显示打印时不相关的内容。 您可能还想添加内容以使打印版本更有用。 您可能还想调整页面的字体或其他元素以优化它们以进行打印。 让我们来看看这些技术。
隐藏内容
在 CSS 中,隐藏内容并防止生成框的方法是使用值为none
的 display 属性。
.box { display: none; }
使用display: none
将折叠元素及其所有子元素。 因此,如果您将图像库标记为列表,则在打印时隐藏它所需要做的就是在ul
上设置display: none
。
您可能想要隐藏的内容是打印时不需要的图像、导航、广告面板和显示相关内容链接的页面区域等。 回顾用户可能打印页面的原因可以帮助您决定要删除的内容。
插入内容
打印页面时可能会显示一些有意义的内容。 您可以将一些内容设置为display: none
并将其显示在您的打印样式表中。 但是,此外,您可以使用 CSS 将通常不输出到屏幕的内容公开。 文档中链接的 URL 就是一个很好的例子。 在您的屏幕文档中,链接通常会显示链接文本,然后可以单击该链接文本来访问该新页面或外部网站。 但是,当无法访问打印的链接时,如果读者可以看到 URL,以防他们希望稍后访问该链接,这可能会很有用。
我们通过使用 CSS 生成的内容来实现这一点。 生成的内容为您提供了一种通过 CSS 将内容插入文档的方法。 打印时,这变得非常有用。
您可以在文档中插入一个简单的文本字符串。 下一个示例使用wrapper
类来定位元素,并在其前面插入字符串“请参阅 www.mysite.com 以获取此信息的最新版本。”
.wrapper::after { content: "Please see www.mysite.com for the latest version of this information."; }
您可以插入文档中已经存在的内容,例如链接href
的内容。 我们在每个带有href
属性的a
实例之后添加 Generated Content,我们插入的内容是href
属性的值 - 这将是链接。
a[href]:after { content: " (" attr(href) ")"; }
如果您愿意,可以使用较新的 CSS :not
选择器来排除内部链接。
a[href^="http"]:not([href*="example.com"]):after { content: " (" attr(href) ")"; }
在 Manuel Matuzovic 撰写的文章“我完全忘记了打印样式表”中还有一些其他有用的技巧。
高级打印样式
如果您的打印版本整齐地放在一个页面上,那么您应该能够通过使用上一节的技术相对简单地创建打印样式表。 但是,一旦您的某些内容可以打印到多个页面上(特别是如果它包含表格或图形等元素),您可能会发现项目以次优方式进入新页面。 您可能还想控制有关页面本身的事情,例如更改页边距大小。
CSS 确实有办法做这些事情,但是,正如我们将看到的,浏览器的支持是不完整的。
分页媒体
CSS Paged Media Specification 将打开,其角色描述如下。
“这个 CSS 模块指定了页面是如何生成和布局的,以在分页演示中保存碎片内容。 它添加了用于控制页边距、页面大小和方向以及页眉和页脚的功能,并扩展了生成的内容以启用页码和运行页眉/页脚。”
屏幕是连续媒体; 如果有更多内容,我们滚动查看。 没有将其分解为单个页面的概念。 一旦我们打印,我们就会输出到一个固定大小的页面,在规范中描述为分页媒体。 Paged Media 规范不处理页面之间的内容是如何分段的,我们稍后会讨论。 相反,它着眼于页面本身的功能。
我们需要一种方法来定位单个页面,我们通过使用@page
规则来做到这一点。 这很像常规选择器,因为我们以@page
为目标,然后编写 CSS 以供页面使用。 一个简单的示例是更改打印文档时创建的所有页面的边距。
@page { margin: 20px; }
您可以使用:left
和:right
传播伪类选择器来定位特定页面。 可以使用:first
伪类选择器定位第一页,并且可以使用:blank
选择由分页符引起的空白页。 例如,仅在第一页设置上边距:
@page :first { margin-top: 250pt; }
要在左侧页面的右侧和右侧页面的左侧设置更大的边距:
@page :left { margin-right: 200pt; } @page :right { margin-left: 200pt; }
该规范定义了能够将内容插入到创建的边距中,但是,似乎没有浏览器支持此功能。 我在关于创建用于打印特定用户代理的样式表的文章“使用 CSS 进行打印设计”中对此进行了描述。
CSS 碎片化
Paged Media 模块处理页面框本身,CSS Fragmentation Module 详细说明了内容如何在fragmentainers之间中断。 片段容器(或片段容器)是包含片段流的一部分的容器。 这是一个流,当它到达溢出的点时,会中断到一个新容器中。
当前,您将遇到碎片的上下文是在分页媒体中,因此在打印时,以及在使用多列布局时,您的内容会在列框之间中断。 Fragmentation 规范定义了各种破坏规则,CSS 属性使您可以在这些上下文中控制内容如何分解为新片段。 它还定义了 CSS 区域规范中内容的中断方式,尽管目前这不是跨浏览器可用的东西。
而且,说到浏览器,目前在支持方面,碎片化有点混乱。 MDN 上每个属性的浏览器兼容性表似乎准确地支持,但是需要仔细测试这些属性的使用。
来自 CSS2 的旧属性
除了 CSS Fragmentation Level 3 中的break-*
属性外,我们还有来自 CSS2 的page-break-*
属性。 在规范方面,这些已被较新的break-*
属性所取代,因为它们更通用,可以在发生中断的不同上下文中使用。 一页和多列中断之间没有太大区别。 但是,在浏览器支持方面,较旧的属性有更好的浏览器支持。 这意味着您可能需要在当前时间使用它们来控制中断。 实现较新属性的浏览器将使用旧属性的别名而不是删除它们。
在下面的示例中,我将同时展示新属性和旧属性存在的位置。
break-before
break-after
这些属性处理框之间的中断,并接受以下值,初始值为 auto。 最后四个值不适用于分页媒体,而是适用于多列和区域。
-
auto
-
avoid
-
avoid-page
-
page
-
left
-
right
-
recto
-
verso
-
avoid-column
-
column
-
avoid-region
-
region
page-break-before
和page-break-after
的旧属性接受较小范围的值。
-
auto
-
always
-
avoid
-
left
-
right
-
inherit
要始终在h2
元素之前导致分页符,您可以使用以下内容:
h2 { break-before: page; }
为避免段落与紧接在其前面的标题分离:
h2, h3 { break-after: avoid-page; }
旧的page-break-*
属性总是在h2
之前导致分页:
h2 { page-break-before: always; }
为避免段落与紧接在其前面的标题分离:
h2, h3{ page-break-after: avoid; }
在 MDN 上找到属性的信息和使用示例:
- 休息前
- 休息后
- 分页前
- 分页后
break-inside
此属性控制框内的中断并接受值:
-
auto
-
avoid
-
avoid-page
-
avoid-column
-
avoid-region
与前两个属性一样,CSS2 有一个别名page-break-inside
,它接受以下值:
-
auto
-
avoid
-
inherit
例如,也许您有一个figure
或table
,但您不希望其中一半出现在一个页面上,而另一半出现在另一页上。
figure { break-inside: avoid; }
并且在使用旧属性时:
figure { page-break-inside: avoid; }
在 MDN 上:
- 闯入
- 分页符
孤儿寡妇
Fragmentation 规范还定义了orphans
和widows
属性。 orphans
属性定义当段落等内容在两页之间中断时,第一页底部可以留下多少行。 widows
属性定义了在第二页顶部可以留下多少行。
因此,为了防止在页面末尾出现单行并在下一页顶部出现单行,您可以使用以下内容:
p { orphans: 2; widows: 2; }
widows
和orphans
属性得到很好的支持(缺少的浏览器实现是 Firefox)。
在 MDN 上:
- 寡妇
- 孤儿
box-decoration-break
Fragmentation 模块中定义的最后一个属性是box-decoration-break
。 此属性处理边框、边距和填充是否中断或包裹内容。 它接受的值是:
-
slice
-
clone
例如,如果我的内容区域有一个 10 像素的灰色边框并且我打印内容,那么打印的默认方式是边框将继续到每一页上,但是,它只会在内容的末尾换行. 所以我们在进入下一页并继续之前休息一下。
如果我使用box-decoration-break: clone
,每个页面的边框和任何填充和边距都将完成,从而为每个页面提供灰色边框。
目前,这仅适用于 Firefox 中的 Paged Media,您可以在 MDN 上找到有关 box-decoration-break 的更多信息。
浏览器支持
如前所述,浏览器对 Paged Media 和 Fragmentation 的支持不完整。 就碎片化而言,另一个问题是必须为每种布局方法指定和实现中断。 如果您希望在打印样式表中使用 Flexbox 或 CSS Grid,您可能会感到失望。 您可以查看 Flexbox 和 Grid 的 Chrome 错误。
我现在能给出的最好的建议是保持你的打印样式表相当简单。 添加碎片属性——包括旧的page-break-*
属性和新属性。 但是,请接受这些可能并不适用于所有浏览器。 而且,如果您发现缺乏浏览器支持令人沮丧,请向浏览器提出这些问题或为已经提出的问题投票。 尤其是碎片化,应该被视为建议而不是命令,即使它得到支持。 可能会非常具体地说明您希望在何时何地破坏事情,以至于几乎不可能布置页面。 你应该假设有时你可能会得到次优的破坏。
测试打印样式表
测试打印样式表可能有点无聊,通常需要使用打印预览或重复打印到 PDF。 然而,浏览器 DevTools 让我们更容易做到这一点。 Chrome 和 Firefox 都可以只查看打印样式。
火狐
打开开发人员工具栏,然后在提示符下键入media emulate print
。
铬合金
打开 DevTools,点击三点图标,然后选择“更多工具”和“渲染”。 然后,您可以在 Emulate CSS Media 下选择打印。
这只会有助于测试对 CSS 布局、隐藏或生成的内容的更改。 它无法帮助您解决碎片化问题——您需要为此打印或打印为 PDF。 但是,它将为您节省一些往返打印机的次数,并且可以帮助您在开发网站的新部分时检查您仍然隐藏并显示正确的内容。
当打印样式表不够用时该怎么办
在理想情况下,当直接从浏览器打印时,浏览器会实现更多的分页媒体规范,并且碎片会以一致的方式更彻底地实现。 在使用相关浏览器从浏览器打印时发现的错误当然值得提出。 如果我们不要求修复这些东西,它们将保持低优先级来修复。
如果您确实需要高级别的打印支持并且想要使用 CSS,那么目前您需要使用特定于打印的用户代理,例如 Prince。 在我的文章“使用 CSS 设计打印”中,我详细介绍了如何使用 CSS 来格式化书籍。
Prince 也可以安装在您的服务器上,以便在 Web 上使用 CSS 生成精美的打印文档,但是,它的价格很高。 另一种选择是像 DocRaptor 这样的服务器,它在 Prince 渲染引擎之上提供 API。
有诸如 wkhtmltopdf 之类的开源 HTML 和 CSS-to-PDF 生成器,但大多数使用浏览器渲染引擎来创建打印输出,因此在实现分页媒体和碎片规范时与浏览器具有相同的限制。 一个例外是 WeasyPrint,它似乎有自己的实现并支持略有不同的功能,尽管它不像 Prince 那样功能齐全。
您将在 print-css.rocks 站点上找到有关用于打印的用户代理的更多信息。
其他资源
由于过去几年从 CSS 打印的内容确实很少,因此 Smashing Magazine 和其他地方的许多旧资源仍然有效。 可以在以下资源中找到一些额外的提示和技巧。 如果您发现了有用的打印工作流程或技术提示,请将其添加到下面的评论中。
- “我完全忘记了打印样式表,” UX Collective 的 Manuel Matuzovic
- “打印样式表方法:黑名单与白名单”,Chris Coyier,CSS-Tricks
- “完美的打印样式表,” Andreas Hecht,Noupe
- “如何设置打印样式表”,Christian Krammer,Smashing Magazine
- “打印样式表的 5 个强大技巧和窍门”,Dudley Storey,Smashing Magazine