给博客添加评论和标签过滤功能

2024-03-09 2024-03-09History1049 字

本文主要介绍了如何使用 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: }
CSS 代码第 2
一开始的时候隐藏所有的 <article> 元素
CSS 代码第 5
在选中 checkbox 后,此选择器就生效,重新显示对应的 article 元素

实现

知道了原理后,剩下的问题就是如何添加对应的 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)))
16
这里的代码比较多,采用 HTML export code blocks 的方式。
19
这里我们添加一个显示全部文章的默认按钮。

这样,我们的标签过滤功能就完成了,详细的 CSS 代码可以查看仓库: GitHub - Elilif/Elilif.github.io 。效果如下所示:

Peek 2024-03-09 20-07.gif
Figure 1: tag-filtering-demo

Footnotes:

1

org-html-postamble 会被添加到所有导出页面中。

2

org-publish-project-alist 中的 :sitemap-filename 属性对应的文件。

3

这是 ox-publish 的另一个缺点:只能用原始的字符串拼接来定制 html 。


Author: Eli Qian Email: eli.q.qian@gmail.com Create Date: 2024-03-09 Last modified: 2024-03-09 Creator: Emacs 29.2 (Org mode 9.6.15)