1 问题# 评论系统是博客的关键组件,Hugo 支持若干个评论系统,包括流行的 Disqus , 基于 GitHub 的 Giscus 和 Utteranc , 以及其他评论系统
我博客 使用的评论系统是 Utteranc, 主题是 PaperMod , PaperMod 支持 dark 和 light 两种主题, 在初始化 Utteranc 时可以指定 theme, 如 Github-Light
1
2
3
4
5
6
7
< script src = "https://utteranc.es/client.js"
repo = "ramsayleung/comment"
issue-term = "title"
theme = "github-light"
crossorigin = "anonymous"
async >
</ script >
但是 theme 在初始化时就指定好了,那么在博客切换到 dark theme 的时候, Utteranc 也不会自适应 dark theme,博客的theme与评论theme就不一致:
2 解决思路# 解决思路其实很简单,就获取当前的 theme, 然后再初始化 Utteranc 对应的 theme; 再在用户切换 theme 之后,再重新初始化 Utteranc
.
2.1 获取当前Theme# Hugo 并没有提供标准接口来获取当前主题, 虽然可以通过以下的方式来获取的 theme:
1
2
const isDarkMode = window . matchMedia ( '(prefers-color-scheme: dark)' ). matches ;
console . log ( isDarkMode ? 'dark' : 'light' );
但是这个是通过编译出来的CSS来判断当前theme,用户一旦手动切换了 theme, 上面的代码就无法生效了.
每个Hugo主题定义的方式可能还不一样, 以 PaperMod 为例,观察之后发现,light theme的时候body的html 为:
1
2
< body id = "top" class = "" >
</ body >
dark theme 的时候 class
就变为了 class="dark"
:
1
2
< body id = "top" class = "" >
</ body >
所以可以通过判断 body
是否包含 dark
的 class 来判断当前是否为 dark theme.
1
2
3
4
// 不同的Hugo theme可能会需要不同的判断方式
function getCurrentTheme () {
return document . body . classList . contains ( 'dark' ) ? 'dark' : 'light' ;
}
2.2 初始化评论系统# Utteranc 文档提供的启用评价系统代码如下:
1
2
3
4
5
6
7
< script src = "https://utteranc.es/client.js"
repo = "[ENTER REPO HERE]"
issue-term = "pathname"
theme = "github-light"
crossorigin = "anonymous"
async >
</ script >
因为我们需要在切换 Theme 时重新加载 Utteranc, 所以就需要通过 Javascript 来实现上面的HTML功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function loadUtterances ( darkMode = false ) {
const commentContainer = document . getElementById ( "comments-utteranc" );
if ( commentContainer !== null ) {
commentContainer . innerHTML = ''
const commentScript = document . createElement ( "script" );
commentScript . setAttribute ( "id" , "utteranc" );
commentScript . setAttribute ( "src" , "https://utteranc.es/client.js" );
commentScript . setAttribute ( "data-repo" , "ramsayleung/comment" );
commentScript . setAttribute ( "data-theme" , darkMode ? "github-dark" : "github-light" );
commentScript . setAttribute ( "data-issue-term" , "title" );
commentScript . setAttribute ( "crossorigin" , "anonymous" );
commentScript . setAttribute ( "async" , "true" );
commentContainer . appendChild ( commentScript );
}
}
2.3 监听主题变动# 用户可以在博客界面手动选择他们喜欢的主题,可以从 dark
-> light
, light
-> dark
, 我们需要做的就是监听主题的变动,在切换主题之后,重新加载评论系统。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Watch for theme changes
const themeObserver = new MutationObserver (( mutations ) => {
mutations . forEach (( mutation ) => {
if ( mutation . type === 'attributes' && mutation . attributeName === 'class' ) {
const isDarkMode = getCurrentTheme () === 'dark' ;
loadUtterances ( isDarkMode );
console . log ( `changing theme` );
}
});
});
// Start observing the body element for class changes
themeObserver . observe ( document . body , {
attributes : true ,
attributeFilter : [ 'class' ]
});
3 总结# 自适应评论系统的代码在这里 , 实现的效果如下: