如何解决双引号内文本着色因<br>标签导致的兼容问题?
解决双引号内文本着色的换行兼容与未闭合引号问题
问题分析
你遇到的核心问题有两个:
- 直接操作
innerHTML处理引号着色时,会破坏<br>等HTML标签结构,尤其是在引号不规范(缺失闭合引号)时,错误范围会扩大。 - 拆分
<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
相关产品推荐
相关产品推荐

