如何在TypeScript中拆分HTML为行并保留HTML结构?
解决TypeScript中按换行拆分HTML并保留DOM结构的问题
直接按字符串拆分\n会破坏嵌套DOM结构,导致子元素丢失父容器的类和层级。正确做法是通过DOM API遍历处理节点,而非操作原始HTML字符串。
实现思路
- 将HTML解析为DOM树,避免字符串操作的结构破坏
- 递归遍历DOM节点,识别文本节点中的换行符
- 对包含换行的文本节点,拆分文本并复制父元素层级,生成独立的行节点
- 收集所有行节点,转换为HTML字符串输出
TypeScript代码实现
function splitHtmlByLine(html: string): string[] { // 解析HTML为DOM元素 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const targetElement = doc.body.firstElementChild; if (!targetElement) return []; const lineElements: Element[] = []; // 递归处理节点,拆分换行并维护DOM结构 function processNode(node: Node, parentStack: Element[]): void { if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent || ''; const lines = text.split('\n'); lines.forEach((lineText, index) => { if (index === 0) { node.textContent = lineText; } else { // 复制整个父层级结构 const newParentChain = parentStack.map(parent => parent.cloneNode(false) as Element); const newTextNode = doc.createTextNode(lineText); // 组装新DOM链 let current = newParentChain[0]; for (let i = 1; i < newParentChain.length; i++) { current.appendChild(newParentChain[i]); current = newParentChain[i]; } current.appendChild(newTextNode); lineElements.push(newParentChain[0]); } }); } else if (node.nodeType === Node.ELEMENT_NODE) { const element = node as Element; const newParentStack = [...parentStack, element]; const children = Array.from(element.childNodes); children.forEach(child => processNode(child, newParentStack)); // 识别顶级token元素作为行单元 if (element.classList.contains('token') && !element.parentElement?.classList.contains('token')) { lineElements.push(element.cloneNode(true) as Element); } } } processNode(targetElement, []); // 转换为HTML字符串列表 return lineElements.map(el => el.outerHTML); } // 示例调用 const sampleHtml = ` <code class="language-diff is-loaded"><span class="token coord">@@ -1 +1,2 @@</span> <span class="token deleted-sign deleted"><span class="token prefix deleted">-</span> <span class="token line">print('hello world') </span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">for i in range(10): </span><span class="token prefix inserted">+</span><span class="token line"> print('hello world') </span></span></code> `; const result = splitHtmlByLine(sampleHtml); result.forEach((line, idx) => { console.log(`line ${idx+1}: ${line}`); });
效果验证
拆分后原问题中的第4行将正确保留父元素结构:
<span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line"> print('hello world')</span></span>
内容的提问来源于stack exchange,提问作者kampi
相关产品推荐
相关产品推荐

