fix(toc): fix toc link bug

This commit is contained in:
Dillon
2020-02-21 11:11:56 +08:00
parent 41a14bc2dc
commit a4116f14a7
12 changed files with 183 additions and 177 deletions

View File

@@ -1,6 +1,7 @@
html { html {
font-family: $global-font-family; font-family: $global-font-family;
font-size: 16px; font-size: 16px;
width:100%;
} }
/* scrollbar, only support webkit */ /* scrollbar, only support webkit */

View File

@@ -2,8 +2,7 @@
.page { .page {
max-width: 780px; max-width: 780px;
.post-toc { #toc-auto {
width: 300px;
margin-left: 800px; margin-left: 800px;
} }
} }
@@ -13,8 +12,7 @@
.page { .page {
max-width: 680px; max-width: 680px;
.post-toc { #toc-auto {
width: 240px;
margin-left: 700px; margin-left: 700px;
} }
} }
@@ -24,8 +22,7 @@
.page { .page {
max-width: 560px; max-width: 560px;
.post-toc { #toc-auto {
width: 180px;
margin-left: 580px; margin-left: 580px;
} }
} }
@@ -41,11 +38,11 @@
.page { .page {
max-width: 80%; max-width: 80%;
.post-toc { #toc-auto {
display: none; display: none;
} }
.post-toc-mobile { #toc-static {
display: block; display: block;
} }
} }

View File

