花潮论坛

搜索
热搜: 活动 交友 discuz
查看: 31|回复: 6

CSS Custom Highlight API+Range+正则实现查找关键字高亮

[复制链接]
  • TA的每日心情
    奋斗
    2026-7-10 18:10
  • 签到天数: 1865 天

    [LV.Master]伴坛终老

    3269

    主题

    13万

    回帖

    29万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

    花潮帅哥鼠牛虎兔龙蛇马羊猴鸡狗猪多彩人生星月交辉奔放热烈海样胸怀春风拂面火热情怀优雅迷人神秘浪漫相遇之美鹰傲苍穹花好月圆紫色情节飞龙在天王者至尊大将风范音画大师天籁妙音共看流星风雨同行我心永远幸福快乐喜乐安康侠骨柔肠心想事成开朗大方花潮管理

    发表于 2026-7-10 18:11 | 显示全部楼层 |阅读模式

    请马上登录,朋友们都在花潮里等着你哦:)

    您需要 登录 才可以下载或查看,没有账号?立即注册

    x

    实现 Web 页查找关键字高亮,传统的方法都会改变原始 HTML 结构,通常的做法是对匹配的字符串用 span 标签包裹。CSS Custom Highlight API(CSS 自定义高亮接口,下称 CCHA)则完全可以避开对 DOM 树的写操作,直接通过在 DOM 中创建 Range 对象(范围)实现高亮,自2025年起已经得到现代浏览器的广泛支持。

    一、CCHA 实现高亮步骤拆解

    CCHA 首先需要在 CSS 中创建一个 ::highlight() 伪元素用以定义高亮样式:

    <style>
        /* 高亮伪元素(::highlight 是CSS内置伪类,hl-keyword 是自定义名称)*/
        ::highlight(hl-keyword) {
            background: tan;
        }
    </style>
        

    ::highlight(参数) 伪元素的参数是一个自定义名称,该名称是待引用的依据,习惯上分为两部分用以简单描述自身,中间使用短连接符将它们连接起来成为一体,例如 hl-keyword 表示“高亮关键字”。命名随意,起到辅助理解其含义方便后续使用即可。这是第一步。

    接下来是创建 Range 范围。创建 Range 范围应该针对指定元素,例如下例的 id="p" 的段落标签,假设我们想高亮前边的四个字:

    <p id="p">花朝论坛使用 Discuz! X3.4 创建</p>
    
    <script>
        const p = document.getElementById('p'); // 获取要操作的元素
        const range = new Range(); // 创建 Range 范围
        range.setStart(p.firstChild, 0); // 范围开始位置
        range.setEnd(p.firstChild, 4); // 范围结束位置
    </script>
        

    这样创建出来的 Range 范围就可以使用 CCHA 进行高亮,但还需要重要的两步:为 Range 范围创建高亮对象、注册高亮样式并对 Range 范围应用该样式:

    const hl = new Highlight(range); // 创建高亮对象(参数 range 为前面创建的范围)
    CSS.highlights.set('hl-keyword', hl); // 为高亮对象注册CSS定义的伪元素(使用该高亮样式)
        

    下面将上述所有步骤的代码整合成一个可运行的演示,该演示示例通过按钮的点击事件创建高亮效果并可取消高亮:

    <style>
        ::highlight(hl-keyword) { background: tan; }
    </style>
    
    <p id="p">花朝论坛使用 Discuz! X3.4 创建</p>
    <p>
        <button onclick="hLight()">创建高亮</button>
        <button onclick="clearHLight()">取消高亮</button>
    </p>
     
    <script>
        // 高亮函数(设定范围并高亮显示)
        function hLight() {
            const p = document.getElementById('p'); // 获取要操作的元素
            const range = new Range(); // 创建 Range 范围
            range.setStart(p.firstChild, 0); // 范围开始位置
            range.setEnd(p.firstChild, 4); // 范围结束位置
        
            const hl = new Highlight(range); // 创建高亮对象(参数 range 为前面创建的范围)
            CSS.highlights.set('hl-keyword', hl); // 注册CSS定义的伪元素(应用高亮样式)
        }
    
        // 注销高亮函数
        function clearHLight() {
            CSS.highlights.clear(); // 使用CSS.highlights.clear() 方法清除高亮样式注册
        }
    </script>
        
    【创建高亮小结】
    1. 在CSS中使用 ::highlight() 伪元素定义高亮样式
    2. 创建 Range(范围)对象
    3. 为 Range 范围创建高亮对象(new Highlight)
    4. 将CSS高亮样式注册给高亮对象(即高亮对象应用 CSS 高亮伪元素样式)

    二、使用 TreeWalker 遍历节点

    前面的示例待操作元素的HTML结构是单纯文本节点所构造,但真正的 Web 页节点构成可能是非常复杂的,文本可能存在于深层嵌套的元素。为此,创建 Range 范围会跟着变成复杂化,我们需要对之进行遍历、找出最里层的文本节点(#text)。遍历指定元素内的子节点有很多方法,其中借助 JS 内置的 TreeWalker API 加以实现最为简洁高效。以下函数演示了如何找出指定元素 pa 包含的所有文本,不论这些文本如何被各种子元素所嵌套:

    function findText(pa) {
        // 创建 TreeWalker 对象,节点过滤器仅接收#text
        const walker = document.createTreeWalker(pa, NodeFilter.SHOW_TEXT);
        // 遍历所有 walker 找到的节点
        while(walker.nextNode()) {
            const textNode = walker.currentNode; // 处理当前文本节点
            const text = textNode.textContent;   // 拿到文本内容
            //console.log(text); // 打印节点文本内容(或处理其它业务逻辑)
        }
    }
        

    应该认为 TreeWalker 处理节点的能力相当强大,至少,它轻而易举就能拿到所有被各种嵌套元素分割、包裹的具体文本内容。

    高亮指定元素内所有匹配的字符

    接下来的工作是在指定元素内找到匹配查找关键字的所有字符,然后高亮它们。以下示例可以在线预览:

    <style>
        #papa { margin: 20px auto; width: 600px; border: 1px solid gray; padding: 10px; }
        .tMid { text-align: center; }
        ::highlight(hl-keyword) { background: tan; }
    </style>
    
    <h3 class="tMid">请在下面方框选择文本(不支持跨行选择)</h3>
    <div id="papa" class="mdiv">
        9876543210123456789
        <p>abcABCEFG<b>aaa</b></p>
        <p>efgAAA<i>ABC</i>abc</p>
        <div>
            <p>啦啦啦……啊啊啊……</p>
            <p>0123456789876543210</p>
            <p><span style="color: blue">啊啊啊……啦啦啦……</span></p>
        </div>
    </div>
    
    <script>
        const papa = document.getElementById('papa'); // 获取待操作元素
    
        // 高亮函数
        function hlight(pa, keyword) {
            const ranges = []; // Range 范围数组(存储所有范围)
            // 创建 TreeWalker 对象(指定 pa 元素、仅处理 #text 节点)
            const walker = document.createTreeWalker(pa, NodeFilter.SHOW_TEXT);
            // 遍历拿到的节点并逐一匹配
            while(walker.nextNode()) {
                const textNode = walker.currentNode; // 拿到当前节点
                const reg = new RegExp(keyword, 'gi'); // 创建关键字正则表达式
                let matches = textNode.textContent.matchAll(reg); // 使用 matchAll 匹配正则
                if (!matches) return; // 若无匹配跳过
                // 遍历匹配返回的 matches 数组
                matches.forEach(match => {
                    const range = new Range(); // 创建 Range 范围
                    range.setStart(textNode, match['index']); // Range 开始位置
                    range.setEnd(textNode, match['index'] + match[0].length); // Range 结束位置
                    ranges.push(range); // 范围存入数组
                });
                const hl = new Highlight(...ranges); // 为所有匹配的范围创建高亮对象
                CSS.highlights.set('hl-keyword', hl); // 注册高亮样式并应用到高亮对象
            }
        }
        
        // 指定元素选择文本处理(鼠标松开事件)
        papa.onmouseup = (e) => {
            CSS.highlights.clear(); // 清空此前的高亮注册
            const findText = window.getSelection().toString(); // 获取选中的文本
            if (!findText || !e.target.closest('#papa')); // 无选中文本或操作对象元素不是指定元素忽略
            hlight(papa, findText); // 调用高亮函数
        };
    </script>
        

    以上示例综合运用了 CCHA 自定义高亮接口、TreeWalker 遍历接口、Selection 和 Range 对象、正则构造以及 CSS ::highlight() 伪元素实现了文章命题即高亮显示指定元素内所有关键词。其中的正则可能需要扩展,如果不满足于仅查找普通关键词的话。

    评分

    参与人数 3威望 +110 金钱 +220 经验 +110 收起 理由
    红影 + 50 + 100 + 50 匠心独运,细节精致入微!
    杨帆 + 30 + 60 + 30 匠心独运,细节精致入微!
    梦江南 + 30 + 60 + 30 匠心独运,细节精致入微!

    查看全部评分

  • TA的每日心情
    开心
    2026-7-10 10:08
  • 签到天数: 775 天

    [LV.10]以坛为家III

    546

    主题

    3万

    回帖

    8万

    积分

    贵宾

    Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7

    花潮美女鼠牛虎兔龙蛇马羊猴鸡狗猪缤纷心情心曲飞扬花好月圆飞龙在天音画大师天籁妙音共看流星风雨同行我心永远喜乐安康花潮贵宾

    发表于 2026-7-10 18:31 | 显示全部楼层
    问好黑黑老师,谢谢辛苦,学习了!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-7-10 18:10
  • 签到天数: 1865 天

    [LV.Master]伴坛终老

    3269

    主题

    13万

    回帖

    29万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

    花潮帅哥鼠牛虎兔龙蛇马羊猴鸡狗猪多彩人生星月交辉奔放热烈海样胸怀春风拂面火热情怀优雅迷人神秘浪漫相遇之美鹰傲苍穹花好月圆紫色情节飞龙在天王者至尊大将风范音画大师天籁妙音共看流星风雨同行我心永远幸福快乐喜乐安康侠骨柔肠心想事成开朗大方花潮管理

     楼主| 发表于 2026-7-10 19:11 | 显示全部楼层
    梦江南 发表于 2026-7-10 18:31
    问好黑黑老师,谢谢辛苦,学习了!

    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-7-10 18:10
  • 签到天数: 1865 天

    [LV.Master]伴坛终老

    3269

    主题

    13万

    回帖

    29万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

    花潮帅哥鼠牛虎兔龙蛇马羊猴鸡狗猪多彩人生星月交辉奔放热烈海样胸怀春风拂面火热情怀优雅迷人神秘浪漫相遇之美鹰傲苍穹花好月圆紫色情节飞龙在天王者至尊大将风范音画大师天籁妙音共看流星风雨同行我心永远幸福快乐喜乐安康侠骨柔肠心想事成开朗大方花潮管理

     楼主| 发表于 2026-7-10 19:28 | 显示全部楼层
    【附】匹配HTML特殊字符的正则构造函数

    1. function buildEntityRegex(keyword) {
    2.     const entityMap = {
    3.         '&': '&amp;|&#38;|&#x26;',
    4.         '<': '&lt;|&#60;|&#x3C;',
    5.         '>': '&gt;|&#62;|&#x3E;',
    6.         '"': '&quot;|&#34;|&#x22;',
    7.         "'": '&#39;|&#x27;|&apos;'
    8.     };
    9.    
    10.     const regexStr = keyword.split('').map(char => {
    11.         // 如果字符在实体映射中,允许匹配原字符或实体
    12.         if (entityMap[char]) {
    13.             return `(?:${char}|${entityMap[char]})`;
    14.         }
    15.         // 转义正则特殊字符
    16.         return char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    17.     }).join('');

    18.     return new RegExp(regexStr, 'gi');
    19. }
    复制代码
    加入此函数后,在一楼最后的示例中的 hlight() 函数中,将
    1. const reg = new RegExp(keyword, 'gi');
    复制代码
    改为
    1. const reg = buildEntityRegex(keyword);
    复制代码
    即可匹配诸如 >、&gt; 之类的HTML字符。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-7-10 18:10
  • 签到天数: 1865 天

    [LV.Master]伴坛终老

    3269

    主题

    13万

    回帖

    29万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

    花潮帅哥鼠牛虎兔龙蛇马羊猴鸡狗猪多彩人生星月交辉奔放热烈海样胸怀春风拂面火热情怀优雅迷人神秘浪漫相遇之美鹰傲苍穹花好月圆紫色情节飞龙在天王者至尊大将风范音画大师天籁妙音共看流星风雨同行我心永远幸福快乐喜乐安康侠骨柔肠心想事成开朗大方花潮管理

     楼主| 发表于 2026-7-10 19:30 | 显示全部楼层
    RegExp(keyword, 'gi') 中的 gi 参数,g 表示全局匹配,i 表示不区分大小写。如果希望区分大小写,可将 i 去掉。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-7-10 09:33
  • 签到天数: 591 天

    [LV.9]以坛为家II

    382

    主题

    4535

    回帖

    2万

    积分

    版主

    Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7

    花潮帅哥鼠牛虎兔龙蛇马羊猴鸡狗猪多彩人生鹰傲苍穹飞龙在天王者至尊大将风范花潮版主

    发表于 2026-7-10 19:58 | 显示全部楼层
    厉害~多路径实现查找关键字高亮,为马老师点赞
    ★青春是一个人的精神生命,奋斗是一个人最大的体面,学习是一个人最美的姿态。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2025-12-1 20:32
  • 签到天数: 1052 天

    [LV.10]以坛为家III

    1898

    主题

    33万

    回帖

    40万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9

    花潮美女虎龙狗猪多彩人生星月交辉海样胸怀火热情怀优雅迷人神秘浪漫缤纷心情草莓情怀蝴蝶情怀心曲飞扬星星情怀七彩绚丽活泼开朗女儿情怀相遇之美一往情深花好月圆心香一瓣紫色情节飞龙在天金剪刀天籁妙音妙笔生花风雨同行我心永远天长地久幸福快乐绚丽缤纷喜乐安康中秋征文周年庆指尖上的流年舞会之星分析(喊冤)章总结章杀人王小强章最佳杀刺临屏写诗七夕诗钟活动第五届风云第六届风云情人节花潮管理

    发表于 2026-7-10 21:13 | 显示全部楼层
    马黑黑 发表于 2026-7-10 19:30
    RegExp(keyword, 'gi') 中的 gi 参数,g 表示全局匹配,i 表示不区分大小写。如果希望区分大小写,可将 i  ...

    原来是这里设置的,去看预览发现选择后不分大小写都能高亮呢。
    只是个高亮,竟然有这么多的内容,想学会代码还真不容易啊。黑黑辛苦了
     
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    小黑屋|手机版|Archiver|服务支持:DZ动力|huachaowang.com Inc. ( 蜀ICP备17032287号-1 )

    GMT+8, 2026-7-10 23:25 , Processed in 0.214288 second(s), 26 queries .

    Powered by Discuz! X3.4

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表