User:RoyZuo/AutoExpandTOC.js
Appearance
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
| Documentation for this user script can be added at User:RoyZuo/AutoExpandTOC. |
// ==UserScript==
// @name Vector TOC Expand/Collapse All
// @description Adds an Expand/Collapse all button next to the "Beginning" TOC item and auto-expands the TOC when fully loaded
// @version 1.0
// @author RoyZuo
// ==/UserScript==
(function () {
'use strict';
/**
* Get all Vector TOC toggle buttons inside a TOC
*/
function getToggles(toc) {
return toc.querySelectorAll('.vector-toc-toggle');
}
/**
* Check if all sections are expanded
*/
function allExpanded(toc) {
const toggles = getToggles(toc);
return toggles.length > 0 && Array.from(toggles).every(btn => btn.getAttribute('aria-expanded') === 'true');
}
/**
* Expand or collapse all TOC sections
*/
function setAll(toc, expand) {
getToggles(toc).forEach(btn => {
const isExpanded = btn.getAttribute('aria-expanded') === 'true';
if (expand !== isExpanded) btn.click();
});
}
/**
* Update the button label
*/
function updateLabel(button, toc) {
button.textContent = allExpanded(toc) ? 'Collapse all sections' : 'Expand all sections';
}
/**
* Insert the button next to "Beginning" TOC item
*/
function insertButton(toc) {
const beginningItem = toc.querySelector('#toc-mw-content-text');
if (!beginningItem) return false;
// Prevent duplicates
if (beginningItem.querySelector('.vector-toc-toggle-all')) return true;
const button = document.createElement('button');
button.type = 'button';
button.className = 'cdx-button cdx-button--weight-quiet vector-toc-toggle-all';
button.style.marginLeft = '0.5em';
button.style.fontSize = '0.875em';
updateLabel(button, toc);
button.addEventListener('click', () => {
const expand = !allExpanded(toc);
setAll(toc, expand);
updateLabel(button, toc);
});
const link = beginningItem.querySelector('.vector-toc-link');
if (!link) return false;
link.insertAdjacentElement('afterend', button);
// Auto-expand TOC once toggle buttons exist
setTimeout(() => {
setAll(toc, true);
updateLabel(button, toc);
}, 50);
return true;
}
/**
* Observe TOC for dynamic content
*/
function observeTOC() {
const toc = document.querySelector('.vector-toc');
if (!toc) return;
const observer = new MutationObserver(() => {
if (insertButton(toc)) {
observer.disconnect(); // stop observing once button is added
}
});
observer.observe(toc, { childList: true, subtree: true });
// Try immediately in case TOC already exists
insertButton(toc);
}
// Start after full page load
if (document.readyState === 'complete') {
observeTOC();
} else {
window.addEventListener('load', observeTOC);
}
})();