Skip to content

Commit 7bcd55e

Browse files
authored
TCR-772 : fix(sidebar): handle bottom-of-page and short document highlighting
* Add isAtBottom() function to detect when user scrolls near page bottom * When at bottom, select last heading above viewport center * Fixes issue where bottom sections never get highlighted * Ensures accurate highlighting for short documents
2 parents 4690c25 + e0f18f0 commit 7bcd55e

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

docs/.vuepress/theme/sidebar/Sidebar.vue

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,25 @@ const throttle = (func, delay) => {
9595
}
9696
}
9797
98+
// Check if user has scrolled to the bottom of the page
99+
const isAtBottom = () => {
100+
const scrollHeight = document.documentElement.scrollHeight
101+
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
102+
const clientHeight = document.documentElement.clientHeight
103+
// Consider "at bottom" if within 10% of viewport height from the bottom
104+
// This scales better across different screen sizes
105+
const threshold = clientHeight * 0.1
106+
return scrollHeight - scrollTop - clientHeight < threshold
107+
}
108+
98109
// Update sidebar active state based on the topmost visible heading
99110
const updateSidebarActiveState = () => {
100111
const sidebar = document.querySelector('.sidebar')
101112
if (!sidebar) return
102113
103114
const sidebarAnchors = sidebar.querySelectorAll('a')
104115
const sidebarAnchorsContainer = sidebar.querySelectorAll('.collapsible.sidebar-sub-header')
116+
const pageAnchors = document.querySelectorAll('.header-anchor')
105117
106118
// Get all visible headings sorted by their position (topmost first)
107119
const sortedVisibleHeadings = Array.from(visibleHeadings.value).sort((a, b) => {
@@ -110,12 +122,33 @@ const updateSidebarActiveState = () => {
110122
return aRect.top - bRect.top
111123
})
112124
113-
// Select the topmost visible heading
114-
const topmostHeading = sortedVisibleHeadings[0]
125+
let targetHeading = null
126+
127+
// If at bottom of page, select the last heading that's above the viewport center
128+
if (isAtBottom() && pageAnchors.length > 0) {
129+
const viewportCenter = window.innerHeight / 2
130+
const allAnchorsArray = Array.from(pageAnchors)
131+
132+
// Find all headings that are above the viewport center
133+
const headingsAboveCenter = allAnchorsArray.filter(anchor => {
134+
const rect = anchor.getBoundingClientRect()
135+
return rect.top < viewportCenter
136+
})
137+
138+
// Select the last one (closest to bottom)
139+
if (headingsAboveCenter.length > 0) {
140+
targetHeading = headingsAboveCenter[headingsAboveCenter.length - 1]
141+
}
142+
}
143+
144+
// If not at bottom or no heading found, use the topmost visible heading
145+
if (!targetHeading && sortedVisibleHeadings.length > 0) {
146+
targetHeading = sortedVisibleHeadings[0]
147+
}
115148
116-
if (!topmostHeading) return
149+
if (!targetHeading) return
117150
118-
const currentAnchor = topmostHeading.getAttribute('data-anchor')
151+
const currentAnchor = targetHeading.getAttribute('data-anchor')
119152
120153
// Update active class on sidebar links
121154
sidebarAnchors.forEach(a => a.classList.remove('active'))

0 commit comments

Comments
 (0)