MediaWiki:Common.js: Difference between revisions
Jump to navigation
Jump to search
Created page with "→Any JavaScript here will be loaded for all users on every page load.: →TOC - floating: mw.hook('wikipage.content').add(function () { const toc = document.getElementById('toc'); if (!toc) return; const tocTop = toc.getBoundingClientRect().top + window.scrollY; function onScroll() { if (window.scrollY > tocTop) { toc.classList.add('toc-floating'); } else { toc.classList.remove('toc-floating'); } } // Desktop only listener..." |
No edit summary |
||
| Line 4: | Line 4: | ||
TOC - floating | TOC - floating | ||
*/ | */ | ||
mw.hook('wikipage.content').add(function () { | mw.hook('wikipage.content').add(function () { | ||
const toc = document.getElementById('toc'); | const toc = document.getElementById('toc'); | ||
if (!toc) return; | if (!toc) return; | ||
// Avoid duplicating if MW re-renders content | |||
if (toc.querySelector('.toc-fold-btn')) return; | |||
// Insert fold button | |||
const btn = document.createElement('button'); | |||
btn.type = 'button'; | |||
btn.className = 'toc-fold-btn'; | |||
btn.setAttribute('aria-expanded', 'true'); | |||
btn.setAttribute('title', 'Hide table of contents'); | |||
btn.textContent = '⟨⟨'; // folded indicator; we’ll flip it dynamically | |||
toc.prepend(btn); | |||
const STORAGE_KEY = 'mw_toc_folded'; | |||
function setFolded(folded) { | |||
toc.classList.toggle('toc-folded', folded); | |||
btn.setAttribute('aria-expanded', String(!folded)); | |||
btn.setAttribute('title', folded ? 'Show table of contents' : 'Hide table of contents'); | |||
btn.textContent = folded ? '⟩⟩' : '⟨⟨'; | |||
try { localStorage.setItem(STORAGE_KEY, folded ? '1' : '0'); } catch (e) {} | |||
} | |||
// Restore folded state | |||
let folded = false; | |||
try { folded = localStorage.getItem(STORAGE_KEY) === '1'; } catch (e) {} | |||
setFolded(folded); | |||
btn.addEventListener('click', function (e) { | |||
e.preventDefault(); | |||
folded = !toc.classList.contains('toc-folded'); | |||
setFolded(folded); | |||
}); | |||
// Don't float if not using desktop | |||
if (window.innerWidth < 1200) return; | |||
const tocTop = toc.getBoundingClientRect().top + window.scrollY; | const tocTop = toc.getBoundingClientRect().top + window.scrollY; | ||
// Floating trigger: only float after scrolling past original position | |||
function onScroll() { | function onScroll() { | ||
if (window.scrollY > tocTop) { | if (window.scrollY > tocTop) { | ||
| Line 15: | Line 51: | ||
} else { | } else { | ||
toc.classList.remove('toc-floating'); | toc.classList.remove('toc-floating'); | ||
// optional: when not floating, don’t keep it folded-looking | |||
toc.classList.remove('toc-folded'); | |||
btn.setAttribute('aria-expanded', 'true'); | |||
btn.setAttribute('title', 'Hide table of contents'); | |||
btn.textContent = '⟨⟨'; | |||
} | } | ||
} | } | ||
window.addEventListener('scroll', onScroll, { passive: true }); | |||
window.addEventListener('scroll', onScroll); | onScroll(); | ||
}); | }); | ||
// Prevent overlap with footer | // Prevent overlap with footer | ||
const footer = document.querySelector('#footer'); | const footer = document.querySelector('#footer'); | ||
Revision as of 13:40, 10 February 2026
/* Any JavaScript here will be loaded for all users on every page load. */
/*
TOC - floating
*/
mw.hook('wikipage.content').add(function () {
const toc = document.getElementById('toc');
if (!toc) return;
// Avoid duplicating if MW re-renders content
if (toc.querySelector('.toc-fold-btn')) return;
// Insert fold button
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'toc-fold-btn';
btn.setAttribute('aria-expanded', 'true');
btn.setAttribute('title', 'Hide table of contents');
btn.textContent = '⟨⟨'; // folded indicator; we’ll flip it dynamically
toc.prepend(btn);
const STORAGE_KEY = 'mw_toc_folded';
function setFolded(folded) {
toc.classList.toggle('toc-folded', folded);
btn.setAttribute('aria-expanded', String(!folded));
btn.setAttribute('title', folded ? 'Show table of contents' : 'Hide table of contents');
btn.textContent = folded ? '⟩⟩' : '⟨⟨';
try { localStorage.setItem(STORAGE_KEY, folded ? '1' : '0'); } catch (e) {}
}
// Restore folded state
let folded = false;
try { folded = localStorage.getItem(STORAGE_KEY) === '1'; } catch (e) {}
setFolded(folded);
btn.addEventListener('click', function (e) {
e.preventDefault();
folded = !toc.classList.contains('toc-folded');
setFolded(folded);
});
// Don't float if not using desktop
if (window.innerWidth < 1200) return;
const tocTop = toc.getBoundingClientRect().top + window.scrollY;
// Floating trigger: only float after scrolling past original position
function onScroll() {
if (window.scrollY > tocTop) {
toc.classList.add('toc-floating');
} else {
toc.classList.remove('toc-floating');
// optional: when not floating, don’t keep it folded-looking
toc.classList.remove('toc-folded');
btn.setAttribute('aria-expanded', 'true');
btn.setAttribute('title', 'Hide table of contents');
btn.textContent = '⟨⟨';
}
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
});
// Prevent overlap with footer
const footer = document.querySelector('#footer');
const footerTop = footer.getBoundingClientRect().top + window.scrollY;
if (window.scrollY + toc.offsetHeight + 100 > footerTop) {
toc.style.opacity = '0';
} else {
toc.style.opacity = '1';
}