这位同好通过修改layouts/partials/toc.html的方式,把目录移到了侧边。构思挺好,根据页面是否能放得下侧边栏目录,来给目录动态增加、删除wide类,然后css中给包含wide类的元素设置特别的属性,来实现自适应的外观变化。

如果把对原主题文件的修改局限在官方提供的几个能自由发挥的地方(比如wiki讲到的assets/css/extended/layouts/partials/里的extend_head.htmlextend_footer.html等),以及在不覆盖主题原有文件的情况下增加文件,那么需要做一点修改。下面的代码及其思路均基于自这位同好的版本修改,本文不为比较孰好孰坏,只是玩一玩。

随页面宽度变化改变目录位置

工程目录中新增assets/js/toc.js文件,内容如下。添加事件回调去判断要不要给toc加上wide类。这里省去了原作者根据浏览位置给toc做当前位置指示的代码。

window.addEventListener('DOMContentLoaded', function () {
    let toc = document.getElementsByClassName("toc");
    if (toc.length >= 1) {
        const main_width = parseInt(getComputedStyle(document.body).getPropertyValue('--main-width'), 10);
        const toc_width = parseInt(getComputedStyle(document.body).getPropertyValue('--toc-width'), 10);
        const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 10);
        const post = document.querySelector('article.post-single .post-content');

        function checkTocPosition() {
            const width = document.body.scrollWidth;
            toc[0].style.setProperty('--post-height', `${post.offsetHeight}px`);
            if (width - main_width - (toc_width * 2) - (gap * 4) > 0) {
                toc[0].classList.add("wide");
            } else {
                toc[0].classList.remove("wide");
            }
        }

        checkTocPosition();
        window.addEventListener('resize', checkTocPosition, false);
    }
}, false);

工程目录中新增layouts/partials/extend_head.html文件1,内容如下(已经有这个文件的话,就加一段)。这里利用resources.Getassets下找js/toc.js并压缩输出到public/assets/js/toc.js。不一定非要压缩,但如果直接在<script>里指定src="/assets/js/toc.js",在baseURL包含目录的情况下也许会碰到路径问题。

{{ if (and (.Param "ShowToc") (ne (len .Fragments.Headings) 0)) }}
{{- $toc := resources.Get "js/toc.js" | resources.Minify | slice | resources.Concat "assets/js/toc.js" }}
<script defer crossorigin="anonymous" src="{{ $toc.RelPermalink }}"></script>
{{- end }}

也可以把上面第一段包裹在<script>里,然后放在工程目录的layouts/partials/extend_footer.html文件中2。因为baseof.html调用footer.html时使用partialCachedextend_footer.html里拿到上下文一般是某个缓存,得不到页面的真正内容,也就没办法像改extend_head.html那样判断是否能生成目录。放在extended_footer.html里只能做最基本筛选3,使得每个普通页面都会带上这个脚本,没那么优雅。

滚动页面时目录不跑出页面

下面这段放在assets/css/extended/toc.css中。主要是利用目录元素的定位属性来实现,外层元素设置为绝对定位,高度撑满页面,然后内层在其上sticky到顶部。篇首那位同好在修改toc.html时在目录外头增加了一层aside元素,但如果没有写样式的特殊需要,原来的结构也够用,如果要保留PaperMod原始toc风格,需要把外层的边框、缩进和底色等属性转移到内层去(下面属性值为unset的那几个)。

原作者将toc外层元素的高度设为100%,并通过top将位置设为和正文H1标题同高,所以整体没啥问题。但下面这段让目录移出来过后保持原来的位置高度,所以高度设为100%后整个页面就被撑长了(多了标题那一坨东西的高度)。所以在上面的js里获取正文(.post-content)的高度设给toc更加合理。

:root {
    --toc-width: 250px;
}

.toc.wide {
    position: absolute;
    height: var(--post-height);
    left: calc((var(--toc-width) + var(--gap)) * -1);
    width: var(--toc-width);
    margin: unset;
    border: unset;
    background: unset;
    border-radius: unset;
    padding: unset;
}

.wide > details {
    position: sticky;
    top: var(--gap);
    margin: 0 2px 40px;
    border: 1px solid var(--border);
    background: var(--code-bg);
    border-radius: var(--radius);
    padding: 0.4em;
}

总结

新增功能大概会有dom、js、css三个方面的改动,通过上面的实践简单总结如下:如果需要新增一些动态文档内容的(比如想要给链接加lightbox效果,需要增加遮罩层和空内容框架),可以考虑放在extend_footer.html中;js放在/assets/js/中,并在extend_head.html中添加加载代码(推荐一并用js.Build压缩代码),一些资源链接,以及加载外部js,也都放在extend_head.html中;css则放在/assets/css/extended/中,建议根据功能划分文件,不一定非要写到主题给的那个占位用的/assets/css/extended/blank.css里,extended/里放的文件能够覆盖主题原有样式,所有文件最后都会和主题原有样式表捆绑成1个css文件。


  1. extend_head.html模板调用路径为layouts/_default/baseof.htmllayouts/partials/head.htmllayouts/partials/extend_head.html。 ↩︎

  2. extend_footer.html模板调用路径为layouts/_default/baseof.htmllayouts/partials/footer.htmllayouts/partials/extend_footer.html。 ↩︎

  3. 这个基本筛选可以这么写。

    {{- if (and (eq .Kind "page") (ne .Layout "archives") (ne .Layout "search") (.Param "ShowToc")) }}
    ...
    {{- end }}
    
     ↩︎