前言 链接到标题
注释
- 笔者苦没有侧栏索引久已,没有这个书签一样的跳转功能,对于一些长笔记博客就是噩梦,一不小心刷新一下就要从头开始往下找,并且markdown独特的head功能也损失殆尽。
- 寻找建立侧栏目录的过程几乎在创好博客之后不就久开始了,然而找到的其他主题的,比如stake、papermod等都有原生的侧栏目录,或者说是通用hugo的侧栏对于coder都不适用(当然,也可能是我再操作过程中没有完全复刻指引操作),尝试ai量身定做,或者是在coder仓库里用ai发挥,但是没有一种方案是能实现我想要的侧栏效果的。
- 看来这个效果还是有一定复杂性的,对于ai来说有点难理解,那么解决问题最好办法还是复现,
- 于是我尝试发issues,但是可能是我描述不清,我开的issues两周都没有人回复。
- 在这两周之内时不时我还是尝试着前面提到的各种方法,包括网页搜索、ai,但是均无成效。
- 今天突发奇想,我似乎之前用github copilot只是在读他的仓库代码,还没有用来整理过已有的77条issues,或许其中有我想要的答案?
- 果然,在第一轮检索的结果中ai就找到了我满意的结果,这个需求是有人已经实现过的,但是作者倾向于顶部目录,因此这个issue的回答者最终没有pull上来,因此没有构成coder的原生功能。
- 在AI的帮助下,我把那位贡献者的解决方案整理成了操作步骤,并成功实现了想要的效果。下面是我的详细记录(我之前完全没学过前端,并不指望能理解全部代码,只要能跟着教程实现功能并做些简单调整就满足了)。
- 如果你也想要这个效果,或者说你也在寻找coder主题的侧栏目录的实现方法,那么可以参考下面的步骤。
初步修改:复现 链接到标题
第一步要做的就是看到这个效果,至于之后怎么调,都要在“能实现”的基础上进行。
保存ckvv的代码 链接到标题
将ckvv提供的代码:
function debounce(func, wait, options = {
immediate: false,
middle: true,
thisArg: null,
}) {
let timer;
let restDate = new Date();
const immediate = options.immediate !== false;
const middle = options.middle !== false;
const thisArg = options.thisArg || null;
return function (...args) {
timer && clearTimeout(timer);
let isFirst = !timer;
timer = setTimeout(() => {
func.apply(thisArg, args);
restDate = new Date();
}, wait);
if ((new Date() - restDate > wait && middle) || (isFirst && immediate)) {
clearTimeout(timer);
func.apply(thisArg, args);
restDate = new Date();
}
}
}
function setActive(anchors) {
const ele = anchors.find((ele, index, arr) => {
return ele.getBoundingClientRect().top >= 0 || index >= arr.length - 1;
});
if (ele) {
const tableOfContents = document.querySelector('#table-of-contents');
const toActive = tableOfContents.querySelector(`a[href="#${ele.id}"]`);
if (!toActive) return;
const activeA = tableOfContents.querySelector(`.active`);
if (activeA) activeA.classList.remove('active');
toActive.classList.add('active');
window.history.pushState(null, null, `#${ele.id}`);
tableOfContents.scrollTo({
left: 0,
top: toActive.offsetTop - tableOfContents.getBoundingClientRect().height / 2,
behavior: 'smooth',
});
}
}
function initContents(icon = '<svg t="1690868184633" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3381" width="32" height="32"><path d="M128 192l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3382"></path><path d="M128 448l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3383"></path><path d="M128 704l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3384"></path></svg>') {
if(document.querySelector('#table-of-contents-wapper')) return;
const contents = document.createElement('details');
contents.id = 'table-of-contents-wapper';
contents.innerHTML = `<summary>
${icon}
</summary>`
const styleElement = document.createElement('style')
styleElement.innerHTML = `#table-of-contents-wapper {
user-select: none;
position: fixed;
right: 1em;
top: 4em;
border-radius: 8px;
z-index: 999
}
@media only screen and (min-width:768px) {
#table-of-contents-wapper[open] summary {
position: relative;
left: 5.8em
}
}
@media only screen and (max-width:768px) {
#table-of-contents-wapper {
top: auto;
right: auto;
bottom: 1.8rem;
left: 1.8rem
}
}
#table-of-contents-wapper summary {
display: inline-block;
font-size: 1.5em;
border-radius: 4px;
cursor: pointer;
padding: .2em
}
#table-of-contents-wapper #table-of-contents {
line-height: 1.3;
width: 19rem;
font-size: .8rem;
padding: .8em;
border: 1px solid;
border-radius: 6px;
overflow-y: scroll;
max-height: calc(100vh - 20rem);
color: currentColor
}
#table-of-contents-wapper #table-of-contents a {
width: 100%;
display: inline-block;
color: currentColor;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis
}
#table-of-contents-wapper #table-of-contents .active {
border-left: 2px solid #42a5f5;
color: #42a5f5
}
.content h1,
.content h2,
.content h3 {
padding-top: 2em !important;
margin-top: -2em !important
}`;
const anchors = [...document.querySelector('.content').querySelectorAll('h1[id],h2[id],h3[id],h4[id]')];
if (!anchors.length) return contents.remove();
const tableOfContents = document.createElement('div');
tableOfContents.id = 'table-of-contents';
anchors.forEach(ele => {
const a = document.createElement('a');
if (!ele.innerText) return;
a.innerText = ele.innerText;
a.href = `#${ele.id}`;
a.style.paddingLeft = `${ele.tagName.charAt(1)}em`;
tableOfContents.appendChild(a);
});
contents.appendChild(tableOfContents);
contents.open = window.innerWidth >= 768;
document.head.appendChild(styleElement);
document.body.append(contents);
setActive(anchors);
const debounceSetActive = debounce(setActive, 200)
window.addEventListener('scroll', () => {
debounceSetActive(anchors);
});
}
保存在/static/js/
目录下,命名为toc-sidebar.js
。
修改footer.html 链接到标题
对于/layouts/partials/footer.html
文件,添加如下代码:
<!-- 用于增加侧栏 -->
<script src="/js/toc-sidebar.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function() {
initContents();
});
</script>
提示
ai说也可以在
/layouts/partials/beseof.html
文件中添加,但是我没有尝试。修改single.html 链接到标题
修改后如下
{{ define "title" }}
{{ .Title }} · {{ .Site.Title }}
{{ end }}
{{ define "content" }}
{{ partial "page.html" . }}
{{ partial "posts/math.html" . }}
{{ end }}
然后可以运行一下hugo server,应该就已经实现了,笔者的视线效果如下:
优化 链接到标题
调整字号 链接到标题
显然,这个字号是相当小的,于是将js文件输给ai,ai告诉我了如何改,请将下面的代码覆盖进js文件中对应的函数,然后根据注释指引修改
function initContents(icon = '<svg t="1690868184633" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3381" width="32" height="32"><path d="M128 192l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3382"></path><path d="M128 448l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3383"></path><path d="M128 704l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3384"></path></svg>') {
if(document.querySelector('#table-of-contents-wapper')) return;
const contents = document.createElement('details');
contents.id = 'table-of-contents-wapper';
contents.innerHTML = `<summary>
${icon}
</summary>`
const styleElement = document.createElement('style')
// ...existing code...
styleElement.innerHTML = `#table-of-contents-wapper {
user-select: none;
position: fixed;
right: 1em;
top: 4em;
border-radius: 8px;
z-index: 999
}
@media only screen and (min-width:768px) {
#table-of-contents-wapper[open] summary {
position: relative;
left: 5.8em
}
/* 让文章内容靠左 */
.content {
margin-right: 22rem; /* 给右侧预留足够空间 */
max-width: calc(100% - 25rem);
}
}
@media only screen and (max-width:768px) {
#table-of-contents-wapper {
top: auto;
right: auto;
bottom: 1.8rem;
left: 1.8rem
}
}
#table-of-contents-wapper summary {
display: inline-block;
font-size: 1.5em;
border-radius: 4px;
cursor: pointer;
padding: .2em
}
#table-of-contents-wapper #table-of-contents {
line-height: 1.5; /* 增加行高改善可读性 */
width: 22rem; /* 增加宽度,从19rem到22rem */
font-size: 0.95rem; /* 增大字号,从0.8rem到0.95rem */
padding: 1em;
border: 1px solid;
border-radius: 6px;
overflow-y: scroll;
max-height: calc(100vh - 20rem);
color: currentColor
}
#table-of-contents-wapper #table-of-contents a {
width: 100%;
display: inline-block;
color: currentColor;
line-height: 20; /* 增加行高 */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 0.15em 0; /* 增加上下内边距,让链接更易点击 */
}
#table-of-contents-wapper #table-of-contents .active {
border-left: 2px solid #42a5f5;
color: #42a5f5
}
.content h1,
.content h2,
.content h3 {
padding-top: 2em !important;
margin-top: -2em !important
}`;
// ...existing code...
const anchors = [...document.querySelector('.content').querySelectorAll('h1[id],h2[id],h3[id],h4[id]')];
if (!anchors.length) return contents.remove();
const tableOfContents = document.createElement('div');
tableOfContents.id = 'table-of-contents';
anchors.forEach(ele => {
const a = document.createElement('a');
if (!ele.innerText) return;
a.innerText = ele.innerText;
a.href = `#${ele.id}`;
a.style.paddingLeft = `${ele.tagName.charAt(1)}em`;
tableOfContents.appendChild(a);
});
contents.appendChild(tableOfContents);
contents.open = window.innerWidth >= 768;
document.head.appendChild(styleElement);
document.body.append(contents);
setActive(anchors);
const debounceSetActive = debounce(setActive, 200)
window.addEventListener('scroll', () => {
debounceSetActive(anchors);
});
}
同时这一部分将文章尽可能的靠左,从而减少了挤在一起的情况,至于为什么呢?问ai。
效果如下:
明显舒服多了。 至此,初代侧栏目录就完成了。感谢ckvv在issues提供的启发帮助!