标签需手动插入文本中才能生效,不能自动匹配;须遍历文本节点用正则替换并转义特殊字符,注意避免xss、样式覆盖及可访问性问题。

为什么直接用 <mark></mark> 包裹关键词会失效
因为 <mark></mark> 是纯语义标签,它不会自动匹配或高亮文本中的子字符串——你得自己把原始文本切开、插入 <mark></mark>,再拼回去。常见错误是写成 <p>搜索结果<mark>关键词</mark></p>,但实际 DOM 里根本没出现“关键词”二字,只是硬编码进去的。
用 JavaScript 动态替换文本节点中的关键词
核心思路:遍历目标元素内的所有文本节点,对每个节点内容做 String.prototype.replace(),把关键词替换成带 <mark></mark> 的 HTML 片段,再用 Node.replaceWith() 更新 DOM。注意必须用 textContent 做匹配(避免误操作已有标签),且要转义正则特殊字符。
实操建议:
- 用
RegExp.escape(需自行补丁)或keyword.replace(/[.*+?^${}()|[]\]/g, '\$&')处理关键词中的正则元字符 - 区分大小写?加
i标志;只匹配完整词?加边界断言 - 避免重复高亮:替换前先检查节点是否已含
<mark></mark>,或用document.querySelectorAll('mark')清理旧标记 - 示例片段:
function highlight(textNode, keyword) {<br> const escaped = keyword.replace(/[.*+?^${}()|[]\]/g, '\$&');<br> const regex = new RegExp(escaped, 'gi');<br> const highlighted = textNode.textContent.replace(regex, '<mark>$</mark>');<br> const temp = document.createElement('div');<br> temp.innerHTML = highlighted;<br> textNode.replaceWith(...temp.childNodes);<br>}Vue/React 等框架中避免 dangerouslySetInnerHTML
在 Vue 中,不要用
v-html直接插渲染后的 HTML 字符串,否则会丢失响应式;React 同理,别滥用dangerouslySetInnerHTML。应该把高亮逻辑下沉到 computed 或 useMemo,生成带<mark></mark>的 VNode 或 ReactNode 数组。实操建议:
- Vue 3 Composition API:用
computed(() => splitText(content, keyword))返回[{type: 'text', text: 'xxx'}, {type: 'mark', text: 'key'}]结构,模板中v-for渲染 - React:用
useMemo拆分字符串,返回fragments或span数组,每个<mark></mark>单独包裹 - 关键点:保留原始文本节点结构,不破坏父容器事件绑定和 CSS 选择器作用域
样式冲突与可访问性注意事项
浏览器默认给
<mark></mark>加黄色背景,但很多项目会全局重置mark { background: unset; },导致高亮消失。另外,纯色块对色觉障碍用户不友好,且屏幕阅读器默认不特别播报<mark></mark>。实操建议:
- 显式设置
mark { background-color: #ffeb3b; color: #212121; padding: 0 2px; },避免被 CSS reset 覆盖 - 加
role="mark"和aria-label(如aria-label="高亮关键词:xxx")提升可访问性 - 慎用
mark::before或mark::after伪元素——它们可能干扰文本流和复制行为 - 移动端双击选中时,
<mark></mark>内容容易被截断,建议用user-select: text显式声明
- Vue 3 Composition API:用
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











