不可能⁉️ 仅用 CSS 语法高亮😱……而且只用一个元素和渐变效果🤯
哎呀,又来了。
是的,我又回来了,再次引爆网络。我知道,已经过去一段时间了。
但这一次……我或许想出了一些有点用处的东西?
我们会看到!
首先,标题并非标题党。
实际上,我已经构建了一个(有缺陷的)语法高亮系统,它只使用一个元素,并且可以用纯 CSS 实现(用于显示代码片段)。
无需<span>为每个要高亮显示的语法部分单独创建一个元素,也无需使用笨重的 JavaScript 库来渲染所有内容。
听起来很有意思?让我们直接来看一个代码示例:
一个例子
看看这漂亮的语法高亮!
全部代码不到1kb!
以下是HTML代码:
<pre>
let thisContent = 'highlighted';
for(x = 0; x < 10; x++){
console.log('loopy loopy loop' + x);
}
</pre>
注意:请<pre><code>在生产环境中用于标记代码块。
以下是CSS代码:
body{
background-color: #111;
padding: 20px;
color: white;
font-size: 125%;
}
pre{
width: 80ch;
font-family: monospace;
font-size: 30px;
line-height: 30px;
background:
linear-gradient(to right, white 0ch, #78dce8 0ch, #78dce8 3ch, white 3ch, white 18ch, #FFD658 18ch, #FFD658 31ch, white 31ch, white 80ch),
linear-gradient(to right, white 0ch, #a6e22e 0ch, #a6e22e 3ch, white 3ch, white 12ch, #f92672 12ch, #f92672 15ch, #A7C 15ch, #A7C 17ch, white 17ch, white 80ch),
linear-gradient(to right, white 0ch, white 0ch, white 4ch, #fd971f 4ch, #fd971f 11ch, white 11ch, white 12ch, #a6e22e 12ch, #a6e22e 15ch, white 15ch, white 16ch, #FFD658 16ch, #FFD658 34ch, #f92672 34ch, #f92672 37ch, white 37ch, white 80ch),
linear-gradient(to right, white 0ch, white 0ch, white 80ch),
linear-gradient(to right, white 0ch, white 0ch, white 80ch);
background-repeat: no-repeat;
background-size: 80ch 30px, 80ch 60px, 80ch 90px, 80ch 120px, 80ch 150px;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
就是这样!
所以呢?
所以……highlight.js,领先的语法高亮库之一……等等……只有286Kb(而且还是经过压缩和gZip处理的!原始JS文件超过980kb😱)。
如果只是想显示一段带有语法高亮显示的代码片段,那么要通过网络传输如此大量的JS 代码就太不划算了。
然而,很多网站都这样做,从而损害了网站的性能。
所以,虽然演示看起来可能不太令人印象深刻,但节省 99.8% 的数据量确实非常惊人!
解释:它是如何运作的?
所以我们所做的,是将一个精心设计的背景应用linear-gradient到<pre>元素上,它看起来像这样:
每个彩色方块都对应着我们想要突出显示的一些文本。
然后我们只需使用background-clip: text;CSS 属性,它允许我们说“嘿,请将背景上方的文本用作遮罩,并且仅当前面有文本时才显示背景,否则显示透明背景。”
最后我们得到了以下例子:
工作示例
但如果真这么容易,为什么不是每个人都这样做呢?
简而言之:linear-gradients以及对齐。
手动对齐线性渐变会花费大量时间。
此外,这依赖于线条不“环绕”,否则渐变将不再对齐(尽管通过一些巧妙的数学运算,我们或许可以解决这个问题!但这不属于本次演示的一部分)。
你看,为了生成梯度,我们需要知道:
- 每个高亮显示部分要生成的宽度是多少?
- 文本共有多少行?
- 知道每个部分需要用什么颜色突出显示。
第二部分很简单。但是,计算每个要高亮显示的元素的宽度很难,确定要高亮显示哪些元素……更是难上加难!
幸运的是……我足够傻,构建了一个功能齐全的编辑器,它可以在 JavaScript 代码片段中找到相关的“标记”,然后使用这些标记来计算每个部分linear-gradient应该出现在哪里。
想试试吗?
代码片段生成器
指示:
- 输入:在第一个框中输入一段简短的JS代码片段(*)
- 预览:检查预览是否符合预期(预览)(这是一个不太稳定的高亮设置,可能会在某些代码片段上失效)。
- 输出:将输出部分生成的代码复制并粘贴到你的代码中!(别忘了,这是为深色背景设计的,所以你需要将
<body>元素设置为深色背景,或者创建一个包裹<pre>元素并将其设置为深色背景。)
(*)由于此演示的限制,请确保任何一行不超过 70 个字符。
就是这样,请尝试以下操作:
了解其工作原理
你看,那段 JavaScript 代码简直是一团乱麻,全是拼凑起来的片段……我并不指望你能看懂它。
以下是事件的简化版本:
- 我们获取(输入)部分中的每一行并循环遍历它。
- 我们使用正则表达式(是的……我知道)来捕获 JavaScript 中的关键字,例如
letandfunction等。 - 然后,我们为每条线创建一个线性渐变,渐变的每个部分的长度对应于找到的匹配项。
- 最后,我们调整
background-sizeCSS 属性以适应给定代码行的高度,以便linear-gradient我们拥有的每个声明都与每行代码的长度和高度对齐。
最后一部分可能是最令人困惑的部分。
为了更好地解释,请思考以下几点:
let a = 'test';
let b = a + ' your code';
假设我们只想突出显示这两行中的字符串(“test”和“your code”)。
所以我们需要两个梯度。
它们的长度必须为 25 个字符(最长行的长度),并且我们需要在以下位置显示一个彩色方块:
- 第 1 行第 9 至 13 个字符
- 第 2 行第 13 至 23 个字符
这些梯度会按如下方式转化为线性梯度:
linear-gradient(to right, white 0ch, white 8ch, red 8ch, red 14ch, white 14ch, white 25ch),
linear-gradient(to right, white 0ch, white 12ch, red 12ch, red 24ch, white 24ch, white 25ch);
其中,“红色”是高亮颜色,“白色”是非高亮颜色。(我们的字符索引从 0 开始,如果您想知道为什么每个字符的编号/位置都比第一个字符少 1)。
但是,如果我们单独使用这两个渐变色,就会失败。
这是因为第一个梯度占据了 100% 的高度。
所以为了解决这个问题,我们需要linear-gradient使用background-size.
效果大概是这样的:
background-size: 25ch 22px, 25ch 44px;
假设高度line-height为 22px(因此第一个渐变从顶部算起高 22px,以覆盖第一行的高度,然后第二个渐变从顶部算起高 44px,以覆盖第二行。这是因为线性渐变会彼此叠加,先声明的渐变会显示在最上面)。
哦,但是它仍然不起作用。
你看,渐变默认是重复的。所以我们还需要设置background-repeat: no-repeat;
现在我们可以看到字符串的高亮显示了:
基本上就是这样,只需为每种类型的标记添加不同的颜色,你就拥有了一个完全由 CSS 实现的、无需任何<span>元素的“可用的”语法高亮系统。
但为什么?
好吧,好吧。你现在已经读完了整篇文章,但仍然想知道为什么。这很正常!
说实话,我只是冒出了一个很傻的想法。
但是,我也喜欢把一些东西拿来,用一种出乎意料的方式来使用。
我觉得这是个很好的学习方法,因为这类问题没有现成的教程可以参考。我只能不断尝试,自己摸索出解决问题的方法。
它还能帮助你更深入地学习,因为你需要阅读文档并进行实验。(例如,我学到了一些linear-gradient之前不太理解的知识,比如当你将多个线性渐变声明为单个背景时,它们是如何堆叠的。)
你觉得怎么样?
这真的有可能变成有用的东西吗?
你能想象在构建步骤中生成用于文档的超轻量级代码片段,并且只提供 CSS 和单个元素吗?
虽然目前这只是个玩笑性质的项目,但这个概念真的能在实际生产中行得通吗?🤔
请在评论区告诉我你的想法。💗
文章来源:https://dev.to/grahamthedev/impossible-css-only-js-syntax-highlighting-with-a-single-element-and-gradients-243j




