使用方法
let a = new ClickScroll()
a.navBtns = [dom1, dom2, dom3] // 導覽列dom元素,陣列
a.targetEls = [dom4, dom5, dom6] // 目標dom元素,陣列
a.init() // 註冊點擊事件
a.exH = -100 // 額外距離,不一定要有
a.initScroll() // 註冊滾動事件,不一定要有
a.removeScroll() // 移除滾動事件,不一定要有
使用前,需先引入
class ClickScroll {
exH = 0
targetElsDistance = []
constructor (navBtns, targetEls) {
this.navBtns = navBtns
this.targetEls = targetEls
}
// 為每個navBtn註冊click事件
init () {
this.navBtns.forEach( (btn, index) => {
btn.addEventListener('click', (e) => {
e.preventDefault()
this.computeDistance()
this.handleScroll(index)
})
})
}
// 註冊全域滾動事件
initScroll () {
window.addEventListener('scroll', this.detectScroll)
}
// 移除滾動事件
removeScroll () {
window.removeEventListener('scroll', this.detectScroll)
}
// 計算目標DOM元素們,距離元件最頂端距離
// 最後,距離數值填入陣列
computeDistance () {
this.targetElsDistance.length = 0
this.targetEls.forEach( el => {
let distance = 0
while (el && el.nodeName !== 'BODY') {
distance += Number(el.offsetTop)
el = el.offsetParent
}
this.targetElsDistance.push(distance)
})
}
// 執行滾動,距離是targetElsDistance + exH
handleScroll (index) {
window.scrollTo({
top: this.targetElsDistance[index] + this.exH,
behavior: 'smooth',
})
}
// 只有自己新增'active'的className,其餘DOM取消'active'
addRemoveActive (target) {
if (target.classList.contains('active')) return
this.navBtns.forEach( btn => {
btn.classList.remove('active')
})
target.classList.add('active')
}
detectScroll = () => {
this.computeDistance()
let arr = this.targetElsDistance
let exH = this.exH - 5
if (scrollY < arr[0] + exH) {
this.navBtns.forEach( btn => {
btn.classList.remove('active')
})
return
}
if (scrollY > arr[arr.length - 1] + exH) {
this.addRemoveActive(this.navBtns[arr.length - 1])
return
}
for (let i = 0; i < arr.length - 1; i++) {
if (scrollY > arr[i] + exH && scrollY < arr[i+1] + exH) {
this.addRemoveActive(this.navBtns[i])
}
}
}
}