MediaWiki:ShowFirstTabs.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
(function () {
'use strict';
function getTabTargetId(tabElement) {
if (!tabElement) return null;
const dataTab = tabElement.getAttribute('data-tab');
if (dataTab) return dataTab;
const ariaControls = tabElement.getAttribute('aria-controls');
if (ariaControls) return ariaControls;
const href = tabElement.getAttribute('href');
if (href && href.charAt(0) === '#') return href.slice(1);
return null;
}
function findScopedElementById(scopeRoot, id) {
if (!id) return null;
const element = document.getElementById(id);
if (!element) return null;
if (scopeRoot && scopeRoot !== document && !scopeRoot.contains(element)) return null;
return element;
}
function setActiveState(element, isActive) {
if (!element) return;
element.classList.toggle('active', isActive);
if (element.hasAttribute('aria-selected')) {
element.setAttribute('aria-selected', isActive ? 'true' : 'false');
}
if (element.hasAttribute('aria-hidden')) {
element.setAttribute('aria-hidden', isActive ? 'false' : 'true');
}
if (element.hasAttribute('tabindex')) {
element.setAttribute('tabindex', isActive ? '0' : '-1');
}
}
function getScopeForTablist(tablistElement) {
if (!tablistElement) return document;
if (tablistElement.classList.contains('nav-tabs')) {
return tablistElement.closest('.tabs-container') || tablistElement.parentElement || document;
}
return tablistElement.closest('.nested-content, .tab-content') || document;
}
function showNavTab(tabElement) {
const tablist = tabElement.closest('.nav-tabs');
if (!tablist) return;
const scope = getScopeForTablist(tablist);
const tabs = Array.from(tablist.querySelectorAll('.nav-tab[data-tab]'));
const tabMappings = tabs
.map((tab) => ({ tab, id: getTabTargetId(tab) }))
.filter((m) => !!m.id);
const targetId = getTabTargetId(tabElement);
if (!targetId) return;
for (const mapping of tabMappings) {
setActiveState(mapping.tab, mapping.tab === tabElement);
const panel = findScopedElementById(scope, mapping.id);
if (panel && panel.classList.contains('tab-content')) {
setActiveState(panel, mapping.id === targetId);
}
}
const targetPanel = findScopedElementById(scope, targetId);
if (targetPanel && targetPanel.classList.contains('tab-content')) {
setActiveState(targetPanel, true);
ensureNestedChain(targetPanel);
}
}
function showNestedTab(tabElement) {
const tablist = tabElement.closest('.nested-tabs');
if (!tablist) return;
const scope = getScopeForTablist(tablist);
const tabs = Array.from(tablist.querySelectorAll('.nested-tab[data-tab]'));
const tabMappings = tabs
.map((tab) => ({ tab, id: getTabTargetId(tab) }))
.filter((m) => !!m.id);
const targetId = getTabTargetId(tabElement);
if (!targetId) return;
for (const mapping of tabMappings) {
setActiveState(mapping.tab, mapping.tab === tabElement);
const panel = findScopedElementById(scope, mapping.id);
if (panel && panel.classList.contains('nested-content')) {
setActiveState(panel, mapping.id === targetId);
}
}
const targetPanel = findScopedElementById(scope, targetId);
if (targetPanel && targetPanel.classList.contains('nested-content')) {
setActiveState(targetPanel, true);
ensureNestedChain(targetPanel);
}
}
function findDirectNestedTablist(containerElement) {
if (!containerElement) return null;
const tablists = Array.from(containerElement.querySelectorAll('.nested-tabs'));
if (tablists.length === 0) return null;
const containerIsNested = containerElement.classList.contains('nested-content');
for (const tablist of tablists) {
const closestNestedContent = tablist.closest('.nested-content');
if (containerIsNested) {
if (closestNestedContent === containerElement) return tablist;
} else {
if (!closestNestedContent) return tablist;
}
}
return null;
}
function ensureNestedChain(activeContainer) {
let currentContainer = activeContainer;
while (currentContainer) {
const nestedTablist = findDirectNestedTablist(currentContainer);
if (!nestedTablist) return;
const nestedTabs = Array.from(nestedTablist.querySelectorAll('.nested-tab[data-tab]'));
if (nestedTabs.length === 0) return;
let tabToActivate = nestedTabs.find((t) => t.classList.contains('active'));
if (!tabToActivate) tabToActivate = nestedTabs[0];
const targetId = getTabTargetId(tabToActivate);
if (!targetId) return;
showNestedTab(tabToActivate);
const nextContainer = findScopedElementById(currentContainer, targetId);
if (!nextContainer || !nextContainer.classList.contains('nested-content')) return;
currentContainer = nextContainer;
}
}
function handleSpecialTrigger(event) {
const trigger = event.target.closest('[data-tab-trigger][data-tab-button]');
if (!trigger) return false;
event.preventDefault();
const buttonId = trigger.getAttribute('data-tab-button');
const tabId = trigger.getAttribute('data-tab-trigger');
let tabButton = null;
if (buttonId) tabButton = document.getElementById(buttonId);
if (!tabButton && tabId) tabButton = document.querySelector('.nav-tab[data-tab="' + tabId + '"], .nested-tab[data-tab="' + tabId + '"]');
if (!tabButton) return true;
if (tabButton.classList.contains('nav-tab')) showNavTab(tabButton);
else if (tabButton.classList.contains('nested-tab')) showNestedTab(tabButton);
const href = trigger.getAttribute('href');
if (href && href.charAt(0) === '#') {
const anchor = document.querySelector(href);
if (anchor) {
setTimeout(() => anchor.scrollIntoView({ behavior: 'smooth', block: 'start' }), 100);
}
}
return true;
}
function onDocumentClick(event) {
if (handleSpecialTrigger(event)) return;
const navTab = event.target.closest('.nav-tab[data-tab]');
if (navTab) {
event.preventDefault();
showNavTab(navTab);
return;
}
const nestedTab = event.target.closest('.nested-tab[data-tab]');
if (nestedTab) {
event.preventDefault();
showNestedTab(nestedTab);
}
}
function normalizeInitialState() {
const navTablists = Array.from(document.querySelectorAll('.nav-tabs'));
for (const tablist of navTablists) {
const activeTab = tablist.querySelector('.nav-tab.active[data-tab]') || tablist.querySelector('.nav-tab[data-tab]');
if (activeTab) showNavTab(activeTab);
}
const activeTabPanels = Array.from(document.querySelectorAll('.tab-content.active'));
for (const panel of activeTabPanels) ensureNestedChain(panel);
const activeNestedPanels = Array.from(document.querySelectorAll('.nested-content.active'));
for (const panel of activeNestedPanels) ensureNestedChain(panel);
}
function init() {
document.addEventListener('click', onDocumentClick, false);
normalizeInitialState();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();