fix: 修复侧边栏点击后不自动滚动定位的问题
- 在 onMounted 中调用 initOutlineAutoScroll 初始化函数 - 添加 VPSidebar 选择器支持,修正 VitePress 侧边栏类名 - 修正激活状态类名为 is-active(VitePress 使用) - 新增 scrollSidebarToActiveItem 函数实现侧边栏自动滚动 - 点击侧边栏链接后自动将激活项平滑滚动到可视区域中心
This commit is contained in:
@@ -86,7 +86,6 @@ onMounted(() => {
|
|||||||
applyLineHeight(savedLineHeight)
|
applyLineHeight(savedLineHeight)
|
||||||
isHydrated.value = true
|
isHydrated.value = true
|
||||||
|
|
||||||
// 初始化 outline 自动滚动功能
|
|
||||||
initOutlineAutoScroll()
|
initOutlineAutoScroll()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -95,7 +94,34 @@ onMounted(() => {
|
|||||||
// 当页面滚动时,自动滚动 outline 让当前激活项保持在可视区域
|
// 当页面滚动时,自动滚动 outline 让当前激活项保持在可视区域
|
||||||
// ============================================
|
// ============================================
|
||||||
function initOutlineAutoScroll() {
|
function initOutlineAutoScroll() {
|
||||||
// 使用 MutationObserver 监听 outline 的变化
|
const outlineSelectors = [
|
||||||
|
'.VPDocAsideOutline',
|
||||||
|
'.VPTableOfContents',
|
||||||
|
'.vitepress-doc-sidebar',
|
||||||
|
'.sidebar-outline',
|
||||||
|
'aside'
|
||||||
|
]
|
||||||
|
|
||||||
|
const sidebarSelectors = [
|
||||||
|
'.VPSidebar',
|
||||||
|
'.VPDocSidebar',
|
||||||
|
'.vitepress-doc-sidebar'
|
||||||
|
]
|
||||||
|
|
||||||
|
let outlineContainer = null
|
||||||
|
for (const selector of outlineSelectors) {
|
||||||
|
outlineContainer = document.querySelector(selector)
|
||||||
|
if (outlineContainer) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outlineContainer) return
|
||||||
|
|
||||||
|
let sidebarContainer = null
|
||||||
|
for (const selector of sidebarSelectors) {
|
||||||
|
sidebarContainer = document.querySelector(selector)
|
||||||
|
if (sidebarContainer) break
|
||||||
|
}
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
const observer = new MutationObserver((mutations) => {
|
||||||
for (const mutation of mutations) {
|
for (const mutation of mutations) {
|
||||||
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
||||||
@@ -107,7 +133,17 @@ function initOutlineAutoScroll() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 开始监听
|
const sidebarObserver = new MutationObserver((mutations) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
||||||
|
const target = mutation.target
|
||||||
|
if (target.classList.contains('is-active')) {
|
||||||
|
scrollSidebarToActiveItem(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const startObserving = () => {
|
const startObserving = () => {
|
||||||
const outlineContainer = document.querySelector('.VPDocAsideOutline')
|
const outlineContainer = document.querySelector('.VPDocAsideOutline')
|
||||||
if (outlineContainer) {
|
if (outlineContainer) {
|
||||||
@@ -116,32 +152,48 @@ function initOutlineAutoScroll() {
|
|||||||
subtree: true,
|
subtree: true,
|
||||||
attributeFilter: ['class']
|
attributeFilter: ['class']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const existingActive = outlineContainer.querySelector('.active')
|
||||||
|
if (existingActive) {
|
||||||
|
scrollOutlineToActiveItem(existingActive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sidebarContainer) {
|
||||||
|
sidebarObserver.observe(sidebarContainer, {
|
||||||
|
attributes: true,
|
||||||
|
subtree: true,
|
||||||
|
attributeFilter: ['class']
|
||||||
|
})
|
||||||
|
|
||||||
|
const existingSidebarActive = sidebarContainer.querySelector('.is-active')
|
||||||
|
if (existingSidebarActive) {
|
||||||
|
scrollSidebarToActiveItem(existingSidebarActive)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载完成后开始监听
|
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', startObserving)
|
document.addEventListener('DOMContentLoaded', startObserving)
|
||||||
} else {
|
} else {
|
||||||
startObserving()
|
startObserving()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同时监听路由变化(VitePress 是 SPA)
|
|
||||||
const originalPushState = history.pushState
|
const originalPushState = history.pushState
|
||||||
const originalReplaceState = history.replaceState
|
const originalReplaceState = history.replaceState
|
||||||
|
|
||||||
history.pushState = function (...args) {
|
history.pushState = function (...args) {
|
||||||
originalPushState.apply(this, args)
|
originalPushState.apply(this, args)
|
||||||
setTimeout(startObserving, 100)
|
setTimeout(startObserving, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
history.replaceState = function (...args) {
|
history.replaceState = function (...args) {
|
||||||
originalReplaceState.apply(this, args)
|
originalReplaceState.apply(this, args)
|
||||||
setTimeout(startObserving, 100)
|
setTimeout(startObserving, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('popstate', () => {
|
window.addEventListener('popstate', () => {
|
||||||
setTimeout(startObserving, 100)
|
setTimeout(startObserving, 300)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +224,32 @@ function scrollOutlineToActiveItem(activeLink) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 滚动侧边栏让当前激活项保持在可视区域中心
|
||||||
|
function scrollSidebarToActiveItem(activeItem) {
|
||||||
|
const sidebarContainer = document.querySelector('.VPSidebar') || document.querySelector('.VPDocSidebar')
|
||||||
|
if (!sidebarContainer || !activeItem) return
|
||||||
|
|
||||||
|
const targetElement = activeItem.querySelector('.item') || activeItem.querySelector('a') || activeItem
|
||||||
|
|
||||||
|
const containerRect = sidebarContainer.getBoundingClientRect()
|
||||||
|
const targetRect = targetElement.getBoundingClientRect()
|
||||||
|
|
||||||
|
const targetTop = targetRect.top - containerRect.top + sidebarContainer.scrollTop
|
||||||
|
const targetHeight = targetRect.height
|
||||||
|
const targetCenterY = targetTop + targetHeight / 2
|
||||||
|
|
||||||
|
const isInside = targetRect.top >= containerRect.top - 20 &&
|
||||||
|
targetRect.bottom <= containerRect.bottom + 20
|
||||||
|
|
||||||
|
if (!isInside) {
|
||||||
|
const targetScrollTop = targetCenterY - containerRect.height / 2
|
||||||
|
sidebarContainer.scrollTo({
|
||||||
|
top: targetScrollTop,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(fontSize, (next) => {
|
watch(fontSize, (next) => {
|
||||||
if (!isHydrated.value) return
|
if (!isHydrated.value) return
|
||||||
const normalized = clampFontSize(next)
|
const normalized = clampFontSize(next)
|
||||||
|
|||||||
Reference in New Issue
Block a user