是唯一语义正确、开箱即用的高亮方案,但需显式声明背景色与文字色以保障可访问性,且动态高亮必须用treewalker或range api安全操作dom,避免xss和结构破坏。

<mark></mark> 是唯一语义正确、开箱即用的方案,别用 <span></span> 加 background-color 临时凑合——它不传达“这段文字与上下文强相关”,屏幕阅读器不会提示,SEO 也无额外信号。
为什么直接写 <mark>关键词</mark> 还是黄底看不清?
浏览器默认黄色在暗色模式、高对比度设置、邮件客户端里基本失效。不写 CSS 就上线,等于放弃可访问性。
- 必须显式声明
background-color和color,不能依赖默认值 - 建议加
padding: 2px 4px,避免文字紧贴边缘 - 深色模式要手动适配:
@media (prefers-color-scheme: dark) { mark { background-color: #ffca28; color: #121212; } } - 别写
background: none——它不清除background-image,应写background-color: transparent
动态高亮关键词时,innerHTML.replace() 为什么总出错?
这是最危险的写法:用户搜 [a-z],正则没转义 → 脚本中断;搜 <script>alert(1)</script> → XSS;原文含 <code>console.log() → 替换后 <code>console.<mark>log</mark>(),<mark></mark> 被撕开,DOM 解析失败。
- 必须只操作文本节点,用
document.createTreeWalker(element, NodeFilter.SHOW_TEXT) - 关键词要做双重转义:HTML 实体(
&→&) + 正则元字符(.→\.) - 匹配后创建新
mark元素:const mark = document.createElement('mark'); mark.textContent = match;,再用node.parentNode.replaceChild(mark, node) - 中文关键词记得加
u标志:new RegExp(pattern, 'giu')
用 Range API 高亮用户选中的文字,比正则更稳
正则替换无法区分“用户拖选的‘abc’”和“其他位置独立出现的‘abc’”,还容易跨标签切开 HTML 结构。而 window.getSelection() 拿到的是真实选区,Range.surroundContents() 可原位包裹,零风险。
- 获取选区:
const selection = window.getSelection(); if (selection.rangeCount === 0) return; - 取首个范围:
const range = selection.getRangeAt(0); - 创建并包裹:
const mark = document.createElement('mark'); range.surroundContents(mark); - 注意:若选区跨多个节点(如含
<strong></strong>),surroundContents会抛错,需先range.cloneContents()再重构
真正难的不是怎么写 <mark></mark>,而是决定“谁该被标记”——语义边界模糊时(比如关键词嵌套、部分匹配、HTML 实体混杂),Range 或 TreeWalker 才是兜底手段;样式只是表层,可访问性和 DOM 安全才是容易被忽略的硬伤。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











