Angular中使用jsPDF+html2canvas导出含输入框的表格为PDF时输入值不显示
问题根源定位
html2canvas的渲染逻辑是爬取DOM的静态结构生成canvas,原生input、textarea、select这类表单控件的用户输入值,存储在元素的运行时value属性中,不会写入DOM的静态文本节点,因此html2canvas抓取时只能识别到输入框的边框、初始占位符,完全读不到用户手动输入的动态内容,这就是表格加了输入框后导出内容缺失的核心原因。
另外当前HTML结构存在规范问题:table标签内直接嵌套div块级元素,不符合HTML标准,浏览器渲染时会自动把这些div移出table的DOM层级,这也是之前更换form标签后导出仍然出现错位、内容不全的次要原因,建议把外层table替换为普通div容器,通过CSS实现表格布局效果,避免浏览器自动重排DOM。
适配现有技术栈的修复方案(jsPDF + html2canvas)
核心思路是:导出触发时,把所有输入框的当前值临时转换成同位置的静态文本节点,隐藏原始input元素,等html2canvas完成渲染生成canvas后,再恢复原始输入框的显示,全程不影响页面的正常交互。
改造后的代码如下:
pdfCreationClick() { const doc = new jspdf.jsPDF('p','mm','a4'); doc.text("this is siva",15,15); const targetContainer = document.getElementById('secondTable') as HTMLElement; // 临时镜像所有输入框的当前值 const tempReplaceList: Array<{originInput: HTMLInputElement, tempNode: HTMLElement}> = []; targetContainer.querySelectorAll('input').forEach(inputEl => { const currentValue = inputEl.value; // 创建和原输入框尺寸、样式完全一致的静态文本节点 const tempTextNode = document.createElement('span'); tempTextNode.textContent = currentValue; const inputStyle = getComputedStyle(inputEl); tempTextNode.style.cssText = ` display: inline-block; width: ${inputEl.offsetWidth}px; height: ${inputEl.offsetHeight}px; padding: ${inputStyle.padding}; border: none; font: ${inputStyle.font}; color: ${inputStyle.color}; line-height: ${inputStyle.lineHeight}; `; // 临时隐藏原输入框,插入静态文本节点 inputEl.style.visibility = 'hidden'; inputEl.parentNode?.appendChild(tempTextNode); tempReplaceList.push({originInput: inputEl, tempNode: tempTextNode}); }); // 执行原有渲染逻辑 html2canvas(targetContainer).then((canvas) => { // 渲染完成后恢复原输入框的显示 tempReplaceList.forEach(({originInput, tempNode}) => { tempNode.remove(); originInput.style.visibility = ''; }); const imgWidth = 208; const imgData = canvas.toDataURL('image/png'); const imgHeight = canvas.height * imgWidth / canvas.width; doc.addImage(imgData, 'PNG', 0, 0, 208, imgHeight); doc.save('first.pdf'); }) }
更稳定的替代导出方案(无截图类问题)
不需要依赖DOM截图渲染,直接利用组件的双向绑定数据源dataarray,通过jsPDF的原生表格生成能力,绘制矢量格式的表格内容,彻底规避输入框值读取不到的问题,同时导出的PDF文件更小、文本更清晰。
实现代码示例:
pdfCreationClick() { const doc = new jspdf.jsPDF('p','mm','a4'); doc.text("this is siva", 15, 15); // 定义表格列配置 const tableHeader = [['序号', '产品', '描述', '数量', '单价', '金额', '税率']]; // 直接从组件数据源读取用户填写的内容(因为有[(ngModel)]双向绑定,数据实时同步) const tableBody = this.dataarray.map((item, index) => [ index + 1, item.product, item.description, item.quantity, item.rate, item.rate * item.quantity, item.tax ]); // 绘制矢量表格 (doc as any).autoTable({ startY: 20, head: tableHeader, body: tableBody, theme: 'grid' }); doc.save('first.pdf'); }
该方案的优势:
- 完全和DOM渲染逻辑解耦,不存在输入框值丢失的问题,用户输入的内容会通过双向绑定实时同步到ts变量中,读取即可使用
- 导出的PDF为矢量文本格式,放大不会出现模糊问题,文件体积比截图式导出小70%以上
- 不受HTML结构是否符合规范的影响,不会出现内容错位
内容的提问来源于stack exchange,提问作者Siva
相关产品推荐
相关产品推荐

