浏览器中搜索与高亮文本

在前段时间开发中, 遇到了搜索和高亮文本的需求. 希望开发一个通用的基于html的搜索与高亮模块,
不但能够应用在编辑器中, 而且也能在普通的HTML页面中使用.

普通HTML页面思路

于是看了看市面上的一些搜索与高亮插件, 如
mark.js 和 一些文章, 如纯客户端页面关键字搜索高亮jQuery插件 张鑫旭-鑫空间-鑫生活, 发现思路基本如下:

  1. 收集搜索目标容器的innerHTML
  2. 对innerHtml字符串进行处理, 如关键字正则匹配
  3. 将处理后的html更新至目标容器的innerHTML

mark.js 做得得更精细些, 但也会修改搜索区域的DOM结构 – 这是我不希望看到的.

个人希望搜索与高亮模块能够满足:

  1. 像浏览器自带的搜索功能(window.find) 那样, 高亮搜索关键字且不修改搜索区域的DOM结构
  2. 能够指定搜索区域的容器, 而不是整个页面(window.find 搜索整个页面, 无法指定搜索范围)

编辑器思路

富文本编辑器中开发搜索与高亮文本时, 一般使用 Selection/Range 对象来高亮文本.

如 CKEditor, UEditor 都是这么做的.
浏览器中搜索与高亮文本_第1张图片

如上图, apollo 是搜索关键字, 高亮的Apollo 是使用 Selection/Range 来完成的.

这样做的好处是:

  • 不修改搜索区域的DOM结构

缺陷是:

  • 只能同时显示一个结果, 多个结果时, 只能切换显示.

Safari 浏览器的搜索与高亮

Safari 浏览器的搜索功能挺有意思(如下图).
浏览器中搜索与高亮文本_第2张图片

仔细观察, 且猜测其如何实现会发现:

  1. 搜索与高亮的整个页面添加了一个蒙板, 覆盖在页面上方
  2. 蒙板遇到关键字区域, 开洞露出关键字, 从而产生高亮效果

可以猜想: 这种实现应该也没有修改搜索区域的DOM结构.

新思路

以上搜索与高亮的实现思路各不相同, 能否综利用以满足如下要求呢?

  1. 搜索高亮
  2. 不修改搜索区域的DOM结构

经过一番思考和coding, 发现以下思路可行:

  1. 深度优先遍历&收集搜索区域(container)下所有的textNode节点的相关信息, 并保存在一个数组中.
  2. 将数组中所有textNode节点的value 拼凑一个大字符串
  3. 搜索关键字时, 在大字符串中搜索, 并记录关键字在大字符串中的出现位置信息(start, end)
  4. 将关键字在大字符串的出现位置信息(start, end) 映射成关键字在数组中的textNode节点上的开头结尾信息(start, end)
  5. 根据关键字在数组中的textNode节点上的开头结尾信息(start, end), 创建刚好包含关键字的 range (参考 https://developer.mozilla.org/en-US/docs/Web/API/range)
  6. 使用 range 的 getClientRects() 获取关键字在窗口中的位置信息(top, left, width, height)数组, 并变换成关键字在页面中的位置信息(top, left, width, height)数组.
  7. 根据位置信息(top, left, width, height)数组, 在页面中添加半透明高亮层, 使用绝对定位绘制, 使之悬浮在关键字上方.

以下是搜索 underscor, 总书记 时的效果图:

浏览器中搜索与高亮文本_第3张图片

如果再优化一下界面, 感觉能实现类似 Safari 浏览器的搜索与高亮效果.

与浏览器原生的搜索与高亮( window.find ) 相比, 这种思路的缺陷是:

  • 无法高亮 ,