@@ -1,25 +1,25 @@
.post-toc { #toc-auto {
display: block; display: block;
position: absolute; position: absolute;
width: 360px; width: 100%;
margin-left: 1000px; margin-left: 1000px;
padding: 0 .8rem; padding: 0 .8rem;
border-left: 1px solid $global-border-color; border-left: 1px solid $global-border-color;
overflow-wrap: break-word; overflow-wrap: break-word;
box-sizing: border-box; box-sizing: border-box;
top: $post-toc-top; top: if($header-normal-mode-desktop, 7rem, 12rem);
.dark-theme & { .dark-theme & {
border-left: 1px solid $global-border-color-dark; border-left: 1px solid $global-border-color-dark;
} }
.post-toc-title { .toc-title {
font-weight: 400; font-weight: 400;
margin: .8rem 0; margin: .8rem 0;
text-transform: uppercase; text-transform: uppercase;
} }
.post-toc-content { .toc-content {
&.always-active ul { &.always-active ul {
display: block; display: block;
} }
@@ -73,13 +73,13 @@
} }
} }
.post-toc-mobile { #toc-static {
display: none; display: none;
padding-top: .8rem; padding-top: .8rem;
details { details {
summary { summary {
.post-toc-title { .toc-title {
display: block; display: block;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -110,7 +110,7 @@
} }
} }
.post-toc-content { .toc-content {
border: 2px solid $code-background-color; border: 2px solid $code-background-color;
>nav>ul { >nav>ul {

View File

@@ -62,9 +62,6 @@ $header-position-mobile: if($header-normal-mode-mobile, static, fixed) !default;
$page-padding-top-desktop: if($header-normal-mode-desktop, 1rem, 6rem) !default; $page-padding-top-desktop: if($header-normal-mode-desktop, 1rem, 6rem) !default;
$page-padding-top-mobile: if($header-normal-mode-mobile, 1rem, 6rem) !default; $page-padding-top-mobile: if($header-normal-mode-mobile, 1rem, 6rem) !default;
// Top of the post toc
$post-toc-top: if($header-normal-mode-desktop, 7rem, 12rem) !default;
// Color of the hover header item // Color of the hover header item
$header-hover-color: #161209 !default; $header-hover-color: #161209 !default;
$header-hover-color-dark: #fff !default; $header-hover-color-dark: #fff !default;

View File

@@ -59,7 +59,7 @@ themesDir = "../.."
pre = '<i class="fas fa-language fa-fw"></i>' pre = '<i class="fas fa-language fa-fw"></i>'
name = "" name = ""
title = "简体中文" title = "简体中文"
url = "zh" url = "/zh/"
weight = 5 weight = 5
[languages.en.params] [languages.en.params]
# LoveIt theme version # LoveIt theme version

View File

@@ -28,7 +28,7 @@
{{- .Site.Title -}} {{- .Site.Title -}}
</a> </a>
</div> </div>
<div class="menu-toggle" id="menu-toggle"> <div class="menu-toggle" id="menu-toggle-mobile">
<span></span><span></span><span></span> <span></span><span></span><span></span>
</div> </div>
</div> </div>

View File

@@ -162,7 +162,6 @@
{{- $key }}: {{ $var | safeJS -}}, {{- $key }}: {{ $var | safeJS -}},
{{- end -}} {{- end -}}
}; };
window.echartsArr = [];
</script> </script>
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}

View File

@@ -47,28 +47,24 @@
{{- /* TOC */ -}} {{- /* TOC */ -}}
{{- if or .Params.toc (and .Site.Params.toc (ne .Params.toc false)) -}} {{- if or .Params.toc (and .Site.Params.toc (ne .Params.toc false)) -}}
<aside class="post-toc" id="post-toc"> <div class="toc-auto" id="toc-auto">
<h2 class="post-toc-title">{{ T "toc" }}</h2> <h2 class="toc-title">{{ T "toc" }}</h2>
{{- $globalAutoCollapseToc := .Site.Params.autoCollapseToc | default true }} {{- $globalAutoCollapseToc := .Site.Params.autoCollapseToc | default true }}
<div class="post-toc-content{{ if not (and $globalAutoCollapseToc (ne .Params.autoCollapseToc false)) }} always-active{{ end }}"> <div class="toc-content{{ if not (and $globalAutoCollapseToc (ne .Params.autoCollapseToc false)) }} always-active{{ end }}" id="toc-content-auto"></div>
{{- .TableOfContents -}} </div>
</div> <div class="toc-static" id="toc-static">
</aside>
<aside class="post-toc-mobile" id="post-toc-mobile">
<details> <details>
<summary> <summary>
<div class="post-toc-title"> <div class="toc-title">
<span>{{ T "toc" }}</span> <span>{{ T "toc" }}</span>
<span><i class="details icon fas fa-angle-down"></i></span> <span><i class="details icon fas fa-angle-down"></i></span>
</div> </div>
</summary> </summary>
<div class="post-toc-content"> <div class="toc-content" id="toc-content-static">
{{- $toc := .TableOfContents -}} {{- .TableOfContents -}}
{{- $toc = replaceRE `id="TableOfContents"` `id="TableOfContentsMobile"` $toc -}}
{{- $toc | safeHTML -}}
</div> </div>
</details> </details>
</aside> </div>
{{- end -}} {{- end -}}
{{- /* Content */ -}} {{- /* Content */ -}}

View File

@@ -12,20 +12,41 @@
getScrollTop() { getScrollTop() {
return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
} }
isMobile() {
return window.matchMedia('only screen and (max-width: 560px)').matches;
}
isTocStatic() {
return window.matchMedia('only screen and (max-width: 960px)').matches;
}
} }
class Theme { class Theme {
constructor() { constructor() {
this.util = new Util(); this.util = new Util();
this.scrollTop = 0; this.newScrollTop = this.util.getScrollTop();
this.scrollEvents = []; this.oldScrollTop = this.newScrollTop;
this.scrollEventSet = new Set();
this.resizeEventSet = new Set();
} }
initMobileMenu() { initMenuMobile() {
document.getElementById('menu-toggle').onclick = () => { const menuToggleMobile = document.getElementById('menu-toggle-mobile');
document.getElementById('menu-toggle').classList.toggle('active'); const menuMobile = document.getElementById('menu-mobile');
document.getElementById('menu-mobile').classList.toggle('active'); this._menuMobileOnScroll = this._menuMobileOnScroll || (() => {
}; menuToggleMobile.classList.remove('active');
menuMobile.classList.remove('active');
});
if (this.util.isMobile()) {
menuToggleMobile.onclick = () => {
menuToggleMobile.classList.toggle('active');
menuMobile.classList.toggle('active');
};
this.scrollEventSet.add(this._menuMobileOnScroll);
} else {
this.scrollEventSet.delete(this._menuMobileOnScroll);
}
} }
initSwitchTheme() { initSwitchTheme() {
@@ -80,46 +101,55 @@
} }
} }
_refactorToc(toc) { initToc() {
this.util.forEach(toc.querySelectorAll('a:first-child'), (link) => { const tocCore = document.getElementById('TableOfContents');
link.classList.add('toc-link'); if (tocCore === null) return;
}); if (this.util.isTocStatic()) {
} const tocContentStatic = document.getElementById('toc-content-static');
if (tocCore.parentElement !== tocContentStatic) {
_initTocState(tocContainer) { tocCore.parentElement.removeChild(tocCore);
if (window.getComputedStyle(tocContainer, null).display !== 'none') { tocContentStatic.appendChild(tocCore);
const fixed = window.desktopHeaderMode !== 'normal'; }
const fixedHeight = document.getElementById('header-desktop').getBoundingClientRect().height; if (this._tocOnScroll) this.scrollEventSet.delete(this._tocOnScroll);
const TOP_SPACING = 20 + (fixed ? fixedHeight : 0); } else {
const minTop = tocContainer.offsetTop; const tocContentAuto = document.getElementById('toc-content-auto');
const minScrollTop = minTop - TOP_SPACING + (fixed ? 0 : fixedHeight); if (tocCore.parentElement !== tocContentAuto) {
const footerTop = document.getElementById('post-footer').offsetTop; tocCore.parentElement.removeChild(tocCore);
const toclinks = tocContainer.getElementsByClassName('toc-link'); tocContentAuto.appendChild(tocCore);
const headerLinks = document.getElementsByClassName('headerLink') || []; }
const tocLinkLis = tocContainer.querySelectorAll('.post-toc-content li'); const toc = document.getElementById('toc-auto');
const INDEX_SPACING = 5 + (fixed ? fixedHeight : 0); const page = document.getElementsByClassName('page')[0];
toc.style.maxWidth = `${page.getBoundingClientRect().left - 40}px`;
const changeTocState = () => { this._tocLinks = this._tocLinks || tocCore.getElementsByTagName('a');
const scrollTop = this.util.getScrollTop(); this._tocLis = this._tocLis || tocCore.getElementsByTagName('li');
const maxTop = footerTop - tocContainer.getBoundingClientRect().height; this._headerLinks = this._headerLinks || document.getElementsByClassName('headerLink') || [];
const maxScrollTop = maxTop - TOP_SPACING + (fixed ? 0 : fixedHeight); const headerIsFixed = window.desktopHeaderMode !== 'normal';
if (scrollTop < minScrollTop) { const headerHeight = document.getElementById('header-desktop').offsetHeight;
tocContainer.style.position = 'absolute'; const TOP_SPACING = 20 + (headerIsFixed ? headerHeight : 0);
tocContainer.style.top = `${minTop}px`; const minTocTop = toc.offsetTop;
} else if (scrollTop > maxScrollTop) { const minScrollTop = minTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight);
tocContainer.style.position = 'absolute'; this._tocOnScroll = this._tocOnScroll || (() => {
tocContainer.style.top = `${maxTop}px`; const footerTop = document.getElementById('post-footer').offsetTop;
const maxTocTop = footerTop - toc.getBoundingClientRect().height;
const maxScrollTop = maxTocTop - TOP_SPACING + (headerIsFixed ? 0 : headerHeight);
if (this.newScrollTop < minScrollTop) {
toc.style.position = 'absolute';
toc.style.top = `${minTocTop}px`;
} else if (this.newScrollTop > maxScrollTop) {
toc.style.position = 'absolute';
toc.style.top = `${maxTocTop}px`;
} else { } else {
tocContainer.style.position = 'fixed'; toc.style.position = 'fixed';
tocContainer.style.top = `${TOP_SPACING}px`; toc.style.top = `${TOP_SPACING}px`;
} }
this.util.forEach(toclinks, (link) => { link.classList.remove('active'); }); this.util.forEach(this._tocLinks, (link) => { link.classList.remove('active'); });
this.util.forEach(tocLinkLis, (link) => { link.classList.remove('has-active'); }); this.util.forEach(this._tocLis, (link) => { link.classList.remove('has-active'); });
let activeTocIndex = headerLinks.length - 1; const INDEX_SPACING = 20 + (headerIsFixed ? headerHeight : 0);
for (let i = 0; i < headerLinks.length - 1; i++) { let activeTocIndex = this._headerLinks.length - 1;
const thisTop = headerLinks[i].getBoundingClientRect().top; for (let i = 0; i < this._headerLinks.length - 1; i++) {
const nextTop = headerLinks[i + 1].getBoundingClientRect().top; const thisTop = this._headerLinks[i].getBoundingClientRect().top;
const nextTop = this._headerLinks[i + 1].getBoundingClientRect().top;
if ((i == 0 && thisTop > INDEX_SPACING) if ((i == 0 && thisTop > INDEX_SPACING)
|| (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) { || (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) {
activeTocIndex = i; activeTocIndex = i;
@@ -127,47 +157,25 @@
} }
} }
if (activeTocIndex !== -1) { if (activeTocIndex !== -1) {
toclinks[activeTocIndex].classList.add('active'); this._tocLinks[activeTocIndex].classList.add('active');
let parent = toclinks[activeTocIndex].parentElement; let parent = this._tocLinks[activeTocIndex].parentElement;
while (parent.tagName !== 'NAV') { while (parent !== tocCore) {
parent.classList.add('has-active'); parent.classList.add('has-active');
parent = parent.parentElement.parentElement; parent = parent.parentElement.parentElement;
} }
} }
}; });
changeTocState(); this._tocOnScroll();
this.scrollEventSet.add(this._tocOnScroll);
if (!this._initTocOnce) {
this.scrollEvents.push(changeTocState);
this._initTocOnce = true;
}
}
}
initToc() {
const tocContainer = document.getElementById('post-toc');
if (tocContainer !== null) {
const toc = document.getElementById('TableOfContents');
if (toc === null) {
tocContainer.parentElement.removeChild(tocContainer);
} else {
this._refactorToc(toc);
this._initTocState(tocContainer);
window.addEventListener('resize', () => {
window.setTimeout(() => {
this._initTocState(tocContainer);
}, 0);
}, false);
}
} }
} }
initMermaid() { initMermaid() {
if (window.mermaidMap) { if (window.mermaidMap) {
mermaid.initialize({startOnLoad: false, theme: null}); mermaid.initialize({startOnLoad: false, theme: null});
Object.keys(mermaidMap).forEach((id) => { Object.keys(window.mermaidMap).forEach((id) => {
const element = document.getElementById(id); const element = document.getElementById(id);
mermaid.mermaidAPI.render("d" + id, mermaidMap[id], (svgCode) => { mermaid.mermaidAPI.render("d" + id, window.mermaidMap[id], (svgCode) => {
element.innerHTML = svgCode; element.innerHTML = svgCode;
const svg = element.firstChild; const svg = element.firstChild;
svg.style.width = "100%" svg.style.width = "100%"
@@ -178,29 +186,29 @@
initEcharts() { initEcharts() {
if (window.echartsMap) { if (window.echartsMap) {
for (let i = 0; i < echartsArr.length; i++) { this._echartsArr = this._echartsArr || [];
echartsArr[i].dispose(); for (let i = 0; i < this._echartsArr.length; i++) {
this._echartsArr[i].dispose();
} }
echartsArr = []; this._echartsArr = [];
Object.keys(echartsMap).forEach((id) => { Object.keys(window.echartsMap).forEach((id) => {
let myChart = echarts.init(document.getElementById(id), window.isDark ? 'dark' : 'macarons', {renderer: 'svg'}); const chart = echarts.init(document.getElementById(id), window.isDark ? 'dark' : 'macarons', {renderer: 'svg'});
myChart.setOption(echartsMap[id]); chart.setOption(window.echartsMap[id]);
echartsArr.push(myChart); this._echartsArr.push(chart);
}); });
window.addEventListener("resize", function () { this._echartsOnResize = this._echartsOnResize || (() => {
this.setTimeout(() => { for (let i = 0; i < this._echartsArr.length; i++) {
for (let i = 0; i < echartsArr.length; i++) { this._echartsArr[i].resize();
echartsArr[i].resize(); }
} });
}, 0); this.resizeEventSet.add(this._echartsOnResize);
}, false);
} }
} }
initTypeit() { initTypeit() {
if (window.typeitArr) { if (window.typeitArr) {
for (let i = 0; i < typeitArr.length; i++) { for (let i = 0; i < window.typeitArr.length; i++) {
const group = typeitArr[i]; const group = window.typeitArr[i];
(function typeone(i) { (function typeone(i) {
const content = document.getElementById(`r${group[i]}`).innerHTML; const content = document.getElementById(`r${group[i]}`).innerHTML;
if (i === group.length - 1) { if (i === group.length - 1) {
@@ -221,25 +229,16 @@
} }
} }
initScroll() { initSmoothScroll() {
for (let i = 0; i < this.scrollEvents.length; i++) { if ((!this.util.isMobile() && window.desktopHeaderMode === 'normal')
document.addEventListener('scroll', this.scrollEvents[i], false); || (this.util.isMobile() && window.mobileHeaderMode === 'normal')) {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true});
} else {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'});
} }
const initSmoothScroll = () => { }
const isMobile = window.matchMedia('only screen and (max-width: 560px)').matches;
if ((!isMobile && window.desktopHeaderMode === 'normal') onScroll() {
|| (isMobile && window.mobileHeaderMode === 'normal')) {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true});
} else {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'});
}
};
initSmoothScroll();
window.addEventListener('resize', () => {
window.setTimeout(() => {
initSmoothScroll();
}, 0);
}, false);
const headers = []; const headers = [];
if (window.desktopHeaderMode === 'auto') headers.push(document.getElementById('header-desktop')); if (window.desktopHeaderMode === 'auto') headers.push(document.getElementById('header-desktop'));
if (window.mobileHeaderMode === 'auto') headers.push(document.getElementById('header-mobile')); if (window.mobileHeaderMode === 'auto') headers.push(document.getElementById('header-mobile'));
@@ -248,43 +247,57 @@
header.classList.add('faster'); header.classList.add('faster');
}); });
const toTopButton = document.getElementById('dynamic-to-top'); const toTopButton = document.getElementById('dynamic-to-top');
document.addEventListener('scroll', () => { const MIN_SCROLL = 20;
const scrollTop = this.util.getScrollTop(); window.addEventListener('scroll', () => {
this.newScrollTop = this.util.getScrollTop();
const scroll = this.newScrollTop - this.oldScrollTop;
this.util.forEach(headers, (header) => { this.util.forEach(headers, (header) => {
if (this.scrollTop < scrollTop) { if (scroll > MIN_SCROLL) {
if (!header.classList.contains('fadeOutUp')) { header.classList.remove('fadeInDown');
header.classList.remove('fadeInDown'); header.classList.add('fadeOutUp');
header.classList.add('fadeOutUp'); } else if (scroll < - MIN_SCROLL) {
} header.classList.remove('fadeOutUp');
} else { header.classList.add('fadeInDown');
if (!header.classList.contains('fadeInDown')) {
header.classList.remove('fadeOutUp');
header.classList.add('fadeInDown');
}
}
if (scrollTop > 600) {
if (this.scrollTop < scrollTop) {
if (!toTopButton.classList.contains('fadeOut')) {
toTopButton.classList.remove('fadeIn');
toTopButton.classList.add('fadeOut');
}
} else {
toTopButton.style.display = 'block';
if (!toTopButton.classList.contains('fadeIn')) {
toTopButton.classList.remove('fadeOut');
toTopButton.classList.add('fadeIn');
}
}
} else {
toTopButton.style.display = 'none';
} }
}); });
this.scrollTop = scrollTop; if (this.newScrollTop > 400) {
if (scroll > MIN_SCROLL) {
toTopButton.classList.remove('fadeIn');
toTopButton.classList.add('fadeOut');
} else if (scroll < - MIN_SCROLL) {
toTopButton.style.display = 'block';
toTopButton.classList.remove('fadeOut');
toTopButton.classList.add('fadeIn');
}
} else {
toTopButton.style.display = 'none';
}
if (!this._scrollTimeout) {
this._scrollTimeout = window.setTimeout(() => {
this._scrollTimeout = null;
for (let event of this.scrollEventSet) event();
}, 10);
}
this.oldScrollTop = this.newScrollTop;
}, false);
}
onResize() {
window.addEventListener('resize', () => {
if (!this._resizeTimeout) {
this._resizeTimeout = window.setTimeout(() => {
this._resizeTimeout = null;
for (let event of this.resizeEventSet) event();
this.initMenuMobile();
this.initToc();
this.initSmoothScroll();
}, 100);
}
}, false); }, false);
} }
init() { init() {
this.initMobileMenu(); this.initMenuMobile();
this.initSwitchTheme(); this.initSwitchTheme();
this.initHighlight(); this.initHighlight();
this.initTable(); this.initTable();
@@ -293,7 +306,10 @@
this.initEcharts(); this.initEcharts();
this.initTypeit(); this.initTypeit();
this.initToc(); this.initToc();
this.initScroll(); this.initSmoothScroll();
this.onScroll();
this.onResize();
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long