一、原始效果
首先我们需要了解2个功能项的markdown输出内容,在语雀文档中创建1个高亮块和折叠块,随便输入一些内容并导出为md文档。
高亮块和折叠块效果
在源码中我们可以看到对于高亮块是使用:::
包裹的,不同的颜色是以:::
后的英文名称,如tips、info、success、color1等区分的;而折叠块则是使用原生的html标签(details、summary)构成的。
1 2 3 4 5 6 7 8 9 10 11 12 13
| :::tips 这是一个高亮块
:::
<details> <summary> <span>这是一个折叠块</span> </summary> <p> <span>这是折叠块内容</span> </p> </details>
|
运行Hexo,在界面中看到的效果如下:
高亮块无法解析,折叠块样式有些丑
二、折叠块样式美化
折叠块使用的details标签是HTML原生支持的,不需要JS脚本就可以完成折叠/展开的效果,我们只需要使用CSS美化下结构样式即可。这个最简单,我们先整这个!
就让ChatGPT生成一套details基础样式,然后我们再针对主题色进行微调就完成了。
不得不说AI在这方面的表现十分优异,初级程序员,危 ❗ ❗ ❗
分享一下Blog使用details样式,可以根据自己的需要再微调:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
details { background-color: #ffffff; border: 1px solid #e6e6e6; border-radius: 5px; padding: 1em; margin: 1rem 0; transition: all 0.3s ease; } details:hover { border-color: #01AD9F; } summary { font-weight: bold; cursor: pointer; outline: none; list-style: none; position: relative; padding-left: 25px; transition: color 0.3s ease; min-height: 1rem; line-height: 1rem; } summary:hover { color: #01AD9F; } summary::before { content: "▶"; position: absolute; left: 0; font-size: 14px; transform: rotate(0deg); transition: transform 0.3s ease; } details[open] summary::before { transform: rotate(90deg); } details > *:not(summary) { margin-top: 10px; padding-top: 1rem; border-top: 1px solid #eee; }
|
另外,也许你会发现在details、summary等标签中存在无用的class、id属性字段。我们可以创建一个自定义过滤器来清除它们:
1 2 3 4 5 6 7 8 9 10
| hexo.extend.filter.register('before_post_render', function(data) { const regex = /<(details|summary|p|span)([^>]*)(\s(class|id)="[^"]*")/g; while (regex.test(data.content)) { data.content = data.content.replace(regex, '<$1$2'); } return data; });
|
filter并不会修改md文件内容,仅在hexo页面渲染、构建时进行处理,这就会导致处理时资源重复消耗。最好能够在md文件创建时就对无用属性内容进行过滤,这样只需要处理一次!
三、高亮块样式渲染
由于高亮块内容用了特定的:::
进行包裹,所以我们可以借助hexo.extend.filter.register
构建自己的过滤器方法。在开始前只需要准备好一个正则匹配表达式即可(当然,这个工作也可以交给ChatGPT!)。
AI写正则表达式也是一把好手
现在我们可以构建自己的过滤器了,代码如下:
1 2 3 4 5 6 7 8 9 10
| hexo.extend.filter.register('before_post_render', function(data) { const regex = /:::(\w+)\n([\s\S]*?)\n:::/g;
data.content = data.content.replace(regex, function(match, tag, content) { return `<p>${content}</p>`; });
return data; });
|
四、效果演示
这是Blog第一个折叠块
Hello World!
五、BUG修复(2024/9/26更新)
在支持这两个功能后,我最新的游记《大连行:海水是什么颜色的?》已经使用上了高亮块。但我发现存在2个问题:
- markdown的高亮效果丢失;
- 存在多行内容时,首行以后的内容不会出现在高亮块中;
展示效果和源码
5.1 markdown的高亮效果丢失
原因是因为在拓展是在before_post_render
钩子处运行的,而hexo的渲染顺序是这样的:
- 执行
before_post_render
过滤器
- 使用 Markdown 或其他渲染器渲染(根据扩展名而定)
- 使用Nunjucks渲染
- 执行
after_post_render
过滤器
这使得过滤器运行完毕后,Markdown渲染器未能正确的进行内容渲染(hexo默认渲染器似乎不会对html标签内的内容进行处理)。那就意味着我们需要手动进行内容的markdown渲染了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| hexo.extend.filter.register('before_post_render', function(data) { const regex = /:::(\w+)\n([\s\S]*?)\n:::/g;
data.content = data.content.replace(regex, function(match, tag, content) {
const renderedContent = hexo.render.renderSync({ text: content, engine: 'markdown' }); return `<p>${renderedContent}</p>`; });
return data; });
|
5.2 存在多行内容时,首行以后的内容不会出现在高亮块中
这个问题主要是下面这行代码导致的:
1 2
| return `<p>${renderedContent}</p>`;
|
在Hexo中每一行文本会被渲染为<p>文本...</p>
。这就导致多行内容会出现<p>
标签嵌套的情况。而<p>
标签嵌套是不符合HTML规范的,浏览器会自动转换第二种形式。
1 2 3 4 5 6 7 8 9 10 11
| // 生成的html <p> <p><b>路线一:</b>在南2门花30元坐小火车到达山顶(单程30元,往返60元)</p> <p><b>路线二:</b>在南门花60元坐观光缆车到达山顶(单程60元,往返100元)</p> </p>
// 实际渲染的html <p> <b>路线一:</b>在南2门花30元坐小火车到达山顶(单程30元,往返60元) </p> <p><b>路线二:</b>在南门花60元坐观光缆车到达山顶(单程60元,往返100元)</p>
|
所以改动也很简单,将最外层的<p>
标签改为<div>
标签即可!
1 2 3 4 5
| return `<p>${renderedContent}</p>`;
return `<div class="highlight-box ${tag}">${renderedContent}</div>`;
|