打印iframe中CSS加载不一致问题排查求助
解决方案:重复打印时样式丢失/资源Pending问题
这个问题的核心原因很明确:你在iframe的外部资源(CSS/JS)还没完全加载完成时,就触发了print()操作。Chrome会优先处理打印预览,导致资源请求被挂起,直到你关闭打印窗口才继续加载,自然第二次及以后打印时样式就丢失了。
下面是具体的修复思路和代码调整方案:
1. 等待所有资源加载完成后再打印
最关键的一步是确保iframe里的所有CSS、JS都加载完毕,再调用打印函数。我们可以用Promise来跟踪每个资源的加载状态,用async/await等待全部加载完成。
2. 可选:复用iframe避免重复加载资源
每次创建新iframe都会重新请求外部资源,不仅慢还容易出问题。可以第一次创建后保留iframe,后续打印直接更新内容,资源只加载一次。
修改后的完整代码
// 辅助函数:加载资源并返回Promise,用于跟踪加载状态 function loadResource(type, url, attrs = {}) { return new Promise((resolve, reject) => { let elem; if (type === 'css') { elem = document.createElement('link'); elem.rel = 'stylesheet'; elem.href = url; } else if (type === 'js') { elem = document.createElement('script'); elem.src = url; } // 附加额外属性(比如FontAwesome的integrity和crossorigin) Object.assign(elem, attrs); elem.onload = resolve; elem.onerror = () => reject(new Error(`Failed to load ${url}`)); return elem; }); } // 打印函数(改为async函数) async function print1(elem) { // 尝试复用已有的iframe,没有则创建新的 let printFrame = document.getElementById('printFrame'); if (!printFrame) { printFrame = document.createElement('iframe'); printFrame.id = 'printFrame'; printFrame.name = 'printFrame'; printFrame.className = 'printFrame'; printFrame.style.display = 'none'; document.body.appendChild(printFrame); } const iframeDoc = printFrame.contentDocument || printFrame.contentWindow.document; // 重置iframe内容,避免旧内容干扰 iframeDoc.open(); iframeDoc.write('<!DOCTYPE html><html><head></head><body></body></html>'); iframeDoc.close(); const head = iframeDoc.querySelector('head'); const body = iframeDoc.querySelector('body'); // 定义需要加载的所有资源,等待全部加载完成 try { const resources = [ loadResource('css', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'), loadResource('css', 'https://use.fontawesome.com/releases/v5.3.1/css/all.css', { integrity: 'sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU', crossorigin: 'anonymous' }), loadResource('css', 'https://fonts.googleapis.com/css?family=Roboto|Roboto+Condensed'), loadResource('css', '/css/wscustom.css'), loadResource('js', 'https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js'), loadResource('js', 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js') ]; await Promise.all(resources); } catch (err) { console.error('资源加载失败:', err); alert('打印资源加载失败,请重试'); return; } // 复制页面内的style标签到iframe const pageStyle = document.querySelector('style'); if (pageStyle) { head.appendChild(pageStyle.cloneNode(true)); } // 设置body基础样式 Object.assign(body.style, { fontFamily: 'Roboto, sans-serif', fontSize: '14px', lineHeight: '1.428571429', color: '#333', boxSizing: 'border-box' }); // 构建打印内容并调整样式 const container = iframeDoc.createElement('div'); container.className = 'container'; // 完整复制目标元素(包括子元素和事件,这里用true深拷贝) const elemClone = document.getElementById(elem).cloneNode(true); container.appendChild(elemClone); body.appendChild(container); // 调整列的样式 const cols = container.querySelectorAll('div.col-xs-12.col-md-6.col-lg-4'); cols.forEach(col => { col.classList.remove('col-xs-12', 'col-md-6', 'col-lg-4'); col.classList.add('col-4'); Object.assign(col.style, { width: '31.7%', margin: '5px', height: '514px', float: 'left', position: 'relative' }); const redoIcon = col.querySelector('.fa-redo'); if (redoIcon) redoIcon.style.display = 'none'; }); // 小延迟确保iframe内的DOM和样式完全渲染 await new Promise(resolve => setTimeout(resolve, 100)); // 触发打印 printFrame.contentWindow.focus(); printFrame.contentWindow.print(); // 可选:如果不需要复用iframe,打印后延迟移除(避免打印窗口还在就移除) // setTimeout(() => { // if (printFrame.parentNode) printFrame.parentNode.removeChild(printFrame); // }, 1000); }
额外注意事项
- FontAwesome图标问题:之前的图标显示异常,本质也是字体文件加载延迟导致的,等待资源加载完成后再打印就能解决。
- 打印样式优化:建议把打印专用的样式写在
@media print媒体查询里,放在wscustom.css中,避免JS动态修改样式的不稳定。 - 缓存问题:如果复用iframe,每次打印前一定要重置iframe的内容,避免旧的缓存内容干扰新的打印。
内容的提问来源于stack exchange,提问作者Deo Carvalho
相关产品推荐
相关产品推荐

