MediaWiki:Timeless.js:修订间差异
来自「荏苒之境」
无编辑摘要 |
无编辑摘要 |
||
第48行: | 第48行: | ||
class FloatingToC { | class FloatingToC { | ||
constructor(toc) { | constructor(toc) { | ||
this.element = toc; | |||
this.ignoreScrollUpdate = false; | this.ignoreScrollUpdate = false; | ||
第63行: | 第64行: | ||
document.getElementById('mw-content-text')); | document.getElementById('mw-content-text')); | ||
window.addEventListener('scroll', this. | this.scrollHandler = this.onScroll.bind(this); | ||
window.addEventListener('scroll', this.scrollHandler); | |||
} | |||
remove() { | |||
window.removeEventListener('scroll', this.scrollHandler); | |||
this.element.remove(); | |||
} | } | ||
第95行: | 第102行: | ||
} | } | ||
let siteNavigation = document.getElementById('mw-site-navigation'); | |||
let floatingToC; | let floatingToC; | ||
let toc = document.getElementById('toc'); | let toc = document.getElementById('toc'); | ||
document.addEventListener('replysubmit', () => { | document.addEventListener('replysubmit', () => { | ||
let new_toc = document.querySelectorAll('.mw-parser-output > #toc')[0]; | let new_toc = document.querySelectorAll('.mw-parser-output > #toc')[0]; | ||
if (new_toc != null) { | if (new_toc == null) { | ||
return; | |||
} | |||
if (floatingToC != null) { | |||
floatingToC.remove(); | |||
} | } | ||
toc = new_toc; | |||
toc.parentNode.removeChild(toc); | |||
siteNavigation.appendChild(toc); | |||
floatingToC = new FloatingToC(toc); | |||
}); | }); | ||
if (toc | if (toc) { | ||
toc.parentNode.removeChild(toc); | toc.parentNode.removeChild(toc); | ||
siteNavigation.appendChild(toc); | siteNavigation.appendChild(toc); | ||
floatingToC = new FloatingToC(toc); | floatingToC = new FloatingToC(toc); | ||
} | } |
2025年8月24日 (日) 13:14的版本
/* 将为Timeless皮肤的用户加载此处的所有JavaScript */
class HeadingTracker {
constructor(rootElement) {
this.rootElement = rootElement
this.headings = [];
this.cacheExpired = true;
this.init();
}
init() {
this.collectHeadings();
window.addEventListener('resize', () => this.cacheExpired = true);
window.addEventListener('DOMContentLoaded', () => this.cacheExpired = true);
}
collectHeadings() {
const rawHeadings = Array.from(this.rootElement.querySelectorAll('h1, h2, h3, h4, h5, h6'));
this.headings = rawHeadings
.map(heading => ({
element: heading,
top: heading.getBoundingClientRect().top + window.scrollY
}))
.sort((a, b) => a.top - b.top);
this.cacheExpired = false;
}
getNearestHeadingAboveViewport() {
if (this.cacheExpired) this.collectHeadings();
const scrollY = window.scrollY + 60;
let left = 0, right = this.headings.length - 1;
let result = null;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (this.headings[mid].top <= scrollY) {
result = this.headings[mid].element;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
}
class FloatingToC {
constructor(toc) {
this.element = toc;
this.ignoreScrollUpdate = false;
let tocTextList = Array.from(toc.querySelectorAll(".toctext"));
this.tocTexts = new Map(tocTextList.map(t => {
let textContent = t.textContent
t.parentElement.onclick = () => {
this.ignoreScrollUpdate = true;
this.setCurrentHeading(textContent);
};
return [textContent, t];
}));
this.tracker = new HeadingTracker(
document.getElementById('mw-content-text'));
this.scrollHandler = this.onScroll.bind(this);
window.addEventListener('scroll', this.scrollHandler);
}
remove() {
window.removeEventListener('scroll', this.scrollHandler);
this.element.remove();
}
setCurrentHeading(heading) {
const lastHeading = this.lastHeading;
if (heading == lastHeading) {
return;
}
if (lastHeading) {
let text = this.tocTexts.get(lastHeading)
if (text) text.classList.remove('current-heading');
}
this.lastHeading = heading;
if (heading) {
let text = this.tocTexts.get(heading)
if (text) text.classList.add('current-heading');
}
};
onScroll() {
if (window.innerWidth < 1100) {
return;
}
if (this.ignoreScrollUpdate) {
this.ignoreScrollUpdate = false;
return;
}
let heading = this.tracker.getNearestHeadingAboveViewport();
this.setCurrentHeading(heading ? heading.textContent : null);
}
}
let siteNavigation = document.getElementById('mw-site-navigation');
let floatingToC;
let toc = document.getElementById('toc');
document.addEventListener('replysubmit', () => {
let new_toc = document.querySelectorAll('.mw-parser-output > #toc')[0];
if (new_toc == null) {
return;
}
if (floatingToC != null) {
floatingToC.remove();
}
toc = new_toc;
toc.parentNode.removeChild(toc);
siteNavigation.appendChild(toc);
floatingToC = new FloatingToC(toc);
});
if (toc) {
toc.parentNode.removeChild(toc);
siteNavigation.appendChild(toc);
floatingToC = new FloatingToC(toc);
}