如何将HTML拆解为区分文本与标记的对象数组?闭合标签处理遇阻
问题:HTML转文本与标记分离的对象数组时闭合标签位置错误
需求
将HTML代码转换为区分文本与标记的对象数组,示例格式如下:
[ {"text": "A "}, {"markup": "<b>"}, {"text": "test"}, {"markup": "</b>"} ]
使用的HTML代码
<h2 id="mcetoc_1h1m1ll27l">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h2> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris at tincidunt lectus.<a href="https://www.sadasdas.es" aria-invalid="true">tr</a><a title="titulo" href="https://www.sadasdas.es" aria-invalid="true">adsf afjdasi k</a><a title="titlee" href="https://www.sadasdas.es" aria-invalid="true">asdsssssssssssss</a><a href="https://www.sadasdas.es" aria-invalid="true">s</a></p> <p><a href="https://www.sadasdas.es" aria-invalid="true">Lorem Ipsum</a></p>
实现思路
为避免使用正则,通过document.createTreeWalker生成节点数组,遍历过程中区分元素节点与文本节点,以此构建目标对象数组。
遇到的问题
当元素节点包含文本节点后接元素节点的结构时,闭合标签位置错误。例如处理段落时得到错误的输出顺序:
{markup: '<p>'} {text: 'Lorem ipsum dolor sit...'} {markup: '</p>'} {markup: '<a>'} //...
预期逻辑中<p>标签应该包裹内部所有文本和子元素,而不是在第一个文本节点后就提前闭合。
已实现的代码
const obj = { annotation: [] }; const nodelist = (() => { const res = []; const tw = document.createTreeWalker(document.body); while (tw.nextNode()) { res.push(tw.currentNode) } return res; })(); console.log(nodelist); const nodeHasParents = (node) => node.parentNode.nodeName !== 'BODY'; const isTextNode = (node) => node.nodeType === Node.TEXT_NODE; const isElementNode = (node) => node.nodeType === Node.ELEMENT_NODE; const GetNextNodeElements = (i) => { let n = i + 1; let res = []; while (nodelist[n] && isElementNode(nodelist[n])) { res.push(nodelist[n]); n++; } return res; } const GetNextTextNode = (i) => { let n = i + 1; for (let n = i; n < nodelist.length; n++) { if (isTextNode(nodelist[n])) return nodelist[n]; } } for (let i = 0; i < nodelist.length; i++) { let node = nodelist[i]; let opentags = ''; let closetags = ''; if (isTextNode(node) && !nodeHasParents(node)) { obj.annotation.push({"text": node.textContent}); } else if (isElementNode(node)) { opentags += `<${node.nodeName.toLowerCase()}>`; const currentNode = node; const nextNodeElements = GetNextNodeElements(i); if (nextNodeElements) { nextNodeElements.forEach(node => opentags += node.outerHTML.replace(node.textContent, '').replace(`</${node.nodeName.toLowerCase()}>`, '')); nextNodeElements.reverse(); nextNodeElements.forEach(node => closetags += `</${node.nodeName.toLowerCase()}>`); i = i + nextNodeElements.length; node = nodelist[i]; } if (!!closetags.length) { closetags = `</${currentNode.nodeName.toLowerCase()}>` + closetags; } else closetags += `</${currentNode.nodeName.toLowerCase()}>` obj.annotation.push({"markup": opentags}); obj.annotation.push({"text": GetNextTextNode(i)?.textContent}); obj.annotation.push({"markup": closetags}); } } console.log(obj.annotation);
请求协助
需要修正代码逻辑,解决闭合标签位置错误的问题,确保元素标签能正确包裹内部所有内容,生成符合预期结构的对象数组。
内容的提问来源于stack exchange,提问作者kosmosan
相关产品推荐
相关产品推荐

