目录悬浮

This commit is contained in:
liuzhihang 2022-10-30 15:14:23 +08:00
parent e6b0407b07
commit 10ac27a069
4 changed files with 159 additions and 105 deletions

View File

@ -77,112 +77,135 @@ document.addEventListener("DOMContentLoaded", function () {
// 滚动处理
const scrollFnToDo = function () {
let $cardTocLayout = document.getElementById("card-toc");
// let $cardToc = $cardTocLayout.getElementsByClassName("toc-content")[0];
// let $tocLink = $cardToc.querySelectorAll(".toc-link");
let $article = document.getElementById("article-container");
// let scrollPercent;
// if (GLOBAL_CONFIG.htmlType === 'post') {
// scrollPercent = function (currentTop) {
// const docHeight = $article.clientHeight;
// const winHeight = document.documentElement.clientHeight;
// const headerHeight = $article.offsetTop;
// const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
// const scrollPercent = (currentTop - headerHeight) / (contentMath);
// const scrollPercentRounded = Math.round(100 * scrollPercent);
// const percentage = 100 < scrollPercentRounded ? 100 : scrollPercentRounded <= 0 ? 0 : scrollPercentRounded;
// $cardToc.setAttribute("progress-percentage", percentage);
// };
// }
const isToc = GLOBAL_CONFIG.htmlType === 'post';
const isAnchor = true;
const $article = document.getElementById("article-container");
window.mobileToc = {
open: () => {
$cardTocLayout.style.cssText = "animation: toc-open .3s; opacity: 1; right: 45px";
},
if (!($article && (isToc || isAnchor))) return
close: () => {
$cardTocLayout.style.animation = "toc-close .2s"
setTimeout(function () {
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''";
}, 100);
let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand
if (isToc) {
const $cardTocLayout = document.getElementById("card-toc");
$cardToc = $cardTocLayout.getElementsByClassName("toc-content")[0];
$tocLink = $cardToc.querySelectorAll(".toc-link");
// const $tocPercentage = $cardTocLayout.querySelector(".toc-percentage");
isExpand = $cardToc.classList.contains("is-expand");
scrollPercent = function (currentTop) {
const docHeight = $article.clientHeight;
const winHeight = document.documentElement.clientHeight;
const headerHeight = $article.offsetTop;
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight);
const scrollPercent = (currentTop - headerHeight) / (contentMath);
const scrollPercentRounded = Math.round(scrollPercent * 100);
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded;
// $tocPercentage.textContent = percentage
$cardToc.setAttribute("progress-percentage", percentage);
};
window.mobileToc = {
open: () => {
$cardTocLayout.style.cssText = "animation: toc-open .3s; opacity: 1; right: 45px";
},
close: () => {
$cardTocLayout.style.animation = "toc-close .2s"
setTimeout(function () {
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''";
}, 100);
}
}
// toc 元素点击
$cardToc.addEventListener("click", e => {
e.preventDefault();
const target = e.target.classList;
if (target.contains('toc-content')) return;
const $target = e.target.classList.contains("toc-link") ? e.target : e.target.parentElement;
btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute("href")).replace("#", ""))), 300);
if (window.innerWidth < 900) {
window.mobileToc.close()
}
});
autoScrollToc = item => {
const activePosition = item.getBoundingClientRect().top
const sidebarScrollTop = $cardToc.scrollTop
if (activePosition > (document.documentElement.clientHeight - 100)) {
$cardToc.scrollTop = sidebarScrollTop + 150
}
if (activePosition < 100) {
$cardToc.scrollTop = sidebarScrollTop - 150
}
};
}
// find head position & add active class
const list = $article.querySelectorAll("h1,h2,h3,h4,h5,h6");
let detectItem = "";
const findHeadPosition = function (top) {
if (0 === $tocLink.length || 0 === top) {
return false
}
let currentId = "";
let currentIndex = "";
list.forEach(function (ele, index) {
if (top > btf.getEleTop(ele) - 80) {
const id = ele.id
currentId = id ? "#" + encodeURI(id) : ""
currentIndex = index
}
})
if (detectItem === currentIndex) {
return
}
if (isAnchor) btf.updateAnchor(currentId);
detectItem = currentIndex
if (isToc) {
$cardToc.querySelectorAll(".active").forEach(i => {
i.classList.remove("active")
});
if (currentId === "") {
return;
}
const currentActive = $tocLink[currentIndex];
currentActive.classList.add("active");
setTimeout(() => {
autoScrollToc(currentActive)
}, 0);
if (isExpand) return;
let parent = currentActive.parentNode;
for (; !parent.matches(".toc-list"); parent = parent.parentNode) {
if (parent.matches("li")) parent.classList.add("active")
}
}
}
// toc 元素点击
// $cardToc.addEventListener("click", e => {
// e.preventDefault();
// const target = e.target.classList.contains("toc-link") ? e.target : e.target.parentElement;
//
// btf.scrollToDest(
// btf.getEleTop(
// document.getElementById(
// decodeURI(target.getAttribute("href")).replace("#", "")
// )
// ),
// 300
// );
// if (window.innerWidth < 900) {
// window.mobileToc.close();
// }
// });
// find head position & add active class
// const list = $article.querySelectorAll("h1,h2,h3,h4,h5,h6");
// let detectItem = "";
// const findHeadPosition = function (top) {
//
// if (0 === $tocLink.length || 0 === top) {
// return false
// }
//
// let currentId = "";
// let currentIndex = "";
//
// list.forEach(function (ele, index) {
// if (top > btf.getEleTop(ele) - 80) {
// currentId = "#" + encodeURI(ele.getAttribute("id"));
// currentIndex = index
// }
//
// })
// if (detectItem === currentIndex) {
// return
// }
//
// detectItem = currentIndex
//
// $cardToc.querySelectorAll(".active").forEach(i => {
// i.classList.remove("active")
// })
//
// if (currentId === "") {
// return
// }
//
// const currentActive = $tocLink[currentIndex]
// currentActive.classList.add("active")
//
// setTimeout(() => {
// autoScrollToc(currentActive)
// }, 0)
//
// if (isExpand) return
// let parent = currentActive.parentNode
//
// for (; !parent.matches(".toc"); parent = parent.parentNode) {
// if (parent.matches("li")) parent.classList.add("active")
// }
// }
// window.tocScrollFn = function () {
// return btf.throttle(function () {
// let currentTop = window.scrollY || document.documentElement.scrollTop;
// // scrollPercent(currentTop)
// findHeadPosition(currentTop);
// }, 100)();
// };
// window.addEventListener("scroll", tocScrollFn);
window.tocScrollFn = function () {
return btf.throttle(function () {
let currentTop = window.scrollY || document.documentElement.scrollTop;
scrollPercent(currentTop)
findHeadPosition(currentTop);
}, 100)();
};
window.addEventListener("scroll", tocScrollFn);
};
const tabsFn = {

View File

@ -179,6 +179,17 @@ const btf = {
}
return actualTop
},
updateAnchor: (anchor) => {
if (anchor !== window.location.hash) {
if (!anchor) anchor = location.pathname
const title = "123111111111"
window.history.replaceState({
url: location.href,
title: title
}, title, anchor)
}
}
};

