获取带换行的渲染HTML文本及扩展场景的JavaScript解决方案
解决方案:HTML转带格式的文本(支持列表、表格)
我之前刚好处理过类似的需求,既要把渲染后的HTML转换成带正确换行的文本,还要兼容列表、无边框表格这类特殊场景,下面给你分享客户端和服务端两种JavaScript实现方案:
一、客户端浏览器端实现
利用浏览器原生的DOM API,我们可以递归遍历节点,根据元素的标签类型、计算样式来判断是否添加换行,同时处理列表和表格的格式:
function convertHtmlToText(element) { let text = ''; const computedStyle = window.getComputedStyle(element); const isBlockElement = ['block', 'flex', 'grid', 'table', 'list-item'].includes(computedStyle.display); // 处理块级元素的前置换行(避免顶部多余换行) if (isBlockElement && element !== document.body) { text += '\n'; } for (const child of element.childNodes) { switch (child.nodeType) { case Node.TEXT_NODE: // 保留文本的有效空格,去除首尾多余空白 const trimmedText = child.textContent.trim(); if (trimmedText) { text += trimmedText + ' '; } break; case Node.ELEMENT_NODE: switch (child.tagName.toLowerCase()) { case 'ul': // 处理无序列表:每个li前加" • " for (const li of child.children) { text += ` • ${convertHtmlToText(li).trim()}\n`; } break; case 'ol': // 处理有序列表(可选,按需求调整) let index = 1; for (const li of child.children) { text += ` ${index}. ${convertHtmlToText(li).trim()}\n`; index++; } break; case 'table': // 处理无边框表格:单元格用空格分隔,每行换行 for (const tr of child.querySelectorAll('tr')) { const cells = Array.from(tr.querySelectorAll('td, th')).map(cell => convertHtmlToText(cell).trim()); text += cells.join(' ') + '\n'; } break; default: // 递归处理其他元素 text += convertHtmlToText(child); break; } break; } } // 处理块级元素的后置换行(避免底部多余换行) if (isBlockElement && element !== document.body) { text += '\n'; } // 去除多余的连续换行,保留单个分隔 return text.replace(/\n{2,}/g, '\n').trim(); } // 使用示例:传入目标元素 const resultText = convertHtmlToText(document.getElementById('target-content')); console.log(resultText);
这个方案的关键是通过getComputedStyle判断元素是否为块级,避免把内联的div错误添加换行;列表和表格则单独处理格式,保证输出符合要求。
二、服务端Node.js实现
如果需要在服务端处理,可以用jsdom模拟浏览器环境,逻辑和客户端基本一致:
首先安装依赖:
npm install jsdom
然后实现代码:
const { JSDOM } = require('jsdom'); function convertHtmlToText(htmlString) { const dom = new JSDOM(htmlString); const document = dom.window.document; function traverse(element) { let text = ''; const computedStyle = dom.window.getComputedStyle(element); const isBlockElement = ['block', 'flex', 'grid', 'table', 'list-item'].includes(computedStyle.display); if (isBlockElement && element !== document.body) { text += '\n'; } for (const child of element.childNodes) { switch (child.nodeType) { case Node.TEXT_NODE: const trimmedText = child.textContent.trim(); if (trimmedText) { text += trimmedText + ' '; } break; case Node.ELEMENT_NODE: switch (child.tagName.toLowerCase()) { case 'ul': for (const li of child.children) { text += ` • ${traverse(li).trim()}\n`; } break; case 'ol': let index = 1; for (const li of child.children) { text += ` ${index}. ${traverse(li).trim()}\n`; index++; } break; case 'table': for (const tr of child.querySelectorAll('tr')) { const cells = Array.from(tr.querySelectorAll('td, th')).map(cell => traverse(cell).trim()); text += cells.join(' ') + '\n'; } break; default: text += traverse(child); break; } break; } } if (isBlockElement && element !== document.body) { text += '\n'; } return text.replace(/\n{2,}/g, '\n').trim(); } return traverse(document.body); } // 使用示例:传入HTML字符串 const html = ` <div style="display: inline">这是内联div,不换行</div> <div style="display: block">这是块级div,会换行</div> <ul> <li>列表项1</li> <li>列表项2</li> </ul> <table> <tr><th>表头1</th><th>表头2</th></tr> <tr><td>内容1</td><td>内容2</td></tr> </table> `; console.log(convertHtmlToText(html));
扩展说明
- 如果需要处理更复杂的表格(比如合并单元格),可以额外解析
rowspan和colspan属性,补充对应数量的空格或占位文本; - 对于自定义的块级/内联元素,可以在
isBlockElement的判断数组里添加对应的display值; - 可以根据需求调整列表的缩进(比如把" • "改成" • ")或表格的单元格分隔符(比如用\t代替空格)。
内容的提问来源于stack exchange,提问作者Etay Liberman
相关产品推荐
相关产品推荐

