本文发布于个人网站:https://wintc.top/article/59,转载请注明

很久之前(好像刚好是一年前)写过一个Vue组件,可以匹配文本内容中的关键词高亮,类似浏览器ctrl+f搜索结果。实现方案是,将文本字符串中的关键字搜索出来,然后使用特殊的标签(本文示例使用font标签)包裹关键词替换匹配内容,最后得到一个HTML字符串,渲染该字符串并在font标签上使用CSS样式即可实现高亮的效果。

当时的实现过于简单,没有支持接收HTML字符串作为内容进行关键词匹配。这两天有同学问到,就又思考了这个问题,发现并不是那么麻烦,写了几行代码解决一下。

一、匹配关键字:HTML字符串与文本字符串对比

1. 纯文本字符串的处理

对于纯文本字符串,如:“江畔何人初见月?江月何年初照人? ”,假如我们想匹配“江月”这个关键字,则匹配结果可处理为:江畔何人初见月?江月何年初照人?

这样“江月”两个字被font标签包裹,在font标签上应用特殊的背景样式以达到关键字高亮的效果。

2. 对HTML字符串的处理

对于上述例子,如果内容字符串是一个HTML文本:江畔何人初见?江何年初照人?

对于同样的关键词“江月”,怎样处理它呢?因为关键词中的字在不同的标签内,所以只能分别用font标签进行替换:江畔何人初见?江何年初照人?

这是比较简单的情况,实际情况下关键字则可能跨多级、多层标签。

二、跨标签匹配关键词

跨标签解析关键词,其实就是对于匹配到的关键词,提取出各标签中对应的子片段,然后用font之类的标签包裹,再将高亮样式用于font标签即可。

对于整个HTML内容而言,渲染出来的文本由各类标签内的文本节点组成。因为关键词匹配的内容会跨标签,所以需要将各文本节点有序取出,并将节点内容拼接起来进行匹配。拼接时记下节点文本在拼接串中的起止位置,以便关键词匹配到拼接串的某位置时截取文本片段并使用font标签包裹。

1. 深度优先遍历DOM树取出文本节点

深度优先可以采用循环或者递归的方式遍历,这里采用循环实现,按取出某个元素下所有文本节点(利用nodeType判断文本节点):function getTextNodeList (dom) {

const nodeList = [...dom.childNodes]

const textNodes = []

while (nodeList.length) {

const node = nodeList.shift()

if (node.nodeType === node.TEXT_NODE) {

textNodes.push(node)

} else {

nodeList.unshift(...node.childNodes)

}

}

return textNodes

}

2. 取出所有文本内容进行拼接

获取到了文本节点列表,可以取出所有文本内容并记录每个文本片段在拼接结果中的开始、结束索引:getTextInfoList (textNodes) {

let length = 0

const textList = textNodes.map(text => {

let start = length, end = length + text.wholeText.length

length = end

return [text.wholeText, start, end]

})

return textList

}

拼接文本:const content = textList.map(([text]) => text).join('')

3. 匹配关键词

获得了拼接文本,可以利用拼接文本获取所有的拼接结果了。这里偷个懒直接用正则匹配吧,得把正则用到的一些特殊符号进行转义一下:getMatchList (content, keyword) {

const characters = [...'[]()?.+*^${}:'].reduce((r, c) => (r[c] = true, r), {})

keyword = keyword.split('').map(s => characters[s] ? `${s}` : s).join('[sn]*')

const reg = new RegExp(keyword, 'gmi')

return [...content.matchAll(reg)] // matchAll结果是个迭代器,用扩展符展开得到数组

}

关键词字符转义处理后,字符与字符之间中间插入了正则中的空白符和换行符(sn),以在匹配时忽略一些看不见的字符。上述代码使用了matchAll函数,匹配结果展开后得到的结果是一个数组,数组中的每一项都包含了匹配文本、匹配索引等。matchAll的一个简单例子:

4. 关键词使用font标签替换

根据关键词匹配结果索引,以及每个文本节点的起止索引,可以计算出每个关键词匹配了哪几个文本节点,其中对于开始和结束的文本节点,可能只是部分匹配到,而中间的文本节点的所有内容都是匹配到的。

比如对于HTML文本:江畔何人初见?江月何年初照人?

其DOM树对应的的文本节点有3个:

假如关键字是“何人初见月?”,那此时,对于第一个文本节点匹配了后半部分,第二个文本节点完全匹配,第三个文本节点匹配了第一个字符。三个节点中匹配的部分需要分别用font标签替换:江畔何人初见?江月何年初照人?

默认情况下,连续的文字会在同一个文本节点中,而对于匹配了部分内容的文本节点,就需要将它一分为二,可以利用Text.splitText()")API来分割文本节点,API接收一个索引值,从索引位置将文本节点后半部分切割并返回包含后半部分内容的新文本节点。上述例子中匹配的是3个节点,拆分后就会得到5个文本节点:

