You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 22:05:29