Widget:GameSwitcher:修订间差异
来自OGAS数据中枢
更多操作
无编辑摘要 |
无编辑摘要 |
||
| 第1行: | 第1行: | ||
<script> | <script type="text/javascript"> | ||
( | document.addEventListener('DOMContentLoaded', function () { | ||
var switcher = document.querySelector('.game-switcher'); | |||
var | if (!switcher) return; | ||
if (! | |||
var | var heroWrap = document.getElementById('gs-hero-wrap'); | ||
var | var heroImg = heroWrap ? heroWrap.querySelector('img') : null; | ||
var loaded = {}; | var loaded = {}; | ||
var prefetching = {}; | var prefetching = {}; | ||
var storageKey = 'gs-cache-'; | var storageKey = 'gs-cache-'; | ||
// ============ 初始化 ============ | // ============ 初始化 ============ | ||
var | var firstBtn = switcher.querySelector('.gs-tab-btn'); | ||
if ( | if (firstBtn) { | ||
firstBtn.classList.add('gs-active'); | |||
if ( | firstBtn.setAttribute('aria-selected', 'true'); | ||
if (heroImg && !heroImg.getAttribute('src')) { | |||
heroImg.src = firstBtn.dataset.hero; | |||
} | } | ||
} | } | ||
| 第22行: | 第22行: | ||
// ============ 点击 ============ | // ============ 点击 ============ | ||
switcher.addEventListener('click', function (e) { | |||
activate( | var btn = e.target.closest('.gs-tab-btn'); | ||
if (btn) activate(btn); | |||
}); | }); | ||
// ============ 键盘 ============ | // ============ 键盘 ============ | ||
switcher.addEventListener('keydown', function (e) { | |||
var btn = e.target.closest('.gs-tab-btn'); | |||
if (!btn) return; | |||
if (e.key === 'Enter' || e.key === ' ') { | if (e.key === 'Enter' || e.key === ' ') { | ||
e.preventDefault(); | e.preventDefault(); | ||
activate( | activate(btn); | ||
} | } | ||
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { | if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { | ||
e.preventDefault(); | e.preventDefault(); | ||
var | var btns = Array.from(switcher.querySelectorAll('.gs-tab-btn')); | ||
var idx = | var idx = btns.indexOf(btn); | ||
var next = e.key === 'ArrowRight' ? idx + 1 : idx - 1; | var next = e.key === 'ArrowRight' ? idx + 1 : idx - 1; | ||
if (next >= 0 && next < | if (next >= 0 && next < btns.length) btns[next].focus(); | ||
} | } | ||
}); | }); | ||
// ============ 悬停预加载 ============ | // ============ 悬停预加载 ============ | ||
switcher.addEventListener('mouseenter', function (e) { | |||
var panelId = | var btn = e.target.closest('.gs-tab-btn'); | ||
var pageName = | if (!btn) return; | ||
var panelId = btn.dataset.panel; | |||
var pageName = btn.dataset.page; | |||
if (pageName && !loaded[panelId] && !prefetching[panelId]) { | if (pageName && !loaded[panelId] && !prefetching[panelId]) { | ||
prefetch(panelId, pageName); | prefetch(panelId, pageName); | ||
} | } | ||
}); | }, true); | ||
// ============ URL hash ============ | // ============ URL hash ============ | ||
window.addEventListener('hashchange', restoreHash); | |||
// ============ 激活面板 ============ | // ============ 激活面板 ============ | ||
function activate( | function activate(btn) { | ||
if ( | if (btn.classList.contains('gs-active')) return; | ||
var panelId = | var panelId = btn.dataset.panel; | ||
var heroSrc = | var heroSrc = btn.dataset.hero; | ||
var pageName = | var pageName = btn.dataset.page; | ||
switchHero(heroSrc); | switchHero(heroSrc); | ||
switcher.querySelectorAll('.gs-tab-btn').forEach(function (b) { | |||
b.classList.remove('gs-active'); | |||
. | b.setAttribute('aria-selected', 'false'); | ||
. | }); | ||
btn.classList.add('gs-active'); | |||
btn.setAttribute('aria-selected', 'true'); | |||
switcher.querySelectorAll('.gs-content-panel').forEach(function (p) { | |||
p.classList.remove('gs-active'); | |||
}); | |||
if (! | var panel = document.getElementById(panelId); | ||
if (!panel) { | |||
panel = document.createElement('div'); | |||
panel.id = panelId; | |||
panel.className = 'gs-content-panel gs-active'; | |||
loadContent( | switcher.querySelector('.gs-content').appendChild(panel); | ||
loadContent(panel, panelId, pageName); | |||
} else { | } else { | ||
panel.classList.add('gs-active'); | |||
} | } | ||
setHash(panelId); | setHash(panelId); | ||
schedulePrefetchOthers(); | schedulePrefetchOthers(); | ||
} | } | ||
// ============ 头图切换 | // ============ 头图切换 ============ | ||
function switchHero(url) { | function switchHero(url) { | ||
if (! | if (!heroWrap || !heroImg || !url) return; | ||
if (heroImg.src === url) return; | |||
var preload = new Image(); | |||
preload.onload = function () { | |||
heroWrap.classList.add('gs-hero--fade'); | |||
setTimeout(function () { | setTimeout(function () { | ||
heroImg.src = url; | |||
heroWrap.classList.remove('gs-hero--fade'); | |||
}, 160); | }, 160); | ||
} | }; | ||
preload.src = url; | |||
} | } | ||
// ============ 内容加载 ============ | // ============ 内容加载 ============ | ||
function loadContent( | function loadContent(panel, panelId, pageName) { | ||
if (loaded[panelId]) { | if (loaded[panelId]) { panel.innerHTML = loaded[panelId]; return; } | ||
var stored = | try { | ||
var stored = localStorage.getItem(storageKey + panelId); | |||
if (stored) { loaded[panelId] = stored; panel.innerHTML = stored; return; } | |||
} catch (e) {} | |||
} | |||
if (!pageName) return; | if (!pageName) return; | ||
panel.innerHTML = '<div class="gs-loading">加载中...</div>'; | |||
fetchPage(pageName).then(function (html) { | |||
loaded[panelId] = html; | |||
try { localStorage.setItem(storageKey + panelId, html); } catch (e) {} | |||
panel.innerHTML = html; | |||
}).catch(function () { | |||
panel.innerHTML = '<div class="gs-loading">加载失败,<a href="javascript:location.reload()">刷新</a>重试</div>'; | |||
}). | |||
}); | }); | ||
} | } | ||
| 第147行: | 第134行: | ||
function prefetch(panelId, pageName) { | function prefetch(panelId, pageName) { | ||
prefetching[panelId] = true; | prefetching[panelId] = true; | ||
try { | |||
var stored = localStorage.getItem(storageKey + panelId); | |||
if (stored) { loaded[panelId] = stored; prefetching[panelId] = false; return; } | |||
} catch (e) {} | |||
fetchPage(pageName).then(function (html) { | |||
loaded[panelId] = html; | |||
try { localStorage.setItem(storageKey + panelId, html); } catch (e) {} | |||
}).finally(function () { | |||
}). | |||
prefetching[panelId] = false; | prefetching[panelId] = false; | ||
}); | }); | ||
| 第174行: | 第149行: | ||
function schedulePrefetchOthers() { | function schedulePrefetchOthers() { | ||
setTimeout(function () { | setTimeout(function () { | ||
switcher.querySelectorAll('.gs-tab-btn').forEach(function (btn) { | |||
var panelId = | var panelId = btn.dataset.panel; | ||
var pageName = | var pageName = btn.dataset.page; | ||
if (!loaded[panelId] && !prefetching[panelId] && pageName) { | if (!loaded[panelId] && !prefetching[panelId] && pageName) { | ||
prefetch(panelId, pageName); | prefetch(panelId, pageName); | ||
| 第182行: | 第157行: | ||
}); | }); | ||
}, 500); | }, 500); | ||
} | |||
// ============ API 请求 ============ | |||
function fetchPage(pageName) { | |||
var url = mw.util.wikiScript('api') + | |||
'?action=parse&page=' + encodeURIComponent(pageName) + | |||
'&prop=text&format=json&disablelimitreport=1&origin=*'; | |||
return fetch(url) | |||
.then(function (r) { return r.json(); }) | |||
.then(function (data) { | |||
if (data.parse && data.parse.text) return data.parse.text['*']; | |||
throw new Error('empty'); | |||
}); | |||
} | } | ||
| 第190行: | 第178行: | ||
function restoreHash() { | function restoreHash() { | ||
var hash = | var hash = location.hash.replace('#', ''); | ||
if (hash && hash.indexOf('panel-') === 0) { | if (hash && hash.indexOf('panel-') === 0) { | ||
var | var btn = switcher.querySelector('[data-panel="' + hash + '"]'); | ||
if ( | if (btn && !btn.classList.contains('gs-active')) activate(btn); | ||
} | } | ||
} | } | ||
}); | }); | ||
</script> | </script> | ||
2026年6月20日 (六) 10:52的版本
<script type="text/javascript"> document.addEventListener('DOMContentLoaded', function () {
var switcher = document.querySelector('.game-switcher');
if (!switcher) return;
var heroWrap = document.getElementById('gs-hero-wrap');
var heroImg = heroWrap ? heroWrap.querySelector('img') : null;
var loaded = {};
var prefetching = {};
var storageKey = 'gs-cache-';
// ============ 初始化 ============
var firstBtn = switcher.querySelector('.gs-tab-btn');
if (firstBtn) {
firstBtn.classList.add('gs-active');
firstBtn.setAttribute('aria-selected', 'true');
if (heroImg && !heroImg.getAttribute('src')) {
heroImg.src = firstBtn.dataset.hero;
}
}
restoreHash();
// ============ 点击 ============
switcher.addEventListener('click', function (e) {
var btn = e.target.closest('.gs-tab-btn');
if (btn) activate(btn);
});
// ============ 键盘 ============
switcher.addEventListener('keydown', function (e) {
var btn = e.target.closest('.gs-tab-btn');
if (!btn) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
activate(btn);
}
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
e.preventDefault();
var btns = Array.from(switcher.querySelectorAll('.gs-tab-btn'));
var idx = btns.indexOf(btn);
var next = e.key === 'ArrowRight' ? idx + 1 : idx - 1;
if (next >= 0 && next < btns.length) btns[next].focus();
}
});
// ============ 悬停预加载 ============
switcher.addEventListener('mouseenter', function (e) {
var btn = e.target.closest('.gs-tab-btn');
if (!btn) return;
var panelId = btn.dataset.panel;
var pageName = btn.dataset.page;
if (pageName && !loaded[panelId] && !prefetching[panelId]) {
prefetch(panelId, pageName);
}
}, true);
// ============ URL hash ============
window.addEventListener('hashchange', restoreHash);
// ============ 激活面板 ============
function activate(btn) {
if (btn.classList.contains('gs-active')) return;
var panelId = btn.dataset.panel; var heroSrc = btn.dataset.hero; var pageName = btn.dataset.page;
switchHero(heroSrc);
switcher.querySelectorAll('.gs-tab-btn').forEach(function (b) {
b.classList.remove('gs-active');
b.setAttribute('aria-selected', 'false');
});
btn.classList.add('gs-active');
btn.setAttribute('aria-selected', 'true');
switcher.querySelectorAll('.gs-content-panel').forEach(function (p) {
p.classList.remove('gs-active');
});
var panel = document.getElementById(panelId);
if (!panel) {
panel = document.createElement('div');
panel.id = panelId;
panel.className = 'gs-content-panel gs-active';
switcher.querySelector('.gs-content').appendChild(panel);
loadContent(panel, panelId, pageName);
} else {
panel.classList.add('gs-active');
}
setHash(panelId); schedulePrefetchOthers(); }
// ============ 头图切换 ============
function switchHero(url) {
if (!heroWrap || !heroImg || !url) return;
if (heroImg.src === url) return;
var preload = new Image();
preload.onload = function () {
heroWrap.classList.add('gs-hero--fade');
setTimeout(function () {
heroImg.src = url;
heroWrap.classList.remove('gs-hero--fade');
}, 160);
};
preload.src = url;
}
// ============ 内容加载 ============
function loadContent(panel, panelId, pageName) {
if (loaded[panelId]) { panel.innerHTML = loaded[panelId]; return; }
try {
var stored = localStorage.getItem(storageKey + panelId);
if (stored) { loaded[panelId] = stored; panel.innerHTML = stored; return; }
} catch (e) {}
if (!pageName) return;
panel.innerHTML = '
加载中...
';
fetchPage(pageName).then(function (html) {
loaded[panelId] = html;
try { localStorage.setItem(storageKey + panelId, html); } catch (e) {}
panel.innerHTML = html;
}).catch(function () {
panel.innerHTML = '
加载失败,<a href="javascript:location.reload()">刷新</a>重试
';
}); }
// ============ 预加载 ============
function prefetch(panelId, pageName) {
prefetching[panelId] = true;
try {
var stored = localStorage.getItem(storageKey + panelId);
if (stored) { loaded[panelId] = stored; prefetching[panelId] = false; return; }
} catch (e) {}
fetchPage(pageName).then(function (html) {
loaded[panelId] = html;
try { localStorage.setItem(storageKey + panelId, html); } catch (e) {}
}).finally(function () {
prefetching[panelId] = false;
});
}
function schedulePrefetchOthers() {
setTimeout(function () {
switcher.querySelectorAll('.gs-tab-btn').forEach(function (btn) {
var panelId = btn.dataset.panel;
var pageName = btn.dataset.page;
if (!loaded[panelId] && !prefetching[panelId] && pageName) {
prefetch(panelId, pageName);
}
});
}, 500);
}
// ============ API 请求 ============
function fetchPage(pageName) {
var url = mw.util.wikiScript('api') +
'?action=parse&page=' + encodeURIComponent(pageName) +
'&prop=text&format=json&disablelimitreport=1&origin=*';
return fetch(url)
.then(function (r) { return r.json(); })
.then(function (data) {
if (data.parse && data.parse.text) return data.parse.text['*'];
throw new Error('empty');
});
}
// ============ URL hash ============
function setHash(panelId) {
try { history.replaceState(null, , '#' + panelId); } catch (e) {}
}
function restoreHash() {
var hash = location.hash.replace('#', );
if (hash && hash.indexOf('panel-') === 0) {
var btn = switcher.querySelector('[data-panel="' + hash + '"]');
if (btn && !btn.classList.contains('gs-active')) activate(btn);
}
}
}); </script>