/** * 导航栏锚点高亮 * * 问题:VitePress 的 isActive() 只比较 path,不看 hash。 * nav 里「首页 /」「产品功能 /#features」「核心能力 /#core」路径部分都是 `/`, * 于是停在 #features 时,首页和产品功能会同时被标蓝。 * * 解决:接管首页内锚点项的激活判定,用 data-anchor-active 标记, * 样式侧压掉 VitePress 默认的 .active。 * - 在首页且有 hash:只有 hash 匹配的那项亮,首页项不亮 * - 在首页且无 hash:只有「首页」亮 * - 不在首页:不干预,清掉标记交回默认逻辑 */ const SEL = '.VPNavBarMenuLink' function hashOf(a: HTMLAnchorElement): string { const href = a.getAttribute('href') ?? '' const i = href.indexOf('#') return i === -1 ? '' : href.slice(i) } function apply(): void { const links = Array.from(document.querySelectorAll(SEL)) if (!links.length) return const path = location.pathname const onHome = path === '/' || path === '' || /^\/index\.html?$/.test(path) // 标记首页状态:样式侧据此压掉 VitePress 对「项目 / 关于」的前缀匹配高亮 document.documentElement.classList.toggle('portal-nav-home', onHome) if (!onHome) { links.forEach((a) => a.removeAttribute('data-anchor-active')) return } // 兼容 VitePress 的 cleanUrls:路径可能是 / 或 /index.html const h = location.hash links.forEach((a) => { const href = a.getAttribute('href') ?? '' const itemHash = hashOf(a) if (itemHash) { // 锚点项:只有 hash 完全匹配时才亮 a.toggleAttribute('data-anchor-active', h === itemHash) return } // 没有 hash 的项:只有「指向首页本身」的才算首页项 // (/projects/ 与 /about 也没有 hash,不能一并当作首页) const bare = href.split('#')[0].replace(/\/index\.html?$/, '/') const isHomeItem = bare === '/' || bare === '' if (isHomeItem) { a.toggleAttribute('data-anchor-active', h === '') } else { a.removeAttribute('data-anchor-active') } }) } let raf = 0 function schedule(): void { if (raf) return raf = requestAnimationFrame(() => { raf = 0 apply() }) } /** 首屏导航是异步渲染的,用 MutationObserver 等它出现再判定 */ export function setupNavAnchorHighlight(): void { if (typeof window === 'undefined') return const run = () => { apply() // 导航可能是分块渲染的,多补两次 requestAnimationFrame(apply) setTimeout(apply, 80) setTimeout(apply, 300) } run() window.addEventListener('hashchange', schedule) window.addEventListener('popstate', schedule) window.addEventListener('load', schedule) // 导航栏内容变化(SPA 切换、暗色切换重建)时重新判定 const nav = document.querySelector('.VPNavBar') ?? document.body const mo = new MutationObserver(() => schedule()) mo.observe(nav, { childList: true, subtree: true }) } export { apply as applyNavAnchorHighlight }