这两天有个需求:
要使用 js 打印页面指定元素,且保留样式。
- 以前的打印基本都是使用
iframe
嵌入预先写好的打印模板页面,再执行打印。 - 但是这次只是打印页面的表格,且每次都是动态渲染的,再写打印模板感觉就是重复的无用功。
- 所以决定封装一个方法直接在页面上操作打印表格元素
网上搜索的方案
方案1
let h = window.open('打印窗口', '_blank');
h.document.write(document.getElementById('print-cold-table')?.innerHTML);
h.document.close();
h.print();
h.close();
看了下网上的大部分方案都是新开一个窗口,写入元素内容,执行打印。
- 这个方案是没有保留样式的,且新开窗口感觉不友好。
方案2
点击打印时,将页面其他元素隐藏,只显示需打印的元素。
- 感觉对于用户来说还是有点奇怪,突然页面空了一块。
我的整合方案
使用动态创建
iframe
将元素内容与需要的样式复制到iframe
中,再执行打印。
- 优点:封装方法,操作简单,方便复用。
- 缺点:每次打印性能上可能不够好
效果预览
代码
/**
* 在打印预览中打印指定元素,并设置样式。
* @example
* printElement('#print-table', {
* bodyStyle: {
* padding: '10px',
* backgroundColor: 'red',
* },
* });
* @param selector - 要打印的元素的 CSS 选择器。
* @param styles - iframe 的 style 配置对象。
* @property {any} style - iframe 的基本样式。
* @property {any} bodyStyle - iframe 的 body 样式。
* @property {any} htmlStyle - iframe 的 html 样式。
* @returns
*/
export function printDom(selector: string, styles?: { iframeStyle?: any; bodyStyle?: any; htmlStyle?: any }) {
// 获取需要打印的元素
const element = document.querySelector(selector);
if (!element) {
console.error(`Element with query selector ${selector} not found.`);
return;
}
// 创建打印的 iframe
const iframe: any = document.createElement('iframe');
// 设置 iframe 的样式
Object.assign(iframe.style, {
display: 'none',
width: '100%',
height: 'auto',
...(styles?.iframeStyle || {}),
});
document.body.appendChild(iframe);
// 获取 iframe 的内置对象
const iframeDoc: any = iframe.contentDocument;
const iframeHtml = iframeDoc.documentElement;
const iframeHead = iframeDoc.head;
const iframeBody = iframeDoc.body;
// 获取元素需要的样式
const elementStyles = getComputedStyle(element);
// 将元素需要的样式添加到 iframe
for (let i = 0; i < elementStyles.length; i++) {
const styleName = elementStyles[i];
iframeBody.style[styleName] = elementStyles.getPropertyValue(styleName);
}
try {
// 获取元素所在页面上的样式
const styleSheets = Array.from(document.styleSheets).map((styleSheet) =>
Array.from(styleSheet.cssRules)
.map((cssRule) => cssRule.cssText)
.join('\n'),
);
// 将元素所在页面的样式添加到 iframe
const styleElement = document.createElement('style');
styleElement.innerHTML = styleSheets.join('\n');
iframeHead.appendChild(styleElement);
} catch (e) {
console.error(e);
}
// 获取元素的内容
const elementContent = element.outerHTML;
// 将元素的内容添加到 iframe
iframeBody.innerHTML = elementContent;
// 设置 iframe.body 和 iframe.html 的样式与添加自定义的一些样式
Object.assign(iframeBody.style, {
width: '100%',
height: 'auto',
...(styles?.bodyStyle || {}),
});
Object.assign(iframeHtml.style, {
width: '100%',
height: 'auto',
...(styles?.htmlStyle || {}),
});
// 执行打印
iframe.contentWindow.print();
// 打印完成后移除 iframe
setTimeout(() => {
document.body.removeChild(iframe);
}, 1000);
return iframe;
}
也可直接安装 js-xxx 工具库直接使用
// npm i js-xxx
import { printDom } from 'js-xxx';
printDom('#print-table', {
bodyStyle: {
padding: '10px',
backgroundColor: 'red',
},
});