近期着手项目任务的打印功能,在此作个记录,本文介绍基于React的一种调用浏览器打印页面指定内容的方法。
整体思路: 通过构建一个隐藏的元素(该元素包裹需打印的内容),当打印行为触发时,将页面其他的一些不需要打印的元素隐藏,然后将需打印的元素追加到body中,打印完成后,再恢复初始状态即可。浏览器打印的本质还是将web页面中的元素打印出来而已。
1. 构建待打印元素
在页面中构建一个display为none
的元素,里面的内容为你需要打印的内容。我们还需要设置包裹打印内容的元素的ref属性
,以便于后面获取到元素。
<div style={{ display: 'none' }}>
<div ref={el => (this.printRef = el)}>
{ 打印内容 }
</div>
</div>
2. 打印动作触发时的处理
处理流程:
- 获取待打印元素;
- 将根元素隐藏;
- 将待打印元素追加到body中;
- 调用浏览器的打印预览;
- 预览界面关闭后,将待打印元素从body中移除,将原始页面恢复。
let printView = this.state.printRef //获取待打印元素
document.querySelector('#root').className = 'print-hide' //将根元素隐藏
document.body.appendChild(printView) //将待打印元素追加到body中
window.print() //调用浏览器的打印预览
document.body.removeChild(printView) //将待打印元素从body中移除
document.querySelector('#root').className = '' //将原始页面恢复
对应的CSS设置:
@page {
size: A4;
margin: 0;
}
@media print {
html, body {
min-width: 0;
width: 210mm;
height: 297mm;
}
.print-hide {
visibility: hidden!important;
display: none!important;
}
}
其中,@page
中的size可以自己设置纸张的大小,如果是A4纸可以直接设置值为A4
,媒体查询@media print
中设置的是打印时的样式,因为打印设备知道其输出区域的物理大小,所以使用厘米(cm)、毫米(mm)、英寸(in)等作为打印设计的单位完全可行。
补充(其他原生的打印方法)
- 直接替换body的内容为要打印的内容,之后再重新刷新页面。
const old = window.document.body.innerHTML //备份原来的页面
window.document.body.innerHTML = ''
window.document.body.appendChild(/* 将你要打印的内容附加到这 */)
window.print() //调用print()函数时,会跳出打印预览的界面,以下的代码被阻塞,关闭预览界面后继续执行
window.document.body.innerHTML = old
window.location.reload() //重新加载旧页面
- 打开一个新窗口,将打印内容放到新窗口打印,打印结束后关闭新窗口
const newWindow = window.open("打印窗口", "_blank")
const docStr = '<div>test</div>' //需要打印的内容
newWindow.document.write(docStr)
const styles = document.createElement("style")
styles.setAttribute('type', 'text/css') //media="print"
styles.innerHTML = ''
newWindow.document.getElementsByTagName('head')[0].appendChild(styles)
newWindow.print()
newWindow.close()
以上两种方法可能会造成CSS样式应用无效的问题。
3. 注意点
- 第二小节的步骤2中的意思是:将页面中所有不需要打印的元素隐藏,特别注意像模态窗Model这些元素,也要为它们加上
'print-hide'
className属性。 - 如果需要在特定位置强制分页打印,可以尝试在对应元素上设置
page-break-before:always !important
、page-break-after:always !important
CSS属性,该属性只对块级元素有效(这个非常重要,就被这个坑了)。 - 进入打印预览后,我们无法获知用户最终是选择了打印,还是选择了取消。这里若有人知道解决方法的话,欢迎留言。
- JS能实现谷歌和IE浏览器打印预览,火狐不行,火狐浏览器对于JS的操作权限有极为严格的限制,其基本原则是JS无法操作页面外事务,可以试试用JS调用HTML5机制模拟打印界面输出到打印机,不通过浏览器来完成。
- 在使用口袋打印机(喵喵机)打印,如果打印内容有二维码的时候,要注意二维码的分辨率问题。(我就踩了一个坑:二维码组件生成二维码,默认是以canvas的形式,这样打印出来导致整个内容模糊,改成svg就可以清晰打印了)
若对你有帮助,可以支持一下作者创作更多好文章哦~