不能用 innerHTML.replace() 做高亮,因其会破坏 DOM、引发 XSS、错包标签、正则元字符报错、中文切词错误及生成非法 HTML;必须仅操作文本节点,双重转义关键词,用 createElement('mark') 替换并 normalize() 合并节点;滚动优化需只处理首个可见匹配项并防抖。

mark 元素是唯一语义正确、浏览器原生支持的高亮方式,但直接用字符串替换 innerHTML 会破坏 DOM、引入 XSS 风险、错包标签——必须只操作文本节点。
为什么不能用 innerHTML.replace() 做高亮
用户搜 abc abc"<script>alert(1)</script>",或关键词含 .?[ 等正则元字符,字符串拼接立刻出事;中文场景下还容易把“JavaScript教程”错切成“JavaScript教程”。更隐蔽的问题是: 被替换成 看似正常,但若再搜 "p",就会产出非法 HTML:p>abcp>,页面直接白屏。
- 所有匹配必须限定在
Node.TEXT_NODE内,跳过script、style、注释等节点 - 关键词需双重转义:先
escapeHtml()防 XSS,再escapeRegex()防正则崩溃 - 匹配后用
document.createElement('mark')包裹,再调用node.parentNode.replaceChild()替换,不拼字符串 - 每次替换后立即执行
node.parentNode.normalize(),合并相邻文本节点,避免重复高亮
滚动到首个匹配项时卡顿怎么办
一次搜索可能生成几十个 mark 元素,如果对每个都调用 getBoundingClientRect() 再 scrollIntoView(),低配设备上明显卡顿。更稳妥的做法是只处理第一个可见项,并加防抖。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 用
document.querySelectorAll('mark')拿到全部节点,取[0] - 检查该节点是否在视口内:
rect.top >= 0 && rect.bottom - 不在视口内才调用
element.scrollIntoView({ block: 'center', behavior: 'smooth' }) - 若搜索是实时触发(如
input事件),必须节流scrollIntoView调用,避免高频滚动抖动
如何让高亮样式在所有环境下稳定生效
浏览器默认的 mark 样式在阅读模式、邮件客户端、深色主题、Windows 高对比度模式下大概率被忽略或重置,不能依赖 UA 默认值。
- 必须显式声明 CSS:
mark { background-color: #ffeb3b; color: #212121; } - 英文场景建议加单词边界:
new RegExp(`\b(${safeKeyword})\b`, 'gi'),避免cat匹配到education - 中文无天然空格,不加
\b,但要注意全角/半角一致性(用户搜“Vue”,页面写“vue”就匹配不到) - 多关键词时按长度从长到短排序再匹配,防止“Script”截断“JavaScript”
最易被忽略的是:首次加载时若 URL 带 ?q=xxx 参数,必须在 DOM 加载完成后立即执行高亮 + 滚动逻辑,否则用户看到的是未高亮的原始页面;且 IntersectionObserver 或 scroll 监听器都得手动触发一次初始状态同步,不然导航栏固定定位下首屏标题常被遮挡。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










