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();
 }

可以看到,到这一步,拿到所有文本节点数据
JS + CSS 实现高亮关键词(不侵入DOM)_第1张图片
接下来,我们根据搜索的结果,拿到匹配到的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;
        }
      } 
    });
}

我们可以看下位置信息结果
JS + CSS 实现高亮关键词(不侵入DOM)_第2张图片
接下来,创建选区,进行高亮,这里用到了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ó)地开着旳,有羞涩地打着朵儿旳;正如一粒粒的明珠,又如碧天里的星星,又如刚出浴的美人。微风过处,送来缕缕清香,仿佛远处高楼上渺茫的歌声似的。这时候叶子与花也有一丝的颤动,像闪电般,霎时传过荷塘的那边去了。叶子本是肩并肩密密地挨着,这便宛然有了一道凝碧的波痕。叶子底下是脉脉()的流水,遮住了,不能见一些颜色;而叶子却更见风致了。

      月光如流水一般,静静地泻在这一片叶子和花上。薄薄的青雾浮起在荷塘里。叶子和花仿佛在牛乳中洗过一样;又像笼着轻纱的梦。虽然是满月,天上却有一层淡淡的云,所以不能朗照;但我以为这恰是到了好处——酣眠固不可少,小睡也别有风味的。月光是隔了树照过来的,高处丛生的灌木,落下参差的斑驳的黑影,峭楞楞如鬼一般;弯弯的杨柳的稀疏的倩影,却又像是画在荷叶上。塘中的月色并不均匀;但光与影有着和谐的旋律,如梵婀(ē)(英语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)_第3张图片

你可能感兴趣的:(Javascript,Html,/,CSS,浏览器,javascript,css,前端)