给博客添加评论和标签过滤功能
本文主要介绍了如何使用 giscus 为博客添加评论系统和用 CSS 为博客添加标签过滤功能。
评论功能
本博客使用 giscus 作为评论系统。
为什么选择 giscus
Giscus 是利用 GitHub Discussions 实现的评论系统,可以让访客借助 GitHub 在我们的网站上留下评论和表情。根据官网,giscus 的特性有很多:
giscus 的特点如下:
- 开源。
- 无跟踪,无广告,永久免费。
- 无需数据库。所有数据均储存在 GitHub Discussions 中。
- 支持自定义主题!
- 支持多种语言。
- 高可配置性。
- 自动从 GitHub 拉取新评论与编辑。
- 可自建服务!
但对于我来说,真正吸引我的地方是它是利用 GitHub Discussions 实现的。我觉得这比使用 GitHub issues 要好一点。
引入 giscus
配置其实很简单,在官网上根据说明用鼠标点点就好了,非常人性化。最终我们会得到一段 HTML 配置:
1: <script src="https://giscus.app/client.js" 2: data-repo="[在此输入仓库]" 3: data-repo-id="[在此输入仓库 ID]" 4: data-category="[在此输入分类名]" 5: data-category-id="[在此输入分类 ID]" 6: data-mapping="pathname" 7: data-strict="0" 8: data-reactions-enabled="1" 9: data-emit-metadata="0" 10: data-input-position="bottom" 11: data-theme="preferred_color_scheme" 12: data-lang="zh-CN" 13: crossorigin="anonymous" 14: async> 15: </script>
接下来就只需把这段代码放入博客对应位置就好了。但在 ox-publish 中,这可能比较麻烦。一个方案是我们可以将其放在 org-html-postamble
中,但这会导致我们的主页上也有评论界面,这不是我们想要的。所以我的方案是利用修改 org-html-template
。把评论系统添加到除主页外的所有页面里。具体可以看百般武艺,此乃 Emacs (一):用 Emacs
写博客 。
标签过滤功能
实现原理
ox-publish 对生成文章 tag/categories 页面并没有原生支持,自己实现起来比较麻烦,所以我一开始是不打算做的。但是最近在逛 CodePen 的时候看到了一个 2 cool ways to
use :has() ,突然想到应该可以利用 :has()
来实现博客文章过滤功能,实验了一下果然可以。
原理其实很简单,我们以上述 CodePen 中的代码为例,简单梳理一下流程:
简要的 HTML 代码如下:
1: <section> 2: <form> 3: <p>Filter by category:</p> 4: 5: <label> 6: <input type="checkbox" name="naps" checked /> 7: naps 8: </label> 9: </p> 10: </form> 11: 12: <article> 13: <img src="path/to/image" alt="abstract color pattern" /> 14: <h2>Afternoon Hammock</h2> 15: <p class="categories" data-category="naps"></p> 16: </article> 17: </section>
核心 CSS 代码如下:
1: article { 2: display: none; 3: } 4: 5: section:has([name="naps"]:checked) article:has([data-category="naps"]) { 6: display: block; 7: }
实现
知道了原理后,剩下的问题就是如何添加对应的 HTML/CSS 代码,其中的关键就是如何获得文章标签。因为文章的标签都是动态添加的,在导出时我们一般无法直接得出现有的标签列表。不过好在 ox-publish 对 sitemap 的处理方式为我们提供了机会:ox-publish 是先利用由 :sitemap-format-entry
指定的函数来收集所有的文章标题,然后用
:sitemap-function
指定的函数将数据写到指定文件中 ,最后再将其导出成 HTML
文件。我们可以在其收集文章标题的时候顺便用 org-publish-find-property
来收集对应的标签。对应的代码如下:
1: (defvar eli/blog-tags nil) 2: (defun eli/sitemap-dated-entry-format (entry _style project) 3: "Sitemap PROJECT ENTRY STYLE format that includes date." 4: (let* ((file (org-publish--expand-file-name entry project)) 5: (parsed-title (org-publish-find-property file :title project)) 6: (title 7: (if parsed-title 8: (org-no-properties 9: (org-element-interpret-data parsed-title)) 10: (file-name-nondirectory (file-name-sans-extension file)))) 11: (tags (org-publish-find-property file :filetags project)) 12: (tags-string (mapconcat 13: (lambda (tag) 14: (concat "#" tag)) 15: tags " "))) 16: (dolist (tag tags) 17: (cl-pushnew tag eli/blog-tags :test #'string=)) 18: (org-publish-cache-set-file-property file :title title) 19: (if (= (length title) 0) 20: (format "*%s*" entry) 21: (format "{{{timestamp(%s)}}} [[file:%s][%s]] {{{tags(%s)}}}" 22: (car (org-publish-find-property file :date project)) 23: (concat "articles/" entry) 24: title 25: tags-string))))
- 第 11 行
- 文档中的标签形式是
#+FILETAGS: :taga:tagb:
。 - 第 14 行
- 这里添加了
#
来更好的表示标签。 - 第 17 行
- 收集标签。
- 第 21 行
- 这里的
{{{tags(%s)}}}
是一个导出宏:
1: (add-to-list 'org-export-global-macros 2: '("tags" . "@@html:<span class=\"tags\" data-tags=\"$1\"></span>@@"))
收集到了标签后,剩下的就是设置标签对应的 HTML/CSS 代码了。我们可以在 ox-publish
写入 sitemap 文件的时候,同时插入 HTML 代码和设置 org-html-head-extra
:
1: (defun eli/org-publish-sitemap (title list) 2: "Generate the sitemap with title." 3: (setq org-html-head-extra 4: (format "<style>\n%s\n%s\n</style>" 5: ".content:has([value=\"all\"]:checked) li{display: list-item;}\n" 6: (mapconcat 7: (lambda (tag) 8: (format ".content:has([value=\"%s\"]:checked) 9: li:has([data-tags~=\"%s\"]){display: list-item;}" 10: tag (concat "#" tag))) 11: eli/blog-tags "\n"))) 12: (concat "#+TITLE: " title 13: "\n" 14: "#+DATE: 2023-10-10" 15: "\n" 16: (format "#+BEGIN_EXPORT html 17: <section class=\"filter\">\n%s\n%s</section> 18: #+END_EXPORT" 19: "<label class=\"category\"> 20: <input type=\"radio\" name=\"tag\" value=\"all\" checked/> 21: <span>All</span> 22: </label>" 23: (mapconcat 24: (lambda (tag) 25: (format "<label class=\"category\"> 26: <input type=\"radio\" name=\"tag\" value=\"%s\"/> 27: <span>%s</span> 28: </label>" 29: tag tag)) 30: eli/blog-tags "\n")) 31: "\n" 32: (org-list-to-org list)))
这样,我们的标签过滤功能就完成了,详细的 CSS 代码可以查看仓库: GitHub - Elilif/Elilif.github.io 。效果如下所示: