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

如何解决双引号内文本着色因<br>标签导致的兼容问题?

解决双引号内文本着色的换行兼容与未闭合引号问题

问题分析

你遇到的核心问题有两个:

  1. 直接操作innerHTML处理引号着色时,会破坏<br>等HTML标签结构,尤其是在引号不规范(缺失闭合引号)时,错误范围会扩大。
  2. 拆分<br>单独处理后,正则只能匹配成对引号,无法处理段落内仅开头有引号、无闭合引号的场景。

解决方案

方案1:仅处理单文本节点内的引号(不跨元素)

该方案直接操作文本节点,避免破坏HTML标签,同时支持成对引号和未闭合到文本节点末尾的引号:

// 匹配开头引号,以及后续内容到闭合引号或文本末尾
const quoteRegex = /(["“”])([\s\S]*?)(?=["“”]|$)/g;

function colorTextNodes(node) {
    // 跳过脚本标签
    if (node.nodeName === "SCRIPT") return;

    // 处理文本节点
    if (node.nodeType === Node.TEXT_NODE) {
        const text = node.textContent;
        if (!/["“”]/.test(text)) return;

        const fragment = document.createDocumentFragment();
        let lastIndex = 0;
        let match;

        while ((match = quoteRegex.exec(text)) !== null) {
            const [_, startQuote, content] = match;
            const matchStart = match.index;

            // 添加匹配前的普通文本
            if (matchStart > lastIndex) {
                fragment.appendChild(document.createTextNode(text.slice(lastIndex, matchStart)));
            }

            // 添加开头引号
            fragment.appendChild(document.createTextNode(startQuote));

            // 着色引号内的内容
            if (content.trim()) {
                const redSpan = document.createElement("span");
                redSpan.style.color = "red";
                redSpan.textContent = content;
                fragment.appendChild(redSpan);
            }

            // 检查并添加闭合引号(如果存在)
            const nextChar = text[quoteRegex.lastIndex];
            if (nextChar && ["\"", "“", "”"].includes(nextChar)) {
                fragment.appendChild(document.createTextNode(nextChar));
                quoteRegex.lastIndex++;
            }

            lastIndex = quoteRegex.lastIndex;
        }

        // 添加剩余未处理的文本
        if (lastIndex < text.length) {
            fragment.appendChild(document.createTextNode(text.slice(lastIndex)));
        }

        // 替换原文本节点
        node.parentNode.replaceChild(fragment, node);
    } else {
        // 递归处理子元素
        Array.from(node.childNodes).forEach(child => colorTextNodes(child));
    }
}

// 执行着色
colorTextNodes(document.querySelector("chapter"));

方案2:支持跨元素的引号(含
)

如果需要处理跨<br>或其他元素的连续引号(比如引号在<br>前开始,<br>后结束),可以通过跟踪引号状态实现:

const quoteChars = new Set(["\"", "“", "”"]);

function colorTextWithQuoteState(node, inQuote = false, currentSpan = null) {
    if (node.nodeName === "SCRIPT") return;

    if (node.nodeType === Node.TEXT_NODE) {
        const text = node.textContent;
        let lastIndex = 0;
        const fragment = document.createDocumentFragment();

        for (let i = 0; i < text.length; i++) {
            const char = text[i];

            if (quoteChars.has(char)) {
                // 处理当前字符前的文本
                if (i > lastIndex) {
                    const segment = text.slice(lastIndex, i);
                    if (inQuote && currentSpan) {
                        currentSpan.textContent += segment;
                    } else {
                        fragment.appendChild(document.createTextNode(segment));
                    }
                }

                // 切换引号状态
                inQuote = !inQuote;
                fragment.appendChild(document.createTextNode(char));

                // 进入引号时创建着色span,退出时重置
                if (inQuote) {
                    currentSpan = document.createElement("span");
                    currentSpan.style.color = "red";
                    fragment.appendChild(currentSpan);
                } else {
                    currentSpan = null;
                }

                lastIndex = i + 1;
            }
        }

        // 处理剩余文本
        if (lastIndex < text.length) {
            const segment = text.slice(lastIndex);
            if (inQuote && currentSpan) {
                currentSpan.textContent += segment;
            } else {
                fragment.appendChild(document.createTextNode(segment));
            }
        }

        node.parentNode.replaceChild(fragment, node);
        return { inQuote, currentSpan };
    } else {
        // 递归传递引号状态处理子元素
        let currentState = { inQuote, currentSpan };
        Array.from(node.childNodes).forEach(child => {
            currentState = colorTextWithQuoteState(child, currentState.inQuote, currentState.currentSpan);
        });
        return currentState;
    }
}

// 执行着色
colorTextWithQuoteState(document.querySelector("chapter"));

方案优势

  • 避免直接操作innerHTML导致的HTML结构破坏,<br>等标签完全不受影响。
  • 同时支持成对引号和未闭合引号的场景。
  • 方案2额外支持跨元素的连续引号着色,覆盖更复杂的文本格式。

内容的提问来源于stack exchange,提问作者bobby324

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:54:51