View File

@ -1,12 +1,33 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 侧边栏 -->
<div class="aside-content" id="aside-content" th:fragment="aside(widgets)"
th:if="${theme.config.sidebar.location != 'hide-aside' && not #strings.isEmpty(widgets)}">
<!-- 侧栏部件不确定都有什么 -->
<th:block th:each="widget : ${#strings.listSplit(widgets, ',')}">
<th:block th:replace="'modules/widgets/aside/'+ ${widget}"/>
<!-- 侧栏部件,不包含 toc 则直接遍历 -->
<th:block th:if="${not #strings.contains(widgets, 'toc')}">
<th:block th:each="widget : ${#strings.listSplit(widgets, ',')}">
<th:block th:replace="'modules/widgets/aside/'+ ${widget}"/>
</th:block>
</th:block>
<!-- 侧栏部件toc 之后的组件需要被 sticky_layout 包裹 -->
<th:block th:if="${#strings.contains(widgets, 'toc')}">
<th:block th:each="widget : ${#strings.listSplit(#strings.substringBefore(widgets, 'toc'), ',')}">
<th:block th:replace="'modules/widgets/aside/'+ ${widget}"/>
</th:block>
<!-- toc 之后的组件需要被 sticky_layout 包裹 -->
<div class="sticky_layout">
<th:block th:replace="modules/widgets/aside/toc :: toc"/>
<th:block th:each="widget : ${#strings.listSplit(#strings.substringAfter(widgets, 'toc'), ',')}">
<th:block th:replace="'modules/widgets/aside/'+ ${widget}"/>
</th:block>
</div>
</th:block>

View File

@ -47,10 +47,9 @@
});
tocbot.init({
tocSelector: '.toc-content',
contentSelector: '.post-content',
contentSelector: content,
headingSelector: headerEl,
hasInnerContainers: true,
});
}