中间三个文本节点即是需要被替换的节点,使用replaceChild就可以直接将文本节点替换为font标签。

对于整个HTML字符串,同一个关键词可能同时有多处匹配结果,因此要对所有匹配结果进行上述处理。使用前几步获取的textNodes、textList、matchList,代码实现如下:function replaceMatchResult (textNodes, textList, matchList) {

// 对于每一个匹配结果,可能分散在多个标签中,找出这些标签,截取匹配片段并用font标签替换出

for (let i = matchList.length - 1; i >= 0; i--) {

const match = matchList[i]

const matchStart = match.index, matchEnd = matchStart + match[0].length // 匹配结果在拼接字符串中的起止索引

// 遍历文本信息列表,查找匹配的文本节点

for (let textIdx = 0; textIdx < textList.length; textIdx++) {

const { text, startIdx, endIdx } = textList[textIdx] // 文本内容、文本在拼接串中开始、结束索引

if (endIdx < matchStart) continue // 匹配的文本节点还在后面

if (startIdx >= matchEnd) break // 匹配文本节点已经处理完了

let textNode = textNodes[textIdx] // 这个节点中的部分或全部内容匹配到了关键词,将匹配部分截取出来进行替换

const nodeMatchStartIdx = Math.max(0, matchStart - startIdx) // 匹配内容在文本节点内容中的开始索引

const nodeMatchLength = Math.min(endIdx, matchEnd) - startIdx - nodeMatchStartIdx // 文本节点内容匹配关键词的长度

if (nodeMatchStartIdx > 0) textNode = textNode.splitText(nodeMatchStartIdx) // textNode取后半部分

if (nodeMatchLength < textNode.wholeText.length) textNode.splitText(nodeMatchLength)

const font = document.createElement('font')

font.innerText = text.substr(nodeMatchStartIdx, nodeMatchLength)

textNode.parentNode.replaceChild(font, textNode)

}

}

}

代码里对匹配结果遍历时,采用的是倒序遍历,原因是遍历过程对textNodes存在副作用:在遍历中会对textNodes中的文本节点进行切割。假设同一个文本节点中有多处匹配,会进行多次分割,而textNodes里引用的是原文本节点即前半部分,因此从后往前遍历会确保未处理的匹配文本节点的完整。

同时代码中省去了font节点的样式设置,这个可以根据自己的逻辑来设置。

三、完整代码调用

上述步骤描述了HTML字符串跨标签匹配关键词的所有流程实现,下面是完整的代码调用示例:function replaceKeywords (htmlString, keyword) {

if (!keyword) return htmlString

const div = document.createElement('div')

div.innerHTML = htmlString

const textNodes = getTextNodeList(div)

const textList = getTextInfoList(textNodes)

const content = textList.map(({ text }) => text).join('')

const matchList = getMatchList(content, keyword)

replaceMatchResult(textNodes, textList, matchList)

return div.innerHTML

}

输入一个HTML字符串和关键词,将HTML串中的关键词用font标签包裹后返回。

四、总结

上述实现方案中有一些简单的细节省去了,比如设置font标签的样式、隐藏的dom匹配时忽略等。

font标签样式设置看使用场景吧,如果是长HTML字符串匹配建议是不要直接设置style属性,而是操作样式表来达到目的。可以给font标签设置特殊的属性,然后使用属性选择器来设置样式。比如可以给font设置highlight="${i}"属性,来针对匹配的关键词应用不同的样式。操作样式表可以给style标签设置innerText或者调用CSSStyleSheet.insertRule()")和CSSStyleSheet.deleteRule()")。

demo: https://wintc.top/laboratory/#/search-highlight

github查看源码:https://github.com/Lushenggang/vue-search-highlight

码代码五分钟,写博客两小时....

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[HTML字符串跨标签匹配关键词高亮]http://www.zyiz.net/tech/detail-149293.html

