JS+CSS实现高亮关键词(不侵入DOM)的方式
之前在做关键词检索高亮功能的时候,研究了下目前前端实现高亮的几种方式,第一就是替换dom
元素实现高亮,第二就是利用浏览器新特性Css.highlights
结合js
选区与光标与CSS
高亮伪类实现,实现功能如下:
一、页面布局
一个搜索框,其余要检索关键词的文字,并且给搜索框添加change
事件
二、实现选区
利用TreeWalker
遍历遍历DOM
结构,通过nextNode()
方法,拿到所有文本节点数据,保存到数组里,为接下来的搜索做铺垫
let wrap = document.querySelector('.content') // 创建 createTreeWalker 迭代器,用于遍历文本节点,保存到一个数组 const treeWalker = document.createTreeWalker(wrap, NodeFilter.SHOW_TEXT); const allTextNodes = []; let currentNode = treeWalker.nextNode(); while (currentNode) { allTextNodes.push(currentNode); currentNode = treeWalker.nextNode(); }
可以看到,到这一步,拿到所有文本节点数据
接下来,我们根据搜索的结果,拿到匹配到的dom
节点内容的位置信息这里处理下英文小写,这里要注意,可能每句话会出现多次关键词,这里的indexOf
要拼开始检索的参数,开始检索的参数这里记录下,也就是上一个检索关键词的结尾
let search = document.getElementById('search') search.onchange = event => { let value = event.target.value // 获取关键词 let indices = []; // 位置信息 const ranges = allTextNodes .map((el) => { // 英文全部小写 return { el, text: el.textContent.toLowerCase() }; }) .map(({ text, el }) => { // 拿到匹配到dom节点的位置信息,并且保存为数组 while (startPos < text.length) { let startPos = 0 while (startPos < text.length) { const index = text.indexOf(value, startPos); if (index === -1) break indices.push({ el, start: index, end: index + value.length }); startPos = index + value.length; } } }); }
我们可以看下位置信息结果
接下来,创建选区,进行高亮,这里用到了Highlight
,用法如下:Highlight(range1, range2, range3)
// 根据搜索词的位置创建选区 let result = indices.map(item => { let range = new Range(); range.setStart(item.el, item.start); range.setEnd(item.el, item.end); return range }); // 创建高亮对象 const searchResultsHighlight = new Highlight(...result); // 注册高亮 CSS.highlights.set("search-results", searchResultsHighlight);
添加css highlight
样式:
.content .item::highlight(search-results) { background-color: #ffff00; color: #000; }
可以看下效果,基本成型,但是当我们切换的时候,上一次的检索结果还在,需要清除高亮记录
// 清除上个高亮 CSS.highlights.clear();
三、完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSS高亮</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .container { width: 100vw; height: 100vh; padding-top: 20px; } .search { width: 100%; padding: 10px 0; display: flex; align-items: center; justify-content: center; } .search input { width: 500px; height: 35px; border: 1px #999 solid; outline: 0; background-color: #fff; border-radius: 3px; padding-left: 10px; } .content { width: 100%; padding: 30px; } .content .item { width: 100%; font-size: 16px; color: #000; margin-bottom: 10px; line-height: 30px; user-select: none; letter-spacing: 0.5px; } .content .item::highlight(search-results) { background-color: #ffff00; color: #000; } </style> </head> <body> <div class="container"> <div class="search"> <input id="search" type="text" placeholder="输入关键词检索" /> </div> <div class="content"> <div class="item"> 1、朱自清《荷塘月色》片段 路上只我一个人,背着手踱着。这一片天地好像是我的;我也像超出了平常旳自己,到了另一世界里。我爱热闹,也爱冷静;爱群居,也爱独处。像今晚上,一个人在这苍茫旳月下,什么都可以想,什么都可以不想,便觉是个自由的人。白天里一定要做的事,一定要说的话,现在都可不理。这是独处的妙处,我且受用这无边的荷香月色好了。 曲曲折折的荷塘上面,弥望旳是田田的叶子。叶子出水很高,像亭亭旳舞女旳裙。层层的叶子中间,零星地点缀着些白花,有袅娜(niǎo,nuó)地开着旳,有羞涩地打着朵儿旳;正如一粒粒的明珠,又如碧天里的星星,又如刚出浴的美人。微风过处,送来缕缕清香,仿佛远处高楼上渺茫的歌声似的。这时候叶子与花也有一丝的颤动,像闪电般,霎时传过荷塘的那边去了。叶子本是肩并肩密密地挨着,这便宛然有了一道凝碧的波痕。叶子底下是脉脉(mò)的流水,遮住了,不能见一些颜色;而叶子却更见风致了。 月光如流水一般,静静地泻在这一片叶子和花上。薄薄的青雾浮起在荷塘里。叶子和花仿佛在牛乳中洗过一样;又像笼着轻纱的梦。虽然是满月,天上却有一层淡淡的云,所以不能朗照;但我以为这恰是到了好处——酣眠固不可少,小睡也别有风味的。月光是隔了树照过来的,高处丛生的灌木,落下参差的斑驳的黑影,峭楞楞如鬼一般;弯弯的杨柳的稀疏的倩影,却又像是画在荷叶上。塘中的月色并不均匀;但光与影有着和谐的旋律,如梵婀(ē)玲(英语violin小提琴的译音)上奏着的名曲。 荷塘的四面,远远近近,高高低低都是树,而杨柳最多。这些树将一片荷塘重重围住;只在小路一旁,漏着几段空隙,像是特为月光留下的。树色一例是阴阴的,乍看像一团烟雾;但杨柳的丰姿,便在烟雾里也辨得出。树梢上隐隐约约的是一带远山,只有些大意罢了。树缝里也漏着一两点路灯光,没精打采的,是渴睡人的眼。这时候最热闹的,要数树上的蝉声与水里的蛙声;但热闹是它们的,我什么也没有。 </div> <div class="item"> 2、鲁迅《从百草园到三味书屋》片段 不必说碧绿的菜畦,光滑的石井栏,高大的皂荚树,紫红的桑椹;也不必说鸣蝉在树叶里长吟,肥胖的黄蜂伏在菜花上,轻捷的叫天子(云雀)忽然从草间直窜向云霄里去了。单是周围的短短的泥墙根一带,就有无限趣味。油蛉在这里低唱,蟋蟀们在这里弹琴。翻开断砖来,有时会遇见蜈蚣;还有斑蝥,倘若用手指按住它的脊梁,便会拍的一声,从后窍喷出一阵烟雾。何首乌藤和木莲藤缠络着,木莲有莲房一般的果实,何首乌有拥肿的根。有人说,何首乌根是有象人形的,吃了便可以成仙,我于是常常拔它起来,牵连不断地拔起来,也曾因此弄坏了泥墙,却从来没有见过有一块根象人样。如果不怕刺,还可以摘到覆盆子,象小珊瑚珠攒成的小球,又酸又甜,色味都比桑椹要好得远。 </div> <div class="item"> 3、陈从周《说园》片段 园有静观、动观之分,这一点我们在造园之先,首要考虑。何谓静观,就是园中予游者多驻足的观赏点;动观就是要有较长的游览线。二者说来,小园应以静观为主,动观为辅,庭院专主静观。大园则以动观为主,静观为辅。前者如苏州网师园,后者则苏州拙政园差可似之。人们进入网师园宜坐宜留之建筑多,绕池一周,有槛前细数游鱼,有亭中待月迎风,而轩外花影移墙,峰峦当窗,宛然如画,静中生趣。至于拙政园径缘池转,廊引人随,与“日午画船桥下过,衣香人影太匆匆”的瘦西湖相仿佛,妙在移步换影,这是动观。立意在先,文循意出。动静之分,有关园林性质与园林面积大小。象上海正在建造的盆景园,则宜以静观为主,即为一例。 中国园林是由建筑、山水、花木等组合而成的一个综合艺术品,富有诗情画意。叠山理水要造成“虽由人作,宛自天开”的境界。山与水的关系究竟如何呢?简言之,模山范水,用局部之景而非缩小(网师园水池仿虎丘白莲池,极妙),处理原则悉符画本。山贵有脉,水贵有源,脉源贯通,全园生动。我曾经用“水随山转,山因水活”与“溪水因山成曲折,山蹊随地作低平”来说明山水之间的关系,也就是从真山真水中所得到的启示。明末清初叠山家张南垣主张用平冈小陂、陵阜陂阪,也就是要使园林山水接近自然。如果我们能初步理解这个道理,就不至于离自然太远,多少能呈现水石交融的美妙境界。 </div> <div class="item"> 4、梁实秋《雅舍》片段 “雅舍”最宜月夜——地势较高,得月较先。看山头吐月,红盘乍涌,一霎间,清光四射,天空皎洁,四野无声,微闻犬吠,坐客无不悄然!舍前有两株梨树,等到月升中天,清光从树间筛洒而下,地下阴影斑斓,此时尤为幽绝。直到兴阑人散,归房就寝,月光仍然逼进窗来,助我凄凉。细雨蒙蒙之际,“雅舍”亦复有趣。推窗展望,俨然米氏章法,若云若雾,一片弥漫。但若大雨滂沱,我就又惶悚不安了,屋顶浓印到处都有,起初如碗大,俄而扩大如盆,继则滴水乃不绝,终乃屋顶灰泥突然崩裂,如奇葩初绽,砉然一声而泥水下注,此刻满室狼藉,抢救无及。此种经验,已数见不鲜。 </div> <div class="item"> 5、冰心《图画》 信步走下山门去,何曾想寻幽访胜? 转过山坳来,一片青草地,参天的树影无际。树后弯弯的石桥,桥后两个俯蹲在残照里的狮子。回过头来,只一道的断瓦颓垣,剥落的红门,却深深掩闭。原来是故家陵阙!何用来感慨兴亡,且印下一幅图画。 半山里,凭高下视,千百的燕子,绕着殿儿飞。城垛般的围墙,白石的甬道,黄绿琉璃瓦的门楼,玲珑剔透。楼前是山上的晚霞鲜红,楼后是天边的平原村树,深蓝浓紫。暮霭里,融合在一起。难道是玉宇琼楼?难道是瑶宫贝阙?何用来搜索诗肠,且印下一幅图画。 低头走着,—首诗的断句,忽然浮上脑海来。“四月江南无矮树,人家都在绿阴中。”何用苦忆是谁的著作,何用苦忆这诗的全文。只此已描画尽了山下的人家! </div> <div class="item"> 6、徐志摩《我所知道的康桥》片段 康桥的灵性全在一条河上;康河,我敢说是全世界最秀丽的一条水。河的名字是葛兰大(Granta),也有叫康河(Kiver Cam)的,许有上下流的区别,我不甚清楚。河身多的是曲折,上游是有名的拜伦潭——“Byron's Pool”——当年拜伦常在那里玩的;有一个老村子叫格兰骞斯德,有一个果子园,你可以躺在累累的桃李树荫下吃茶,花果会掉入你的茶杯,小雀子会到你桌上来啄食,那真是别有一番天地。这是上游;下游是从骞斯德顿下去,河面展开,那是春夏间竞舟的场所。上下河分界处有一个坝筑,水流急得很,在星光下听水声,听近村晚钟声,听河畔倦牛刍草声,是我康桥经验中最神秘的一种:大自然的优美、宁静,调谐在这星光与波光的默契中不期然的淹入了你的性灵。 </div> </div> </div> </body> <script> let wrap = document.querySelector('.content') // 创建 createTreeWalker 迭代器,用于遍历文本节点,保存到一个数组 const treeWalker = document.createTreeWalker(wrap, NodeFilter.SHOW_TEXT); const allTextNodes = []; let currentNode = treeWalker.nextNode(); while (currentNode) { allTextNodes.push(currentNode); currentNode = treeWalker.nextNode(); } let search = document.getElementById('search') search.onchange = event => { let value = event.target.value // 清除上个高亮 CSS.highlights.clear(); if (!value) { return } let indices = [] // 位置信息 const ranges = allTextNodes .map((el) => { return { el, text: el.textContent.toLowerCase() }; }) .map(({ text, el }) => { let startPos = 0 while (startPos < text.length) { const index = text.indexOf(value, startPos); if (index === -1) break indices.push({ el, start: index, end: index + value.length }); startPos = index + value.length; } }); // 根据搜索词的位置创建选区 let result = indices.map(item => { let range = new Range(); range.setStart(item.el, item.start); range.setEnd(item.el, item.end); return range }); // 创建高亮对象 const searchResultsHighlight = new Highlight(...result); // 注册高亮 CSS.highlights.set("search-results", searchResultsHighlight); } </script> </html>
四、总结
以上就是关于 CSS Custom Highlight API
的使用方法:
- 1. 获取文字节点
createTreeWalker()
- 2. 创建选区
new Range()
- 3. 创建高亮
new Highlight()
- 4. 自定义样式
::hightlight()
**
highlight()
自定义样式只兼容以下:
文本颜色 `color` 背景颜色 `background-color` 文本修饰 `text-decoration` 文本阴影 `text-shadow` 文本描边 `-webkit-text-stroke` 文本填充 `-webkit-text-fill-color`
以下是兼容性,比较差,目前谷歌只兼容 Chrome 105
以上
以上就是JS+CSS实现高亮关键词(不侵入DOM)的方式的详细内容,更多关于JS+CSS高亮关键词的资料请关注脚本之家其它相关文章!
相关文章
Javascript实现通过选择周数显示开始日和结束日的实现代码
这篇文章主要介绍了Javascript实现通过选择周数显示开始日和结束日的实例代码的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下2016-05-05BootStrap中关于Select下拉框选择触发事件及扩展
Select下拉框的问题,想在选择一个选项后,前台显示做出变动,并且知道选择的是第几个选项。 怎么解决这个问题呢?下面小编给大家带来了BootStrap中关于Select下拉框选择触发事件及扩展,需要的朋友参考下吧2016-11-11详解webpack require.ensure与require AMD的区别
本篇文章主要介绍了详解webpack require.ensure与require AMD的区别,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2017-12-12
最新评论