花潮论坛

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

使用CSS Custom Highlight API做全文多彩字

[复制链接]
  • TA的每日心情
    奋斗
    2026-6-1 17:48
  • 签到天数: 1832 天

    [LV.Master]伴坛终老

    3237

    主题

    13万

    回帖

    28万

    积分

    管理员

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

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

    发表于 2026-6-1 17:55 | 显示全部楼层 |阅读模式

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

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

    x

    关于CSS Custom Highlight API

    CSS 自定义高亮 API 提供了一种机制,可以通过 JavaScript 创建任意文本范围,并使用 CSS 为其着色。

    在现代浏览器中,CSS自定义高亮可以在不改变现有DOM结构的情况下加以实现。完成这一功能,我们需要分几步走。

    一、使用CSS ::highlight() 伪元素设置高亮样式

    ::highlight 伪元素语法结构是 ::highlight(高亮名称),高亮名称自定义,可以根据需要创建一个或多个样式,例如:

    /* CSS自定义高亮代码 */ ::highlight(c1) { color: red; } ::highlight(c2) { color: blue; } ::highlight(c3) { color: green; } /* ... 更多的设置 */

    目前,::highlight 伪元素可以使用的CSS属性并不多,基本上就是前景色、背景色、文本阴影、下划线等。

    二、在JS中将CSS自定义高亮样式注册为可用样式

    这就是注册高亮,本质上就是将第一步创建的各个 ::highlight 样式弄成 CSS Highlight 接口,提供给指定的文本范围使用。

    let hl_ar = []; // 高亮样式池 ['c1', 'c2', 'c3'].forEach(c => { const hl = new Highlight(); // 创建高亮实例 hl_ar.push(hl); // 高亮实例加入样式池 CSS.highlights.set(c, hl); // 当前样式注册到高亮接口(绑定高亮) });

    这样,我们拥有了若干个高亮样式接口,都存储在 hl_ar 数组里,我们可以通过索引使用样式池中的任意一种颜色,它将作用于指定的文本范围,例如这样:

    // 假设已经定义了文本范围 range hl_ar[0].add(range);

    三、逐字高亮文本节点

    CSS Custom Highlight API 自定义高亮作用于文本节点指定的范围。例如下面简单结构的 div 只拥有一个文本节点,我们可以使用 firstChild 直接获取,然后使用第一、二步骤设置和注册的样式给文本逐字着色。以下代码已经集成了前面两个步骤的代码:

    <style> #mydiv { padding: 20px; font: bold 24px sans-serif; } /* CSS自定义高亮代码 */ ::highlight(c1) { color: red; } ::highlight(c2) { color: blue; } ::highlight(c3) { color: green; } </style> <div id="mydiv">Hello World!</div> <script> let hl_ar = []; ['c1','c2', 'c3'].forEach(c => { const hl = new Highlight(); hl_ar.push(hl); CSS.highlights.set(c, hl); }); const txtNode = mydiv.firstChild; for (let i = 0; i < txtNode.textContent.length; i ++) { const range = new Range(); // 创建实例化文本范围 range.setStart(txtNode, i); // 范围起始位置 range.setEnd(txtNode, i + 1); // 范围结束位置 hl_ar[i % hl_ar.length].add(range); } </script>

    四、操作指定元素的所有文本节点

    然而,容器性质的元素(例如 div),其内部结构不可能都如上例那么单纯,它们可能存在各种类型的节点。为此,要高亮容器元素内部的所有文本,我们需要找出全部的文本节点再对每一个文本节点逐字进行高亮显示的相关操作。

    使用 JS 提供的 TreeWalker 对象可以方便地遍历指定元素的所有节点,并且,遍历过程中可以筛选指定类型的节点(例如只要文本节点)。

    关于 TreeWalker 对象

    TreeWalker 对象用于表示文档子树中的节点和它们的位置。TreeWalker 可以使用 Document.createTreeWalker() 方法创建。使用过滤器 TreeWalker.filter(NodeFilter)可以极其方便地筛选节点类型。

    假设我们有一个 id="mybox" 的 div,现在我们要取出其内所有的文本节点,并存储到预设数组中:

    let allTxtNodes = []; // 文本节点数组 // 创建 TreeWalker 对象实例 const walker = document.createTreeWalker(mybox, NodeFilter.SHOW_TEXT); let currentNode = walker.nextNode(); // 当前文本节点 // 递归获取+保存所有文本节点 while (currentNode) { allTxtNodes.push(currentNode); // 存储文本节点 currentNode = walker.nextNode(); // 下一个节点 }

    每一个文本节点都等效于步骤三中的 mydiv.firstChild 节点,这么一来,全文多彩字效果就可以实现了。看完整的例子:

    <style> #mybox { padding: 20px; font: bold 24px sans-serif; } /* CSS自定义高亮代码 */ ::highlight(c1) { color: red; } ::highlight(c2) { color: orange; } ::highlight(c3) { color: magenta; } ::highlight(c4) { color: green; } ::highlight(c5) { color: teal; } ::highlight(c6) { color: blue; } ::highlight(c7) { color: purple; } </style> <div id="mybox"> <h1>渡荆门送别</h1> <p>李白</p> <p>渡远荆门外,来从楚国游。</p> <p>山随平野尽,江入大荒流。</p> <p>月下飞天镜,云生结海楼。</p> <p>仍怜故乡水,万里送行舟。</p> </div> <script> // 步骤一:注册高亮样式并存入数组 let hl_ar = []; ['c1','c2', 'c3', 'c4', 'c5', 'c6', 'c7'].forEach(c => { const hl = new Highlight(); hl_ar.push(hl); CSS.highlights.set(c, hl); // 注册当前样式 }); // 步骤二:获取并保存 id="mybox" 元素全部文本节点 let allTxtNodes = []; // 文本节点数组 // 创建 TreeWalker 对象实例 const walker = document.createTreeWalker(mybox, NodeFilter.SHOW_TEXT); let currentNode = walker.nextNode(); // 当前文本节点 // 递归获取+保存所有文本节点 while (currentNode) { allTxtNodes.push(currentNode); // 存储文本节点 currentNode = walker.nextNode(); // 下一个节点 } // 步骤三:遍历所有文本节点+着色 allTxtNodes.forEach(node => { for (let i = 0; i < node.textContent.length; i ++) { const range = new Range(); // 创建实例化文本范围 range.setStart(node, i); // 范围起始位置 range.setEnd(node, i + 1); // 范围结束位置 // 取颜色范围内随机数 const idx = Math.floor(Math.random() * hl_ar.length); hl_ar[idx].add(range); // 着色(文本范围绑定高亮样式) } }); </script>

    五、其它

    Highlight API可以在现代浏览器中实现无污染文本高亮,它不会改变DOM结构的一丝一毫,这是它的优势。自2025年起,Chrome 105+、Edge 105+、Firefox 140+、Opera 91+、Safari 17.2+ 以及对应的移动端版本均已实现对 Highlight API 的完美支持。

    评分

    参与人数 2威望 +80 金钱 +160 经验 +80 收起 理由
    霜染枫丹 + 30 + 60 + 30 匠心独运,细节精致入微!
    红影 + 50 + 100 + 50 匠心独运,细节精致入微!

    查看全部评分

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

    [LV.10]以坛为家III

    1898

    主题

    33万

    回帖

    39万

    积分

    管理员

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

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

    发表于 2026-6-1 20:04 | 显示全部楼层
    貌似英文的以字母也节点,汉字就是以文字为节点呢。看范例好像依次给文字赋色,用完了再轮换一遍,如此循环着。
    一个新的知识点,感谢黑黑的教学
     
    回复 支持 反对

    使用道具 举报

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

    [LV.10]以坛为家III

    1898

    主题

    33万

    回帖

    39万

    积分

    管理员

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

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

    发表于 2026-6-1 20:05 | 显示全部楼层
    有一个地方没看懂  range.setEnd(node, i + 1); // 范围结束位置

    这个怎么是结束位置呢?
     
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-6-1 17:48
  • 签到天数: 1832 天

    [LV.Master]伴坛终老

    3237

    主题

    13万

    回帖

    28万

    积分

    管理员

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

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

     楼主| 发表于 2026-6-1 20:08 | 显示全部楼层
    红影 发表于 2026-6-1 20:04
    貌似英文的以字母也节点,汉字就是以文字为节点呢。看范例好像依次给文字赋色,用完了再轮换一遍,如此循环 ...

    这是字符:英文以字母为一个字符,汉字以一个汉字为一个字符。

    节点不是这么理解。所谓节点,指的是DOM树中的单位,有很多种类型,其中,<p>文本</p> 是一个元素级节点,里面的“文本”两字是文本节点。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-6-1 17:48
  • 签到天数: 1832 天

    [LV.Master]伴坛终老

    3237

    主题

    13万

    回帖

    28万

    积分

    管理员

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

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

     楼主| 发表于 2026-6-1 20:13 | 显示全部楼层
    红影 发表于 2026-6-1 20:05
    有一个地方没看懂  range.setEnd(node, i + 1); // 范围结束位置

    这个怎么是结束位置呢?

    这是设定字符着色的范围:当遍历到文本节点中的第 i 个字符时,i 是该字符的开始位置(比如 i = 0 时,0 是文本范围第一个字的开始位置),i+1 是第一个字符结束的位置(对应于 i=0 时,i+1=1,这时的文本范围是文本节点的第一个字)。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2026-5-29 00:05
  • 签到天数: 188 天

    [LV.7]常住居民III

    320

    主题

    3398

    回帖

    2万

    积分

    版主

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

    花潮美女鼠牛虎兔龙蛇马羊猴鸡狗猪七彩绚丽金剪刀开朗大方花潮版主

    发表于 2026-6-1 21:53 | 显示全部楼层
    马老师晚上好~~

    这个帖子我一定好好的学习,争取学会运用,感谢马老师的无私分享,祝您夏安~~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2026-6-1 17:48
  • 签到天数: 1832 天

    [LV.Master]伴坛终老

    3237

    主题

    13万

    回帖

    28万

    积分

    管理员

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

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

     楼主| 发表于 2026-6-1 22:57 | 显示全部楼层
    霜染枫丹 发表于 2026-6-1 21:53
    马老师晚上好~~

    这个帖子我一定好好的学习,争取学会运用,感谢马老师的无私分享,祝您夏安~~{ ...

    回复 支持 反对

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2026-6-2 07:24 , Processed in 0.087347 second(s), 25 queries .

    Powered by Discuz! X3.4

    © 2001-2013 Comsenz Inc.

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