html5 关键词,HTML字符串跨标签匹配关键词高亮相关推荐

  1. 关键词高亮:HTML字符串中匹配跨标签关键词

    关注 前端瓶子君,回复"交流" 加入我们一起学习,天天进步 来源:木马啊 转载自:https://wintc.top/article/59 很久之前写过一个Vue组件,可以匹配文本 ...

  2. 提升网站关键词排名从TDK标签开始

    网站SEO在TDK中意味着什么 "T"表示网站页面中的标题元素,它是首字母缩略词.这里可以使用分词技术.编写标题时,我们应尽量不要对其进行修改.会影响网站的排名等!不应将无意义的关 ...

  3. Java检查字符串包含多个关键词

    Java检查字符串包含多个关键词 本文介绍如何在字符串中监测多个关键词. 1. 需求说明 String inputString = "hello there, java"; 我们的 ...

  4. android关键词检索功能,Android实现搜索关键词高亮显示-Kotlin

    在做Wandroid项目时有一个搜索功能,要在搜索结果中将匹配到的关键词高亮显示.但是 玩安卓API并没有提供颜色的高亮,只有字体斜体,效果看起来并不明显,并且昵称也参与了搜索,但并没有增加HTML标 ...

  5. HTML5学习之语义化标签

    一.为什么HTML5要引入新语义标签 在HTML5出现之前,我们一般采用DIV+CSS布局我们的页面.但是这样的布局方式不仅使我们的文档结构不够清晰,而且不利于搜索引擎爬虫对我们页面的爬取.为了解决上 ...

  6. i58400升级可以换什么cpu_宝贝标题关键词顺序可以换吗?关键词顺序对标题有什么影响?...

    别看宝贝的标题就简简单的30个字,可作用一点也不小,用户在搜索宝贝的时候,系统第一件事情就会去匹配你的标题,再去看其他的数据,人群标签也和宝贝标题息息相关.在这里很多人问宝贝的标题关键词顺序可不可以换 ...

  7. html5表格所有属性,HTML5学习笔记之表格标签

    HTML5学习笔记之表格标签 其他HTML5相关文章 一.表格标签 image 1.作用: 以表格形式将数据显示出来, 当数据量非常大的时候, 表格这种展现形式被认为是最为清晰的一种展现形式 2.格式 ...

  8. seo关键词互点软件报价_SEO关键词优化收费问题和外包报价问题,一文详解

    在竞争日益激烈的市场环境中,企业为了在互联网平台中获得较好的排名,以及实现产品的较好变现,大多数都是使用SEO搜索引擎,因为通过优化关键词可以不断地为用户创造"用户最想得到的"&q ...

  9. 网站关键词优化的细节【长尾关键词的挖掘与筛选】

    一.什么是长尾关键词? 分析网站的搜索流量和关键词,我们发现: 对于一般小型网站,目标关键词带来的流量占网站总搜索流量的绝大部分. 存在于网站目录页.内容页的关键词也会带来流量,但为数不多. 网站上非 ...

最新文章

  1. linux 存储映射lun 给_如何在 Linux 上扫描/检测新的 LUN 和 SCSI 磁盘 | Linux 中国
  2. GPT-3 不够 Open,BigScience 构建开放语言模型,规模小 16 倍
  3. LIGA Stereo:基于双目3D检测的Lidar几何感知表示学习(ICCV2021)
  4. java多线程——饥饿和公平
  5. 模拟电子技术_清华大学华成英教授主讲之符号说明
  6. 使用ASP.NET 2.0提供的WebResource管理资源
  7. python之while循环用法举例,break与continue的区别,格式化输出及运算符
  8. 浙江大学计算机保研条件_看了就想录取的简历长啥样?保研简历千万别踩这些坑!...
  9. 探秘Hadoop生态12:分布式日志收集系统Flume
  10. [置顶]       Jquery为单选框checkbox绑定单击事件
  11. controller层没反应_埋地管道防腐层探测检漏仪FJ-10地下管线探测仪的说明及应用...
  12. 掌握这些!让Python不再从入门到放弃,初学者容易忽略的一些细节
  13. UE4学习-使用蓝图进行一个关卡的设计
  14. Nginx 0.7.x + PHP 5.2.6(FastCGI)搭建高性能web服务器
  15. 如何高效工作,享受品质生活?看看少数派的经验总结吧
  16. secureCRT 下载使用
  17. 21天学通C语言-学习笔记(1)
  18. 计算机管理系统论文参考文献,关于计算机系统管理的论文参考文献 计算机系统管理论文参考文献哪里找...
  19. 15针VGA公头焊接示意图
  20. VM虚拟机下载安装步骤

热门文章

  1. PHP PDO 连接SQLSErver,php pdo连接sqlserver配置
  2. 39岁研究生毕业14年的经验和教训
  3. Java不免费_Java 11已经不再完全免费,不要陷入Oracle的Java 11陷阱
  4. 一个人值得深交的五种表现
  5. 2010年3月30日德国小红伞杀毒登陆中国,你关心吗?
  6. Avast显示Win32:Evo-gen [Susp]
  7. 也看编程语言ruby的前途
  8. 目标检测——标注图像(超详细步骤)
  9. 极坐标格式算法MATLAB,极坐标格式算法,polar format algorithm,音标,读音,翻译,英文例句,英语词典...
  10. “自动修复“无法修复你的电脑,日志文件:C:\Windows\System32\Logfiles\Srt\SrtTrail.txt