原文載于:Hexo NexT主題側(cè)邊欄展示相關(guān)文章
Hexo的NexT主題展示相關(guān)文章和熱門文章使用 hexo-related-popular-posts 插件骨杂,但hexo-related-popular-posts 默認(rèn)展示位置是在頁面底部稠肘,而頁面底部本身內(nèi)容較多迎变,多數(shù)人注意不到相關(guān)文章。因此痢掠,考慮將相關(guān)文章展示在側(cè)邊欄回论。
初始想法是將相關(guān)文章新建一個與目錄塊平齊的相關(guān)文章塊振诬,隨著頁面的滾動椭员,目錄吸附在頁面頂部露乏,相關(guān)文章塊展示在目錄塊下方碧浊。無奈前端了解不多,試了下覺得難度系數(shù)略高瘟仿。取了個折衷方案箱锐,將相關(guān)文章與目錄顯示在同一div中。
概覽
效果圖
話不多說劳较,可在原地址看效果: https://finisky.github.io/2019/12/05/sidebarrelatedposts/
側(cè)邊欄相關(guān)文章:
原來的側(cè)邊欄:
實現(xiàn)原則
- 修改盡量小驹止,不對Hexo和NexT源碼改動太多,添加新文件少改老文件观蜗,以免今后升級困難
- 復(fù)用hexo-related-popular-posts的相關(guān)文章處理邏輯
- 風(fēng)格與主題保持一致
所有改動一覽
修改改三個文件臊恋,新增兩個文件:
~/finisky/themes/next/layout$ git status
On branch related
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: _macro/post.swig
modified: _macro/sidebar.swig
modified: ../source/css/_common/outline/sidebar/sidebar.styl
Untracked files:
(use "git add <file>..." to include in what will be committed)
_partials/post/post-related-sidebar.swig
../source/css/_common/outline/sidebar/sidebar-related.styl
獲取相關(guān)文章數(shù)據(jù)
復(fù)用hexo-related-popular-posts計算出來的相關(guān)文章數(shù)據(jù)即可,源碼在_partials/post/post-related.swig (隱去無關(guān)內(nèi)容):
{%- set popular_posts = popular_posts_json(theme.related_posts.params, page) %}
{%- if popular_posts.json and popular_posts.json.length > 0 %}
<div class="popular-posts-header">{{ theme.related_posts.title or __('post.related_posts') }}</div>
<ul class="popular-posts">
{%- for popular_post in popular_posts.json %}
...
<div class="popular-posts-title"><a href="{{ popular_post.path }}" rel="bookmark">{{ popular_post.title }}</a></div>
{%- if popular_post.excerpt and popular_post.excerpt != '' %}
<div class="popular-posts-excerpt"><p>{{ popular_post.excerpt }}</p></div>
{%- endif %}
</li>
{%- endfor %}
</ul>
{%- endif %}
顯然墓捻,可以通過popular_posts這個變量訪問到相關(guān)文章數(shù)據(jù)抖仅。
從側(cè)邊欄模板開始
看下前端代碼,可以找到Table of Contents的模板在themes/next/layout/_macro/sidebar.swig中砖第。該文件定義了整個側(cè)邊欄:
<aside class="sidebar">
<div class="sidebar-inner">
{%- set display_toc = page.toc.enable and display_toc %}
{%- if display_toc %}
{%- set toc = toc(page.content, { "class": "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
{%- set display_toc = toc.length > 1 and display_toc %}
{%- endif %}
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc">
{{ __('sidebar.toc') }}
</li>
<li class="sidebar-nav-overview">
{{ __('sidebar.overview') }}
</li>
</ul>
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
{%- if display_toc %}
<div class="post-toc motion-element">{{ toc }}</div>
{%- endif %}
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
{{ partial('_partials/sidebar/site-overview.swig', {}, {cache: theme.cache.enable}) }}
{{- next_inject('sidebar') }}
</div>
{%- if theme.back2top.enable and theme.back2top.sidebar %}
<div class="back-to-top motion-element">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
{%- endif %}
</div>
</aside>
看起來實現(xiàn)并不復(fù)雜撤卢,copy下Table of Contents的div,只把內(nèi)容修改成popular_posts即可梧兼。實驗了下發(fā)現(xiàn)naive了放吩,這樣修改頁面渲染會出問題,js報錯羽杰,頁面展示異常渡紫。想到應(yīng)該是js修改了這些元素的內(nèi)容,驗證:
~/finisky/themes/next$ grep -r "post-toc" .
...
./source/js/utils.js: const navItems = document.querySelectorAll('.post-toc li');
./source/js/utils.js: var tocElement = document.querySelector('.post-toc-wrap');
./source/js/utils.js: document.querySelectorAll('.post-toc .active').forEach(element => {
./source/js/utils.js: while (!parent.matches('.post-toc')) {
./source/js/utils.js: document.querySelector('.post-toc-wrap').style.maxHeight = sidebarWrapperHeight;
./source/js/utils.js: var hasTOC = document.querySelector('.post-toc');
可見utils.js會獲取class為"post-toc"的元素并操作考赛,會引發(fā)上述錯誤腻惠。
我們僅需要復(fù)用TOC的css顯示風(fēng)格,不會用到j(luò)s的這些動態(tài)操作欲虚。同時集灌,我們不希望改變原有的邏輯。綜上复哆,比較好的實現(xiàn)方式是copy下TOC的css欣喧,在此基礎(chǔ)上修改成相關(guān)文章的css style。
添加css: sidebar-related.styl
搜索找到toc的css文件在:themes/next/source/css/_common/outline/sidebar/sidebar-toc.styl文件中梯找。添加新的sidebar-related.styl在此目錄中:
.sidebar-related {
font-size: $font-size-small;
ol {
list-style-type: disc;
margin: 0;
padding: 0 2px 5px 25px;
text-align: left;
}
}
.sidebar-related-title {
margin-top: 20px;
padding-left: 0;
li {
border-bottom-color: $sidebar-highlight;
color: $sidebar-highlight;
border-bottom: 1px solid;
cursor: pointer;
display: inline-block;
font-size: $font-size-small;
}
}
上面這個小css文件也頗費了些周折唆阿,把無用的style去掉,調(diào)整margin和padding和list-style-type锈锤。當(dāng)然驯鳖,也可改成自己喜歡的樣式闲询,不一定強求與原主題一致。
修改css: sidebar.styl
還要將新加的文件import到themes/next/source/css/_common/outline/sidebar/sidebar.styl中浅辙,添加一行扭弧,修改如下:
...
@import 'sidebar-toc' if (hexo-config('toc.enable'));
+@import 'sidebar-related' if (hexo-config('toc.enable'));
@import 'site-state' if (hexo-config('site_state'));
...
添加側(cè)邊欄列表: post-related-sidebar.swig
在themes/next/layout/_partials/post/文件夾中添加post-related-sidebar.swig:
{%- set popular_posts = popular_posts_json(theme.related_posts.params, page) %}
{%- if page.toc.enable and theme.related_posts.enable and (theme.related_posts.display_in_home or not is_index) and popular_posts.json and popular_posts.json.length > 0 %}
<div>
<ul class="sidebar-related-title">
<li>
{{ theme.related_posts.title or __('post.related_posts') }}
</li>
</ul>
<!--noindex-->
<div class="sidebar-related">
<ol>
{%- for popular_post in popular_posts.json %}
<li><a href="{{ popular_post.path }}" rel="bookmark"><span class="nav-text">{{ popular_post.title }}</span></a></li>
{%- endfor %}
</ol>
</div>
<!--/noindex-->
</div>
{%- endif %}
此處用到了前面定義的兩個css style: sidebar-related-title和sidebar-related。同時用popular_posts變量輸出了超鏈接和文章標(biāo)題记舆。
只有在啟用了TOC和相關(guān)文章鸽捻,且相關(guān)文章有內(nèi)容的情況下才在側(cè)邊欄顯示。
修改側(cè)邊欄: sidebar.swig
萬事俱備泽腮,改一下側(cè)邊欄的文件:themes/next/layout/_macro/sidebar.swig御蒲,僅添加三行,把原始的TOC外面包一層div诊赊,再引用前文寫好的post-related-sidebar.swig即可:
@@ -8,6 +8,7 @@
<aside class="sidebar">
<div class="sidebar-inner">
+ <div>
{%- set display_toc = page.toc.enable and display_toc %}
{%- if display_toc %}
{%- set toc = toc(page.content, { "class": "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
@@ -36,6 +37,9 @@
{{- next_inject('sidebar') }}
</div>
+ </div>
+ {{ partial('_partials/post/post-related-sidebar.swig') }}
{%- if theme.back2top.enable and theme.back2top.sidebar %}
<div class="back-to-top motion-element">
完整sidebar.swig如下:
{% macro render(display_toc) %}
<div class="toggle sidebar-toggle">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
<aside class="sidebar">
<div class="sidebar-inner">
<div>
{%- set display_toc = page.toc.enable and display_toc %}
{%- if display_toc %}
{%- set toc = toc(page.content, { "class": "nav", list_number: page.toc.number, max_depth: page.toc.max_depth }) %}
{%- set display_toc = toc.length > 1 and display_toc %}
{%- endif %}
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc">
{{ __('sidebar.toc') }}
</li>
<li class="sidebar-nav-overview">
{{ __('sidebar.overview') }}
</li>
</ul>
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
{%- if display_toc %}
<div class="post-toc motion-element">{{ toc }}</div>
{%- endif %}
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
{{ partial('_partials/sidebar/site-overview.swig', {}, {cache: theme.cache.enable}) }}
{{- next_inject('sidebar') }}
</div>
</div>
{{ partial('_partials/post/post-related-sidebar.swig') }}
{%- if theme.back2top.enable and theme.back2top.sidebar %}
<div class="back-to-top motion-element">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
{%- endif %}
</div>
</aside>
<div id="sidebar-dimmer"></div>
{% endmacro %}
至此厚满,側(cè)邊欄已可顯示相關(guān)文章,還差最后一步碧磅。
去掉文中相關(guān)文章:post.swig
將原始文末顯示的相關(guān)文章去掉痰滋,刪除三行themes/next/layout/_macro/post.swig:
{### END POST BODY ###}
{#####################}
- {%- if theme.related_posts.enable and (theme.related_posts.display_in_home or not is_index) %}
- {{ partial('_partials/post/post-related.swig') }}
- {%- endif %}
{%- if not is_index %}
{{- next_inject('postBodyEnd') }}
大功告成~