共计 8210 个字符,预计需要花费 21 分钟才能阅读完成。
欢送关注我的公众号:前端侦探
介绍一个比拟前沿然而十分有用的新个性:一个浏览器原生反对的 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.firstChild
const text = nodes.textContent
const 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
恰好能无效避开这个问题。当然也有一些缺点,因为仅仅能扭转文本相干款式,所以也存在一些局限性,这个就须要衡量了,目前兼容性也还有余,仅实用于外部我的项目,敬请期待
最初,如果感觉还不错,对你有帮忙的话,欢送 点赞、珍藏、转发❤❤❤
欢送关注我的公众号:前端侦探