欢送关注我的公众号:前端侦探
介绍一个比拟前沿然而十分有用的新个性:一个浏览器原生反对的 CSS
文本高亮高亮性能,官网名称叫做 CSS Custom Highlight API,有了它,能够在不扭转 dom
构造的状况下自定义任意文本的款式,例如
再例如搜索词高亮
还能够轻易实现代码高亮
如许令人兴奋的性能啊,当初在 Chrome 105
中曾经正式反对了(无需开启试验个性),一起学习一下吧
一、伪元素 ::highlight()
要自定义任意文本款式须要 CSS
和 JS
的独特作用。
首先来看 CSS
局部,一个新的伪元素,非常简单
::highlight(custom-highlight-name) { color: red}
和::selection
这类伪元素比拟相似,仅反对局部文本相干款式,如下
- 文本色彩
color
- 背景色彩
background-color
- 文本润饰
text-decoration
- 文本暗影
text-shadow
- 文本描边
-webkit-text-stroke
- 文本填充
-webkit-text-fill-color
留神,留神,留神不反对background-image
,也就是突变之类的也不反对
然而,仅仅晓得这个伪类是没用的,她还须要一个“参数”,也就是下面的custom-highlight-name
,示意高亮的名称,那这个是怎么来的呢?或者换句话说,如何去标识页面中须要自定义款式的那局部文本呢?
这就须要借助上面的内容了,看看如何生成这个“参数”,这才是重点
二、CSS Custom Highlight API
在介绍之前,倡议先仔细阅读这篇文章:web 中的“光标”和“选区”
大部分操作其实和这个原理是雷同的,只是把拿到的选区做了进一步解决,具体分以下几步
1. 创立选区(重点)
首先,通过Range对象创立文本抉择范畴,就像用鼠标滑过选区一样,这也是最简单的一部分,例如
const parentNode = document.getElementById("foo");const range1 = new Range();range1.setStart(parentNode, 10);range1.setEnd(parentNode, 20);const range2 = new Range();range2.setStart(parentNode, 40);range2.setEnd(parentNode, 60);
这样能够失去选区对象range1
、range2
2. 创立高亮
而后,将创立的选区高亮实例化,须要用到Highlight对象
const highlight = new Highlight(range1, range2, ...);
当然也能够依据需要创立多个
const highlight1 = new Highlight(user1Range1, user1Range2);const highlight2 = new Highlight(user2Range1, user2Range2, user2Range3);
这样能够失去高亮对象highlight1
、highlight2
3. 注册高亮
接着,须要将实例化的高亮对象通过CSS.Highlight](https://developer.mozilla.org...))注册到页面
有点相似于Map
对象的操作
CSS.highlights.set("highlight1", highlight1);CSS.highlights.set("highlight2", highlight2);
目前兼容性比拟差,所以须要额定判断一下
if (CSS.highlights) { //...反对CSS.highlights}
留神看,下面注册的key
名,highlight1
就是上一节提到的高亮名称,也就是 CSS
中须要的“参数”
4. 自定义款式
最初,将定义的高亮名称联合::highlight
,这样就能够自定义选中款式了
::highlight(highlight1) { background-color: yellow; color: black;}
以上就是全副过程了,稍显简单,然而还是比拟好了解的,要害是第一步创立选区的过程,最为简单,再次举荐仔细阅读这篇文章:web 中的“光标”和“选区”,上面用一张图总结一下
原理就是这样,上面看一些实例
三、彩虹文本
当初来实现文章结尾图示成果,彩虹文本成果。总共7种颜色,文字顺次变色,一直循环,而且仅有一个标签
<p id="rainbow-text">CSS Custom Highlight API</p>
这里总共有7
种颜色,所以须要创立7
个高亮区域,能够先定义高亮 CSS
,如下
::highlight(rainbow-color-1) { color: #ad26ad; text-decoration: underline; }::highlight(rainbow-color-2) { color: #5d0a99; text-decoration: underline; }::highlight(rainbow-color-3) { color: #0000ff; text-decoration: underline; }::highlight(rainbow-color-4) { color: #07c607; text-decoration: underline; }::highlight(rainbow-color-5) { color: #b3b308; text-decoration: underline; }::highlight(rainbow-color-6) { color: #ffa500; text-decoration: underline; }::highlight(rainbow-color-7) { color: #ff0000; text-decoration: underline; }
当初必定不会有什么变动,因为还没创立选区
先创立一个高亮区域试试,比方第一个文字
const textNode = document.getElementById("rainbow-text").firstChild;if (CSS.highlights) { const range = new Range(); range.setStart(textNode, 0); // 选区终点 range.setEnd(textNode, 1); // 选区起点 const Highlight = new Highlight(range); CSS.highlights.set(`rainbow-color-1`, Highlight);}
成果如下
上面通过循环,创立7
个高亮区域
const textNode = document.getElementById("rainbow-text").firstChild;if (CSS.highlights) { const highlights = []; for (let i = 0; i < 7; i++) { // 给每个色彩实例化一个Highlight对象 const colorHighlight = new Highlight(); highlights.push(colorHighlight); // 注册高亮 CSS.highlights.set(`rainbow-color-${i + 1}`, colorHighlight); } // 遍历文本节点 for (let i = 0; i < textNode.textContent.length; i++) { // 给每个字符创立一个选区 const range = new Range(); range.setStart(textNode, i); range.setEnd(textNode, i + 1); // 增加到高亮 highlights[i % 7].add(range); }}
这样就在不扭转dom
的状况下实现了彩虹文字效果
残缺代码能够查看以下任意链接:(留神须要Chrome 105+)
- CSS Custom Highlight API (codepen.io)
- CSS Custom Highlight API (runjs.work)
四、文本搜寻高亮
大家都晓得浏览器的搜寻性能,ctrl+f
就能够疾速对整个网页就行查找,查找到的关键词会增加黄色背景的高亮,如下
以前始终很纳闷这个色彩是怎么增加的,毕竟没有任何包裹标签。当初有了CSS Custom Highlight API
,齐全能够手动实现一个和原生浏览器截然不同的搜寻高亮性能。
到目前为止,还无奈自定义原生搜寻高亮的黄色背景,当前可能会凋谢
假如HTML
构造是这样的,一个搜寻框和一堆文本
<label>搜寻 <input id="query" type="text"></label><article> <p> 阅文旗下囊括 QQ 浏览、终点中文网、新丽传媒等业界知名品牌,汇聚了弱小的创作者营垒、丰盛的作品储备,笼罩 200 多种内容品类,触达数亿用户,已胜利输入《庆余年》《赘婿》《鬼吹灯》《全职高手》《斗罗大陆》《琅琊榜》等大量优良网文 IP,改编为动漫、影视、游戏等多业态产品。 </p> <p> 《盗墓笔记》最后连载于终点中文网,是南派三叔成名代表作。2015年网剧开播首日点击破亿,开启了盗墓文学 IP 年。电影于2016年上映,由井柏然、鹿晗、马思纯等主演,累计票房10亿元。 </p> <p> 庆余年》是阅文团体白金作家猫腻的作品,自2007年在终点中文网连载,持续保持历史类珍藏榜前五位。改编剧集成为2019年景象级作品,播出期间登上微博热搜百余次,腾讯视频、爱奇艺双平台总播放量冲破160亿次,并荣获第26届白玉兰奖最佳编剧(改编)、最佳男配角两项大奖。 </p> <p>《鬼吹灯》是天下霸唱创作的经典悬疑盗墓小说,连载于终点中文网。先后进行过漫画、游戏、电影、网络电视剧的改编,均获得不俗的问题,是当之无愧的超级IP。</p></article>
简略丑化一下后成果如下
而后就是监听输入框,遍历文本节点(举荐应用原生的treeWalker
,当然一般的递归也能够),依据搜索词创立选区,具体代码如下
const query = document.getElementById("query");const article = document.querySelector("article");// 创立 createTreeWalker 迭代器,用于遍历文本节点,保留到一个数组const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);const allTextNodes = [];let currentNode = treeWalker.nextNode();while (currentNode) { allTextNodes.push(currentNode); currentNode = treeWalker.nextNode();}// 监听inpu事件query.addEventListener("input", () => { // 判断一下是否反对 CSS.highlights if (!CSS.highlights) { article.textContent = "CSS Custom Highlight API not supported."; return; } // 革除上个高亮 CSS.highlights.clear(); // 为空判断 const str = query.value.trim().toLowerCase(); if (!str) { return; } // 查找所有文本节点是否蕴含搜索词 const ranges = allTextNodes .map((el) => { return { el, text: el.textContent.toLowerCase() }; }) .map(({ text, el }) => { const indices = []; let startPos = 0; while (startPos < text.length) { const index = text.indexOf(str, startPos); if (index === -1) break; indices.push(index); startPos = index + str.length; } // 依据搜索词的地位创立选区 return indices.map((index) => { const range = new Range(); range.setStart(el, index); range.setEnd(el, index + str.length); return range; }); }); // 创立高亮对象 const searchResultsHighlight = new Highlight(...ranges.flat()); // 注册高亮 CSS.highlights.set("search-results", searchResultsHighlight);});
最初,通过CSS
设置高亮的色彩
::highlight(search-results) { background-color: #f06; color: white;}
实时搜寻成果如下
残缺代码能够查看以下任意链接:(留神须要Chrome 105+)
- CSS Highlight search (codepen.io)
- CSS Highlight search (runjs.work)
还能够将高亮成果改成波浪线
::highlight(search-results) { text-decoration: underline wavy #f06;}
成果如下,是不是也可用作错别字标识呢?
除了防止dom
操作带来的便当外,性能也能失去极大的晋升,毕竟创立、移除dom
也是性能小户,上面是一个测试 demo,搬运自
https://ffiori.github.io/highlight-api-demos/demo-performance.html
测试代码能够查看以下任意链接:
- Highlight performance demo (codepen.io)
- Highlight performance demo (runjs.work)
测试成果如下
在10000
个节点的状况下,两者相差100
倍的差距!而且数量越大,性能差距越显著,甚至间接导致浏览器卡死!
五、代码高亮编辑器
最初再来看一个十分实用的例子,能够轻易实现一个代码高亮的编辑器。
假如 HTML
构造是这样的,很简略,就一个纯文本的标签
<pre class="editor" id="code">ul{ min-height: 0;}.sub { display: grid; grid-template-rows: 0fr; transition: 0.3s; overflow: hidden;}:checked ~ .sub { grid-template-rows: 1fr;}.txt{ animation: color .001s .5 linear forwards;}@keyframes color { from { color: var(--c1) } to{ color: var(--c2) }}</pre>
简略润饰一下,设置为可编辑元素
.editor{ white-space: pre-wrap; -webkit-user-modify: read-write-plaintext-only; /* 读写纯文本 */}
成果如下
那么,如何让这些代码高亮呢?
这就须要对内容进行关键词剖析提取了,咱们能够用现有的代码高亮库,比方highlight.js。
hljs.highlight(pre.textContent, { language: 'css' })._emitter.rootNode.children
通过这个办法能够获取到CSS
语言的关键词以及类型,如下
简略解释一下,这是一个数组,如果是纯文本,示意一般的字符,如果是对象,示意是关键词,例如第一个,children
外面的ul
就是关键词,类型是selector-tag
,也就是选择器,除此之外,还有attribute
、number
、selector-class
等各种类型。有了这些关键词,咱们就能够把这些文本独自选取进去,而后高亮成不同的色彩。
接下来,就须要对代码内容进行遍历了,办法也是相似的,如下
const nodes = pre.firstChildconst text = nodes.textContentconst highlightMap = {}let startPos = 0;words.filter(el => el.scope).forEach(el => { const str = el.children[0] const scope = el.scope const index = text.indexOf(str, startPos); if (index < 0) { return } const item = { start: index, scope: scope, end: index + str.length, str: str } if (highlightMap[scope]){ highlightMap[scope].push(item) } else { highlightMap[scope] = [item] } startPos = index + str.length;})Object.entries(highlightMap).forEach(function([k,v]){ const ranges = v.map(({start, end}) => { const range = new Range(); range.setStart(nodes, start); range.setEnd(nodes, end); return range; }); const highlight = new Highlight(...ranges.flat()); CSS.highlights.set(k, highlight);})}highlights(code)code.addEventListener('input', function(){ highlights(this)})
最初,依据不同的类型,定义不同的色彩就行了,如下
::highlight(built_in) { color: #c18401; }::highlight(comment) { color: #a0a1a7; font-style: italic; }::highlight(number),::highlight(selector-class){ color: #986801; }::highlight(attr) { color: #986801; }::highlight(string) { color: #50a14f; }::highlight(selector-pseudo) { color: #986801; }::highlight(attribute) { color: #50a14f; }::highlight(keyword) { color: #a626a4; }
这样就失去了一个反对代码高亮的繁难编辑器了
相比传统的编辑器而言,这个属于纯文本编辑,十分轻量,在高亮的同时也不会影响光标,因为不会生成新的dom
,性能也是超级棒
残缺代码能够查看以下任意链接:
- CSS highlight editor (codepen.io)
- CSS highlight editor (runjs.work)
六、最初总结一下
以上就是对于CSS Custom Highlight API
的应用形式以及利用示例了,上面再来回顾一下应用步骤:
- 创立选区,
new Range
- 创立高亮,
new Highlight
- 注册高亮,
CSS.highlights.set
- 自定义款式,
::highlight()
相比传统应用标签的形式而已,有很多长处
- 应用场景更宽泛,很多状况下不能批改
dom
或者老本极大 - 性能更好,防止了操作
dom
带来的额定开销,在dom
较多状况下性能差别至多100
倍 - 简直没有副作用,能无效缩小
dom
变动引起的其余影响,比方光标选区的解决
其实归根结底,都是dom
变动带来的,而Highlight API
恰好能无效避开这个问题。当然也有一些缺点,因为仅仅能扭转文本相干款式,所以也存在一些局限性,这个就须要衡量了,目前兼容性也还有余,仅实用于外部我的项目,敬请期待
最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤
欢送关注我的公众号:前端侦探