HTML API + CSS 控制页面打印内容和样式

最后更新:

本周来了一个新的需求,需要前端生成打印内容,每一项数据占据一张 A4 纸,选择多项就是分多张打印,所以需要打印指定内容区域,并且使用 page-break 来控制打印区域的分页。
以前就只使用 CSS 控制过打印时样式,隐藏一些不需要打印的区域,还尝没有试过打印指定区域内容,并且控制打印内容强制分页,所以记录一下。


#1 选择打印区域

局部打印的方式据我了解到的有三种:

  1. 通过开始、结束标记来控制打印范围;
  • <!--startprint--><!--endprint-->
  1. 通过把局部内容赋值给body,打印整个页面,而后再把原页面内容重复覆盖回来;
  2. 通过动态创建<iframe>来打印;
    window.print()局部打印三种方式 - 硅谷工具人

比较推荐的是第一种第三种方法。不过我选择去NPM上找一个现成的轮子 😁。
其实也不是我找的,其实小伙伴再之前已经做完了局部打印功能,只是后来提了新的需求这回要求打印内容的样式,所以由我来接手了。
选择的库是 vue-print-nb,我看了一下源码其实现方式是上面提到的第三种。

首先通过 documentCreateElement('iframe') 创建一个 <iframe> 元素;再通过 document.getElementById() 拿到需要打印的区域内容,同时把获取到需要打印的区域内容通过 Node.cloneNode() 复制到创建的 <iframe> 当中;最后用 window.print() 这个API打开打印对话框打印当前文档,就可以实现打印原先指定的区域内容了。

更详细的方法可以查看仓库源码或者查看文章尾部的参考资源中的对应文章,我这里就不现学现卖了。

#2 配置页面打印样式

通过 CSS 的 @page 规则可以控制打印时的页面样式,但是可配置的内容不多。

@page 规则用于在打印文档时修改某些 CSS 属性。你不能用 @page 规则来修改所有的 CSS 属性,而是只能修改 margin,orphans,widowpage breaks of the document。对其他属性的修改是无效的。
@page - MDN

暂时我能用到的就只有 marginsize 以及 page-break 了,使用方式很简单和正常的CSS一样使用,例如:

@page {
  margin: 1cm; // 设置打印页边距;
  size: A4 portrait; // 指定打印时纸张大小和方向
  break-after: page; // 分页属性
}

可以把 @page 规则理解成打印时的对象去设置,并不和 @media print 相似。
并且我在尝试使用 size 指定纸张的大小时(别名), ChromeFireFoxEdge 都没有生效。指定纸张方向是可以的。

  • 使用别名指定纸张大小时, Edge 中甚至影响到了 margin 属性的生效,所以最好 size 只指定方向 (2022 年 7 月 25 日)。
  • 使用具体数值指定纸张大小时,ChromeFireFox 中指定的纸张方向失效了 (2022 年 7 月 25 日)。

简单配置一下页面打印样式,然后开始写打印内容样式。因为实际上可以配置的属性真的不多,而且各浏览器支持的程度也参差不齐,如果高定制化的话会很容易自闭。

#3 页面内容样式

这块内容就是正常写CSS样式了,有一部分在打印时的特殊样式搭配使用 @media print 来匹配就可以了,一遍调整一边使用浏览器的打印预览功能查看实际效果即可,所以就不赘述了。

#4 分页打印内容

分页的话,使用在前文中提到的 page-break,给占据整页/整块区域的内容增加 break-before 或者 break-after 属性即可。
也可以给单独的类似 <hr /> 这样的水平线元素增加 page-break 属性。然后再搭配 @media print 给水平线在打印时隐藏,这样的话可以在查看页面的时候就知道哪些部分他会分页,并且在打印时也不会印象打印内容排版。

注意:如果要在打印时隐藏 <hr/> 元素并且保持分页功能,需要使用 visibility: hidden; 来隐藏,不能使用 display:none 不然分页属性不会生效。


💥 遇到问题

1. 使用 page-break-after 是内容分页但在打印时不生效

因为 page-break-afterbreak-after 替代了。相对应的 page-break-before 也被 break-before 属性替代了。
也有可能是因为你的值设置错了,设置为 page 就好了。

  • 常规中断值有:auto,avoid,always,all;
  • 分页使用的值有:avoid-page,page,left,right,recto,verso;
  • 分栏使用的值有:avoid-column,column;
  • 分区使用的值有:avoid-region,region;
    《CSS新世界》

我看张鑫旭大佬说 region 相关的属性现代浏览器已经不再支持了,所以可以忽略。

2. 设置的背景色没有被打印。

因为默认打印页面时为了节约墨水,所以默认情况下是不会打印背景色的,如果希望打印的时候保留背景色可以只用 color-adjust 属性来控制。

color-adjust 属性并非一个标准属性,所以在使用时需要查看一下各浏览器的兼容程度。有的浏览器需要增加私有前缀(比如 -webkit-print-color-adjust)。

3. 打印页面的时候,在打印预览窗口选择了彩色模式但是实际打印出来还是黑白的。

浏览器弹出的打印预览设置了彩色模式,并不一定代表了打印机设置的首选项内是彩色的,可以打开对应的打印机查看首选项设置内的色彩模式是否为彩色。
具体步骤为:

  1. 在打印预览弹窗内点击 使用系统对话框打印
    • 如果没有该项,可在浏览器中使用 Ctrl+Shift+P 的组合键打开
  2. 在弹出的打印窗口中找到对应的打印机设备,点击首选项
  3. 选择纸张/输出卡片,找到色彩模式修改为彩色
  4. 最后点击确定保存并应用设置,再点击打印尝试打印;

尾声

现在很多和打印相关的规则和API还都是草案,很多规则都没有固定下来成为标准,不同的浏览器也有自己的实现方式,有些属性还是部分支持(比如:size 属性)或者需要私有前缀。
尽量先查看一下最新的文档,不要照搬笔记或者文章,因为谁也不知道啥时候就改变了,我在写这篇笔记时看到草案的修订时间是 2022年5月24日,不知道你们看到的时候会是第几个版本了。

那就这样吧,以上。

📚 文档

Paged media - CSS | MDN
@page - CSS | MDN
size - CSS | MDN
page-break-after - CSS | MDN
break-after - CSS | MDN
print-color-adjust - CSS | MDN
window.print - Web API | MDN
CSS Paged Media Module Level 3 | W3C Editor’s Draft

page-break | CSS-Tricks - CSS-Tricks
Can I force a page break in HTML printing? - Stack Overflow
CSS 打印 - SegmentFault 思否
记CSS中break-after的一个坑 - 知乎
window.print()局部打印三种方式 - 硅谷工具人 - 博客园
window.print()打印时根据页面高度设置居中显示、设置打印布局(纵向、横向)_清云青云的博客 - CSDN