React+TypeScript中window.print触发页面冻结问题求助
问题
在React+TypeScript项目中,尝试打印模态框内带有数据的div(id='printablediv'),点击模态框上的打印按钮时执行以下Print函数:
const Print = () => { let printContents = document.getElementById('printablediv')?.innerHTML!; let originalContents = document.body.innerHTML; document!.body!.innerHTML! = printContents; window.print(); document.body.innerHTML = originalContents; setIsModalOpen(false) }
点击打印后,无论完成打印还是取消打印,页面都会冻结,无法进行任何操作,必须刷新页面才能恢复。不希望使用react-to-print这类第三方库。
补充:Print函数的调用方式如下:
<TicketModal isOpen={isTicketModalOpen} onAnulla={onCloseTicketModal} onPrint={Print} code={createResponseCode} pin={createResponsePin} id={user.uid} currency={createResponseCurrency} amount={createResponseAmount} />
onPrint属性关联到模态框内的打印按钮:
<Button size="lg" variant="primary" className='ticket-modal-card__button' onClick={onPrint} > Print Ticket </Button>
解决思路与修复方案
问题根源
直接替换document.body.innerHTML会彻底破坏React维护的虚拟DOM与真实DOM的绑定关系,同时移除了所有原DOM节点上的事件监听、组件状态关联。即便后续把innerHTML还原,也无法恢复这些绑定,导致页面失去交互能力。
修复代码
改用临时元素承载打印内容,避免修改原body的DOM结构:
const Print = () => { const printDiv = document.getElementById('printablediv'); if (!printDiv) return; // 创建临时容器 const tempContainer = document.createElement('div'); // 复制目标div的内容和基础样式,保证打印显示正常 tempContainer.innerHTML = printDiv.innerHTML; tempContainer.style.position = 'absolute'; tempContainer.style.top = '0'; tempContainer.style.left = '0'; tempContainer.style.width = '100%'; tempContainer.style.height = '100%'; tempContainer.style.background = 'white'; tempContainer.style.zIndex = '9999'; // 保存原body的所有子元素,避免直接替换innerHTML const originalBodyChildren = Array.from(document.body.children); // 清空body并添加临时容器 document.body.innerHTML = ''; document.body.appendChild(tempContainer); // 打印完成/取消后的恢复逻辑 const handlePrintEnd = () => { // 移除临时容器 tempContainer.remove(); // 恢复原body的所有子元素 originalBodyChildren.forEach(child => document.body.appendChild(child)); // 关闭模态框 setIsModalOpen(false); // 移除事件监听,防止重复绑定 window.removeEventListener('afterprint', handlePrintEnd); }; // 监听打印结束事件 window.addEventListener('afterprint', handlePrintEnd); // 触发打印 window.print(); }
关键改进点
- 不直接替换
document.body.innerHTML,而是保存并恢复原body的子元素,保留React的DOM绑定关系 - 使用临时容器单独承载打印内容,打印完成后直接移除,不影响原页面结构
- 通过
afterprint事件确保无论打印完成还是取消,都能正确恢复页面状态 - 给临时容器设置基础样式,保证打印内容正常显示
内容的提问来源于stack exchange,提问作者Oren Papa
相关产品推荐
相关产品推荐

