当正则表达式遇到 DOM(突然变得不再简单)
发布: (2026年2月26日 GMT+8 18:03)
4 分钟阅读
原文: Dev.to
Source: Dev.to

目标
- 支持多词查询
- 优先匹配完整短语
- 回退到单个 token 匹配
- 在 DOM 中高亮结果
- 跳过
和块
脑中想法:“很简单。直接写个正则就行。”
第一步:构建正则
如果用户搜索:
power shell
我会生成如下模式:
power[\s\u00A0]+shell|power|shell
逻辑
- 首先尝试匹配完整短语。
- 若失败,则匹配单个 token。
纸面上看很简洁,单独使用时也能工作。
第二步:进入 DOM
现在问题从纯字符串匹配转向 DOM 遍历。
任务
- 遍历 DOM,同时避免 UI 元素。
- 跳过
,,,块。 - 保持语法高亮。
- 只替换文本节点,保持 DOM 结构完整。
TreeWalker 可以完成这项工作:
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const p = node.parentElement;
if (!p) return NodeFilter.FILTER_REJECT;
if (p.closest("code, pre, script, style")) {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
},
});
现在我们不只是应用正则,而是进行受控的 DOM 变更。
第三步:交替问题
即使短语出现在交替的第一位:
phrase|token1|token2
引擎仍然会根据上下文愉快地匹配单个 token(power、shell、PowerShell)。
面临的挑战
- 重叠匹配
- 执行顺序
- 重置
lastIndex - 防止双重变更
- 防止嵌套 “ 元素
第四步:两遍处理?
我考虑将过程拆分:
- 尝试匹配短语。
- 若未找到,再尝试 token 匹配。
听起来很简单——直到你意识到第一次遍历可能已经改变了 DOM,需要在两遍之间管理状态。
领悟
- 正则问题单独来看很容易。
- DOM 变更问题单独来看也很容易。
- 两者结合后复杂度呈指数增长。
“简单功能”和“迷你搜索引擎”之间的界限非常微妙。
当前进展
- 搜索大多数情况下能工作。
- 高亮已应用。
- 受保护的块已被跳过。
- 结构得到尊重。
它还不是完整的浏览器级 Ctrl + F,但核心功能已经具备。
我现在对 DOM 的尊重远高于以前,也深刻体会到让 JavaScript 逻辑在活跃的 DOM 树中可预测运行才是真正的挑战。
收获
- 正则是确定性的;DOM 是结构化且有状态的。
- 一旦开始替换文本节点,一切都会变得脆弱。
- 边缘情况、状态管理以及稳健的变更处理是实现真正可靠功能的关键。