Files
wordpress-theme-aksrt/assets/js/section-navs.js
T
kate522 1f4a39d8f8 feat(runtime): 全站前端脚本统一接入 AksrtFrontend 全局 DOM 生命周期
- rehydrate.js 新增 boot()/aksrt:ready 事件,支持 boot 后晚注册立即以 init 触发
- aksrt-runtime.js 初始加载改走 AksrtFrontend.boot,tooltip/倒计时注册为组件
- 约 30 个前端脚本(首页、评论、支付、路由、页面叶子、widgets)
  从直接 DOMContentLoaded 迁移为 AksrtFrontend.register,并加幂等标记
- section-navs 依赖改为 aksrt-runtime,保证注册时序
- 清理死代码:删除孤儿 loader.js/main.js 及 runtime/page-navs 遗留副本
- loader.js 移除已迁移模块的 tbquire 路径映射,main.js 相应瘦身
- build-public-assets.mjs 增加 runtime/main.min.js 自动压缩步骤
- 新增 4 个契约测试(homepage-dom/rehydrate-runtime/global-dom-init/
  public-bundle-parity),3 个既有 harness 适配注册表初始化方式
2026-09-02 21:48:56 +08:00

153 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AKSRT Section Navs — legacy section_navs parity (posts-nav-lists + scrollspy).
*
* Builds TOC from [data-nav] h1–h4 into .posts-nav-box / .posts-nav-lists.
*
* @package AKSRT
*/
(function () {
'use strict';
var HEADING_SEL = 'h1, h2, h3, h4';
var MIN_HEADINGS = 2;
var NAVBOX = '.posts-nav-box';
var NAV_CLASS = 'posts-nav-lists';
function isExcluded(el) {
return el.classList.contains('item-heading') || !!el.closest('.no-nav');
}
function ensureId(el, index) {
if (!el.id) {
el.id = 'section-nav-' + index;
}
return el.id;
}
function buildLists(root) {
var headings = root.querySelectorAll(HEADING_SEL);
var html = '';
var count = 0;
headings.forEach(function (h, i) {
if (isExcluded(h)) return;
var id = ensureId(h, i);
var tag = h.tagName.toUpperCase();
var text = (h.textContent || '').trim();
if (!text) return;
html += '<li class="n-' + tag + '"><a href="#' + id + '">' + text + '</a></li>';
count++;
});
return count >= MIN_HEADINGS ? html : '';
}
function fillBoxes(listsHtml, scope) {
var boxes = Array.prototype.slice.call(scope.querySelectorAll(NAVBOX));
if (scope.nodeType === 1 && scope.matches(NAVBOX)) boxes.unshift(scope);
boxes.forEach(function (box) {
var existing = box.querySelector('.' + NAV_CLASS);
if (existing) {
existing.innerHTML = listsHtml;
return;
}
var title = box.getAttribute('data-title') || '';
var titleHtml = title
? '<div class="box-body notop"><div class="title-theme">' + title + '</div></div>'
: '';
box.insertAdjacentHTML(
'beforeend',
titleHtml +
'<div class="aksrt-panel"><div class="' +
NAV_CLASS +
' scroll-y mini-scrollbar list-unstyled">' +
listsHtml +
'</div></div>'
);
});
}
function smoothTo(hash) {
var el = document.querySelector(hash);
if (!el) return;
var offset = 0;
var header = document.querySelector('.header');
if (document.body.classList.contains('nav-fixed') && header) {
offset = header.offsetHeight + 12;
}
var top = el.getBoundingClientRect().top + window.pageYOffset - offset;
window.scrollTo({ top: top, behavior: 'smooth' });
}
var clicksBound = false;
var scrollspyBound = false;
var autoHashHandled = false;
function bindClicks() {
if (clicksBound) return;
clicksBound = true;
document.addEventListener('click', function (e) {
var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null;
if (!a) return;
var href = a.getAttribute('href');
if (!href || href.length < 2) return;
e.preventDefault();
history.replaceState(null, '', href);
smoothTo(href);
});
}
function scrollspy(scope) {
if (scrollspyBound) return;
var links = scope.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]');
if (!links.length) return;
scrollspyBound = true;
var map = [];
links.forEach(function (a) {
var id = a.getAttribute('href').slice(1);
var el = document.getElementById(id);
if (el) map.push({ el: el, a: a, li: a.parentElement });
});
function update() {
var y = window.scrollY + 110;
var current = null;
for (var i = 0; i < map.length; i++) {
if (map[i].el.offsetTop <= y) current = map[i];
}
map.forEach(function (m) {
if (m.li) m.li.classList.toggle('active', m === current);
});
}
window.addEventListener('scroll', update, { passive: true });
update();
}
function autoHash() {
if (autoHashHandled || !window.location.hash) return;
autoHashHandled = true;
setTimeout(function () {
smoothTo(window.location.hash);
}, 40);
}
function init(scope) {
scope = scope || document;
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) {
return;
}
var root = scope.nodeType === 1 && scope.matches('[data-nav], .article-content, .entry-content, main.article')
? scope
: scope.querySelector('[data-nav], .article-content, .entry-content, main.article');
if (!root) return;
var listsHtml = buildLists(root);
if (!listsHtml) return;
if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) {
return;
}
fillBoxes(listsHtml, document);
bindClicks();
scrollspy(document);
autoHash();
}
window.AksrtFrontend.register('aksrt-section-navs', init);
})();