ReactJS -打印DOM中的特定元素



我在一个应用程序上使用ReactJS,目前需要能够从用户的请求页面上打印一些元素(点击按钮)。

我选择使用CSS媒体查询类型print (@media print)来检查是否应该打印一个元素,基于一个选择器,这个选择器可以来自一个元素的类或属性。策略是隐藏所有内容,只保留那些"可打印"的内容。元素的样式表如下:

@media print {
*:not([data-print]) {
display: none;
}
}

然而,要做到这一点,我还需要在每个可打印元素的每个父元素上添加所选择的打印选择器(这里是属性data-print)。

要做到这一点,这是我到目前为止所做的尝试:

export default function PrintButton() {
useEffect(() => {
const handleBeforePrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
let element = printableElement;
while (element.parentElement) {
element.setAttribute("data-print", "");
element = element.parentElement;
}
element.setAttribute("data-print", "");
}
});
};
const handleAfterPrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
let element = printableElement;
while (element.parentElement) {
element.removeAttribute("data-print");
element = element.parentElement;
}
element.removeAttribute("data-print");
}
});
};
window.addEventListener("beforeprint", handleBeforePrint);
window.addEventListener("afterprint", handleAfterPrint);
return () => {
window.removeEventListener("beforeprint", handleBeforePrint);
window.removeEventListener("afterprint", handleAfterPrint);
};
}, []);
return <button onClick={() => window.print()}>Print</button>;
}

其中printNodeSelectors为字符串选择器的const Array

不幸的是,React似乎在我做了所有的脏DOM修改后立即删除😭

我想找到一种方法来实现这一目标,而不必手动把无处不在的应用程序谁应该是可打印的,而在一个React应用程序上工作,有人知道如何做到这一点吗?🙏🏼

仅CSS应该足以隐藏所有不具有data-print属性的元素,并且在其后代中不具有此类元素

使用:hasCSS伪类(与:not伪类结合)来表达第二个条件(后代的选择器):

@media print {
*:not([data-print]):not(:has([data-print])) {
display: none;
}
}

警告:具有data-print属性的元素的祖先将不匹配,因此它们的文本节点(未被标签包装)在打印时不会被隐藏:

<div>
<span>should not print</span>
<span data-print>but this should</span>
Caution: text node without tag may be printed...
</div>

演示:https://jsfiddle.net/6x34ad50/1/(您可以启动打印预览浏览器功能来查看效果,或依赖着色)

类似但只是着色直接看效果:

*:not([data-print]):not(:has([data-print])) {
color: red;
}
<div>
<span>should not print (be colored in red)</span>
<span data-print>but this should</span>
Caution: text node without tag may be printed...
</div>

经过一些思考,尝试和错误之后,似乎即使我设法将属性选择器放在父元素上,我也完全错过了我想要打印的元素的子元素!(React最终并没有从一个神秘的渲染周期中移除属性)

这是一个正在运行的组件:

export default function PrintButton() {

useEffect(() => {
const handleBeforePrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
const elements: Element[] = [];
// we need to give all parents and children a data-print attribute for them to be displayed on print
const addParents = (element: Element) => {
if (element.parentElement) {
elements.push(element.parentElement);
addParents(element.parentElement);
}
};
addParents(printableElement);
const addChildrens = (element: Element) => {
elements.push(element);
Array.from(element.children).forEach(addChildrens);
};
addChildrens(printableElement);
elements.forEach((element) => element.setAttribute("data-print", ""));
}
});
};
const handleAfterPrint = () => {
document.querySelectorAll("[data-print]").forEach((element) => element.removeAttribute("data-print"));
};
window.addEventListener("beforeprint", handleBeforePrint);
window.addEventListener("afterprint", handleAfterPrint);
return () => {
window.removeEventListener("beforeprint", handleBeforePrint);
window.removeEventListener("afterprint", handleAfterPrint);
};
}, []);
return <button onClick={() => window.print()}>Print</button>;
}

我通常不喜欢在使用React时弄乱DOM,但在这里它允许我保留组件中的所有内容,而无需修改周围的任何东西(尽管我同意那些printNodeSelectors需要从外部选择,并且目前不是动态的)

最新更新