这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
浏览器中打印的样式一直是一个比较难以处理的问题(尤其是IE时代),但是,随着现代web的发展,对于实现控制打印时的页面样式,已经提供了很多新特性。下面就介绍一下,如何控制浏览器中的打印样式。以及,在最后介绍几种禁止用户打印的可能方法!
实际浏览器打印,还是和直接通过本地应用调用打印机驱动打印,有着不小的差距,尤其是字体导致的打印清晰度问题。
浏览器打印还有一个很不错的功能点,就是打印时“另存为PDF”,尤其对于想要将网页内容保存为 PDF 格式使用,这是一个不可错过的功能(打印为PDF在样式上不会有太大的偏差,便于保存浏览)。
比如,另存网页为PDF,制作电子档文件(Ctrl + p
或 右键当前网页->菜单中选择“打印”,即可打开打印对话框):
用于打印的CSS
最常见的处理,打印时可能需要隐藏一些内容,比如 footer、header、sidebar 以及 广告推广内容 等。甚至需要为打印设置完全不同的样式和字体。
样式应用的 media 属性
用于答应的样式,可以通过指定 style 或 link 标签的 media 属性为 "print",即表示应用到打印样式
如下所示:
<link rel="stylesheet" src="print.css" type="text/css" media="print" />
指定 media="print" 的 css 外部文件(link引入),会仅在打印时被浏览器下载并渲染。
或
<style type="text/css" media="print">
/* ... */
</style>
@media print 媒体查询
在 CSS 内,使用媒体查询 @media print
将样式只应用到打印上。
@media print {
/* ... */
}
保持页面样式和打印样式一致的最简单方法
保持一致的最简单的方法是使用 media="all"
属性,即所有设备都使用当前样式
<link rel="stylesheet" src="print.css" type="text/css" media="all" />
或
<style type="text/css" media="all">
/* ... */
</style>
打印时的链接处理
链接在 HTML 中几乎无处不在,通过 a 标签引入一个超链接。但是,
如果用于打印,默认只会显示出链接的文字,而丢失真实的链接内容 —— url路径。
要在打印时保留下链接的url,可以借助 :after
伪类,并添加 content,将 url 显示在链接后面。
如下,一个 a 标签应用打印的样式:
@media print {
a[href*='//']:after {
content:" (" attr(href) ") ";
color: red;
}
}
a[href*='//']
仅将外部链接(或url标准链接)在打印时,显示在链接文字的后面(如上图所示)。
内部链接通常用于导航或内部索引,一般不需要打印。如果想将所有链接的url在打印时显示出来,可以改为如下样式:
@media print {
a:after {
content:" (" attr(href) ") ";
}
}
页面中断(Page break)
如果想要在某个元素后,或元素前分页中断,可以使用 page-break-after
和 page-break-before
属性。
比如,实现每三个 div 元素则分页打印:
div:nth-child(3n){
page-break-after: always;
}
page-break-before: always;
也是一样,在元素的前面分页。
page-break-before
和 page-break-after
的取值为: auto
、always
、avoid
、left
、right
。
避免在元素内部在内部中断(尤其是图片)
有一个最重要的问题,就是元素中断,由于元素高度跨度的问题,被中断为在两页显示,尤其是图片,一部分在上一页显示,另一部分在下一页显示。显然是不行的(除非你想这样)。
page-break-inside: avoid;
可以避免元素或图片在内部中断,显示在两页上。
img {
page-break-inside: avoid;
}
div {
page-break-inside: avoid;
}
应用于文档打印的 @page 规则
@page
规则用于打印文档,但只能修改margin,orphans,widow 和 页面中断等属性。而且需要考虑所用浏览器是否支持。
page模型
@page
对应一个页框模型,如下:
它是从 HTML 文档映射过去的。
结合 page 边距的模型:
page 边距
用于纸张打印的 page margin 单位,推荐使用 cm
或 in
。
@page {
margin-top: 2cm;
margin-bottom: 2cm;
margin-left: 2cm;
margin-right: 2cm;
}
@page 的伪类
:first
应用于打印时的首页样式
@page :first{
margin-left: 5cm;
margin-top: 8cm;
}
@page :left
和@page: right
双面打印中的样式
@page :left
双面打印中所有的左侧页;@page :right
双面打印中所有的右侧页。
@page :left {
margin-left: 3cm;
margin-right: 4cm;
}
@page :right {
margin-left: 4cm;
margin-right: 3cm;
}
:blank
打印文档中的空白页
@page :blank {
@top-center {
content: "This page is intentionally left blank";
}
}
page盒子的大小size
size 指定纸张的大小,如:
@page {
size: A4;
}
@page {
size: A5;
}
Margin @ 规则
Margin at 规则只能用在 @page 媒体中,同时目前还处于CSS标准的定义中,将来也有可能被移除。了解即可。
比如,指定 Margin @ 显示 content(内容)【浏览器几乎都未支持】:
@page {
size: A4;
margin: 10%;
@top-left {
content: "我的故事";
}
@bottom-right {
content: "页数:" counter(page);
}
}
所有可能的参考规则如下:
@top-left-corner @top-left @top-center @top-right @top-right-corner @right-top @right-middle @right-bottom @bottom-right-corner @bottom-right @bottom-center @bottom-left @bottom-left-corner @left-bottom @left-center @left-top
调试打印呈现
在开发者工具中,提供了模拟 打印布局 的方式。
F12 打开开发者工具,点击右侧小三点,“更多工具”(More tools
)下,点击“绘制”(Rendering
):
在打开的面板中,在模拟媒体类型下,选择“打印”(print),即可调试打印的样式。
【如何阻止页面打印?】
有一个比较极端的情况,有时候我们不想要用户打印当前页面(这个问题应该结合不允许用户复制页面内容,不允许打开开发者工具等一起解决,后续有机会再讨论)。
当然,这些都不能完全杜绝用户的打印,有很多办法可以跳过这些限制,获取页面内容,然后进行各种需要的操作。
纯CSS实现阻止页面打印
解决办法就是,在打印的 css 样式中,将 body 标签设置为 display: none;
,打印文档不显示页面的任何内容。
@media print {
body { display: none; }
}
这样打开打印页面,就会显示一片空白。从而无法打印。
不过这样显然不是很友好,用户看到一片空白,而不了解是不能打印导致的。因此,我们可以添加一个提示信息。
<body>
<p id="print">当前页面不允许打印!</p>
<div id="noprint">
<!-- 真实的页面内容 -->
</div>
</body>
在主样式中:
#print { display: none; }
#noprint { display: block; }
打印的样式中
@media print {
#print { display: block; }
#noprint { display: none; }
}
也可以放在 print.css
文件中:
<link rel="stylesheet" src="print.css" type="text/css" media="print" />
/* print.css 文件 */
#print { display: block; }
#noprint { display: none; }
显示效果如下:
使用js阻止页面打印
阻止 Ctrl + P
按键 和 浏览器右键菜单
由于在浏览器中打印的方式主要是通过 Ctrl + P,或者,右键菜单点击“打印”实现的。
Ctrl + P
的处理,是在 keypress 事件中,判断是否按下这两个键,如果是,调用 e.preventDefault()
阻止执行。
window.addEventListener("keydown", function (e) {
if(e.ctrlKey && (e.key=="p" || e.key=="P")){
e.preventDefault();
}
});
取消打开右键菜单,同样的 e.preventDefault()
处理,放在 contextmenu
事件方法中。
还有一点,浏览器自身菜单栏中,也有一个“打印”菜单,我们根本无法控制
beforeprint
和 afterprint
打印事件【阻止打印实现】
beforeprint
和 afterprint
是与打印相关的两个事件,但是不支持取消。
不过仍然可以巧妙的结合 document.write()
来实现。
window.addEventListener("beforeprint", function (e) {
document.write('<div style="text-align:center;color:red;">当前页面不允许打印!</div>');
});
在打印预览窗口会显示“当前页面不允许打印!”的提示。
但是,由于是 document.write()
写入文档实现的,点击取消打印后,返回页面就丢失了原来的内容。
并且,点击“取消打印”,并不会触发 afterprint
事件。也就是,不能知道什么时候用户取消打印(来还原原来的页面内容)
还有一个解决办法,在 beforeprint
事件中,重新刷新当前页面,由于重新刷新,就不会打开打印的对话框了。
【都不是很优雅!】
下面通过 js刷新当前页面 实现:
window.addEventListener("beforeprint", function (e) {
confirm("当前页面不允许打印!");
window.location.reload();
});
window.matchMedia("print") 事件方法中巧妙实现阻止打印【小hack,"arcane"方法】
后面在 How to trigger javascript on print event? 中的回答中,发现了一个阻止页面打印很"奥妙"的方法,即:
使用 window.matchMedia("print")
添加事件方法,通过在事件方法中添加一个阻塞方法,阻止打印对话框(或print screen)对当前页面内容的加载,由于无法加载页面也就无法进行打印。
阻止打印的 matchMedia("print")
事件方法:
window.matchMedia("print").addListener(function() {
alert("当前页面不允许打印");
})
同样,该阻塞方法也可以使用 location.reload()
等刷新当前页面的方式实现阻止打印。
通过此方式打开打印对话框时,会一直处于加载的状况,此时无论是点击"打印"还是"取消",都无法进行打印。
点击“打印”/“取消”后,会有弹窗提示 "当前页面不允许打印"。
注:暂时不知何种原因,会显示两次弹窗提示。原文以为是原文回答中提到的有多个
@media print {}
CSS样式的原因,后面测试不是其导致。
beforeprint
中刷新,matchMedia("print")
中阻塞或刷新,可以实现阻止所有可能的浏览器打印方法进行打印。