如何基于无HTML原始串索引在含HTML串中精准包裹指定子串?
实现纯文本区间在带HTML标签字符串中的精准样式包裹
核心思路
要解决这个问题,关键是对齐纯文本(rawStr)和带标签文本(htmlStr)的字符位置:先从htmlStr中提取纯文本,和rawStr匹配后,定位目标区间在htmlStr纯文本中的位置;再遍历htmlStr,区分标签与文本内容,跳过标签的同时,将目标区间内的文本用指定样式的<span class="dsa">包裹,保留原有HTML标签。
实现步骤
- 提取htmlStr的纯文本:过滤掉所有HTML标签,得到和rawStr一致的纯文本内容,用于位置对齐。
- 定位目标区间:根据rawStr的
startIndex和endIndex,确定目标文本在htmlStr纯文本中的起始(targetStart)和结束(targetEnd)位置。 - 遍历处理htmlStr:使用状态机模式,标记当前是否处于HTML标签内;同时记录当前处理到的纯文本位置,当位置落在
[targetStart, targetEnd)区间时,对文本字符进行包裹,遇到标签则直接保留。
代码实现
function wrapTargetInHtml(rawStr, htmlStr, startIndex, endIndex) { // 提取htmlStr的纯文本内容 const htmlPlain = htmlStr.replace(/<[^>]+>/g, ''); // 校验纯文本一致性(可选,确保输入合法) if (htmlPlain !== rawStr) { throw new Error('rawStr与htmlStr的纯文本内容不匹配'); } const targetLength = endIndex - startIndex; if (targetLength <= 0) return htmlStr; let result = ''; let inTag = false; let plainCharCount = 0; let inWrap = false; for (let i = 0; i < htmlStr.length; i++) { const char = htmlStr[i]; // 处理HTML标签的开始 if (char === '<') { // 若正在包裹文本,先关闭span if (inWrap) { result += '</span>'; inWrap = false; } inTag = true; result += char; continue; } // 处理HTML标签的结束 if (char === '>') { inTag = false; result += char; continue; } // 处于标签内,直接追加字符 if (inTag) { result += char; continue; } // 处理纯文本字符 const isInTarget = plainCharCount >= startIndex && plainCharCount < endIndex; // 进入目标区间且未开启包裹时,打开span if (isInTarget && !inWrap) { result += '<span class="dsa" style="background-color:red">'; inWrap = true; } // 离开目标区间但处于包裹状态时,关闭span else if (!isInTarget && inWrap) { result += '</span>'; inWrap = false; } result += char; plainCharCount++; } // 遍历结束后,若仍处于包裹状态则关闭span if (inWrap) { result += '</span>'; } return result; } // 测试示例 const rawStr = " paramount 13.11 nowe soxxxxxx 23 diablo 13.11 .twisted sister 13.11 special 13.45 way"; const htmlStr = " paramount 13.11 nowe soxxxxxx 23 diablo 13<span class='mark' style='background: lime'>.</span>11 .twisted sister 13.11 special 13.45 way"; // 定位rawStr中第二个"13.11"的索引 const startIndex = rawStr.indexOf("13.11", rawStr.indexOf("13.11") + 1); const endIndex = startIndex + 5; // "13.11"长度为5 const processedHtml = wrapTargetInHtml(rawStr, htmlStr, startIndex, endIndex); console.log(processedHtml);
测试结果
运行上述代码后,输出的processedHtml与期望结果完全一致:
" paramount 13.11 nowe soxxxxxx 23 diablo <span class='dsa' style='background-color:red'>13</span><span class='mark' style='background: lime'>.</span><span class='dsa' style='background-color:red'>11</span> .twisted sister 13.11 special 13.45 way"
内容的提问来源于stack exchange,提问作者Travis
相关产品推荐
相关产品推荐

