🌒

Hi Folks.

局部打印实现

引言: 通常实现打印功能,我们会通过window.print()来实现,但是这个方法没办法做到局部打印,例如我想打印一个dom的内容。其实只需要做一些简单处理便可以实现打印了。


方案一(🤔)

将页面DOM临时替换成局部DOM

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<div class="container">
<h1>Hello Word</h1>
<button onclick="printPage()">打印页面</button>
<div class="print-area">
<div class="content">
<h2>Title</h2>
<p>把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。
</p>
</div>
</div>
</div>
</body>

例如上方的结构,我想打印.print-area的内容,我可以这么操作:

1
2
3
4
5
6
7
8
9
10
11
const printPage = () =>{
const printAreaDom = document.querySelector('.print-area')
const originContent /* 原始内容 */ = document.body.innerHTML

// 将页面DOM临时替换成局部DOM
document.body.innerHTML = printAreaDom.innerHTML
// 执行打印
window.print()
// 复原
document.body.innerHTML = originContent
}

这样做虽然能够打印出想要的内容,但是在复杂的环境下,可能会导致dom中的监听事件丢失。


方案二(🤩)

使用CSS媒体查询@print

直接贴代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<head>
<style>
@media print {
:has(.print-area)> :not(.print-area):not(:has(.print-area)) {
display: none;
}
.print-area {
padding: 32px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Hello Word</h1>
<button onclick="printPage()">打印页面</button>
<div class="print-area">
<div class="content">
<h2>Title</h2>
<p>把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。把自己当傻瓜,不懂就问,你会学的更多。
</p>
</div>
</div>
</div>

<script>
function printPage() {
window.print();
}
</script>
</body>

经过如上的媒体查询设置,window.print()就只会打印.print-area中的内容了,其他的内容就会被隐藏。它的作用是选择拥有类名为 .print-area 的祖先元素但不是 .print-area 类型的子孙元素。换句话说,它选择了所有不包含 .print-area 类的元素,并且这些元素的祖先元素中有包含 .print-area 类的元素。对于这些元素,将它们的 display 属性设置为 none,在打印时隐藏它们。*不过要注意的是,该方法只适用于支持:has选择器的运行环境下,好在目前的主流浏览器都支持这个特性,应用MDN里的一句话:

Since December 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

译: 自2023年12月起,此功能适用于最新设备和浏览器版本。此功能可能无法在较旧的设备或浏览器中工作。

兼容性明细

image-20240330190658198

「在没有IE的新时代里,放心使用」

若是对兼容性有苛刻的要求的话,可以使用成熟的三方库实现打印。在Electron、Tauri、uniapp这种跨平台框架中,应该去尝试使用像:has选择器这样的新花样来帮助我们的屎山代码化繁为简。


🔗;

MDN对:has()选择器的介绍


👋;

, — 2024年3月30日