前言
前端实现高亮文本的方法很多,本文主要介绍两种方法:标签式、覆盖式。
本文参考代码点击这里
标签式
如图所示,标签式主要是通过对高亮的文本添加标签,给标签增加样式实现高亮。
这种方法的难点是:不能简单的通过String.prototype.repeat
替换高亮文本,因为在实际开发中,可能我们想高亮的词在页面代码中是有包含有其他标签,或是高亮的文本刚好是一些标签的关键字(a、 span......),这时候repeat
便显得比较无力。所以实现关键点是必须在dom树中解析,利用dom的api逐个节点提取节点内容,这样就能避免匹配到html标签。遇到匹配文本跨节点时,我们应该把在各个节点中包含的关键词分别用标签包装起来。
从上面可以看出,该方案主要可以分为两步:
-
解析dom,判断节点内容是否包含需要高亮的文本。
这里我们主要
Node.prototype.textContent
和String.prototype.indexOf
获取关键字所在的文本的位置。这里采用递归insertTag不断的查找节点中的需要高亮的内容。
-
替换需要高亮的文本节点。
替换需要高亮文本节点核心是分离原来没有加高亮的标签的文本,这里用的是
Text.prototype.splitText
方法把原来文本节点中的需要高亮词切分开来。再通过构造包含相同文本的高亮标签替换上。
以上便是标签式高亮的实现方法。
覆盖式
覆盖式就是通过计算出高亮词在页面的的位置,创建出带有高亮的样式的标签覆盖在高亮词的上(下)面。
覆盖式高亮主要的难点:
-
如何定位:
在文字流中定位其实就是确定高亮点的起始点和结束点的位置信息,通过位置信息计算出插入高亮标签的大小和位置。
这里通过循环在每个需要高亮词的前面后面插入Range,标记出入高亮词的位置。
如图所示,通过循环遍历兄弟节点,通过递归查找子节点。
-
如何处理高亮词跨行问题:
当我们遇到过高亮的文本跨行时该怎么办呢?高亮的区域变成一个不规则的形状,如果小时候搭过积木的话,这个问题就变得简单,像这样的形状我们可以把它看成3块长方形的积木拼接起来,头一行为一块积木,中间若干行(如果有)为另一块积木,剩下的最后一行是一块积木。
使用
getBoundingClientRect
方法便可计算出高亮长方形的长、宽、横坐标、纵坐标。对比起点和终点的纵坐标便可以得出高亮文本的形状。
总结
最后简单对比一下标签式和覆盖式的特点:
- 标签式:主要优点是作用于文本中,可以对高亮内容的样式多样化定制,可以跨段落高亮,缺点是插入逻辑复杂,重新高亮文本成本高,顺序定位高亮文本难(上一个高亮文本、下一个高亮文本...)。
- 覆盖式:主要作用于文本表面,优点忽略dom本身的结构,方便顺序定位高亮文本,容易重新高亮,由于是通过定位覆盖,所以很容易受到外界环境(定位点变化,文本位置变化,行高变化...)的影响,跨段落高亮较难。
文笔粗糙勿喷,如有错误或更多高亮解决方案可以提出来交流,谢谢阅读。