-
Notifications
You must be signed in to change notification settings - Fork 402
Expand file tree
/
Copy pathindex.js
More file actions
83 lines (68 loc) · 2.19 KB
/
index.js
File metadata and controls
83 lines (68 loc) · 2.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const COMPONENT_SELECTOR = '[data-anchors-indicator]'
const ACTIVE_CLASS = 'data-active-class'
const SECTION_ATTR = 'data-sections'
const throttle = (fn, wait) => {
let lastTime = 0
return function () {
const now = Date.now()
if (now - lastTime >= wait) {
fn()
lastTime = now
}
}
}
export default () => {
const components = document.querySelectorAll(COMPONENT_SELECTOR)
components.forEach(component => {
const sectionSelector = component.getAttribute(SECTION_ATTR)
const activeClass = component.getAttribute(ACTIVE_CLASS)
const sections = Array.from(document.querySelectorAll(sectionSelector))
const links = Array.from(component.querySelectorAll('a'))
// 🔥 Map sectionId -> link (O(1) lookup)
const linkMap = {}
links.forEach(link => {
const hash = link.hash.replace('#', '')
linkMap[hash] = link
})
// 🔥 Precompute section offsets once
const sectionData = sections.map(sec => {
let id = sec.tagName === 'H2'
? sec.id
: sec.querySelector('h2')?.id
return {
id,
offset: sec.offsetTop
}
}).filter(s => s.id)
let currentActive = null
function scrollspy() {
const scrollPosition =
document.documentElement.scrollTop || document.body.scrollTop
const position = scrollPosition + (
window.innerWidth >= 768
? window.innerHeight * 0.2
: window.innerHeight * 0.6
)
// 🔥 Traverse from bottom → first match wins (faster)
let newActive = null
for (let i = sectionData.length - 1; i >= 0; i--) {
if (sectionData[i].offset <= position) {
newActive = sectionData[i].id
break
}
}
// 🔥 Update DOM only if changed
if (newActive !== currentActive) {
if (currentActive && linkMap[currentActive]) {
linkMap[currentActive].classList.remove(activeClass)
}
if (newActive && linkMap[newActive]) {
linkMap[newActive].classList.add(activeClass)
}
currentActive = newActive
}
}
window.addEventListener('load', scrollspy)
window.addEventListener('scroll', throttle(scrollspy, 50))
})
}