做網站的時候,「回到頂端」應該也算一個蠻常見的功能,通常實作上大概就是用 window.scrollTo() 之類的來完成吧。
然後當我想給手邊專案加個這樣的功能時,突然發現,天啊,完全沒反應呢!!
google 了一下發現有可能是 css 設定導致的問題,有個說法是說 window.scrollTo() 似乎是對 overflow 屬性的反饋還不是那麼好甚麼的。
然而我調整了我 css 的設定之後 window.scrollTo() 還是毫無反應。
接著我就惱羞成怒,直接放棄 window.scrollTo(),用了很粗暴的方式處理:修改 scroll 事件的 target.scrollingElement.scrollTop。
以下用 vue 實作。
首先是用 addEventListner 對滾輪事件進行監聽。
mounted() {
window.addEventListener('scroll', this.scrollEvent)
}
data 的地方我有用一個 scrollEventObj 記錄滾輪事件的 element。
hideTopFlag 是用於判斷是否顯示回到頂端的按鈕。當使用者在頂端的時候我們不顯示 (hideTopFlag: true)。
data() {
return {
scrollEventObj: {},
hideTopFlag: true
}
}
接著設定點選了要回到頂端的按鈕。
scrollToTop() {
window.setInterval(() => {
this.scrollEventObj.target.scrollingElement.scrollTop -= 20
}, 5)
},
這邊是用 setInterVal 搭配 scrollTop 連續減法,慢慢扣到 0,滑到頂端。
事實上這篇文章的重點就只有 scrollTop 這一行。
然後監聽滾輪事件的部分:
scrollEvent(event) {
this.scrollEventObj = event
if (event.target.scrollingElement.scrollTop < 1) {
this.hideTopFlag = true
for (let i = 1; i < 9999; i++) {
window.clearInterval(i);
}
} else {
this.hideTopFlag = false
}
},
參數 event 是當函數沒有其他參數時設定會自動傳入的。
如果有同時使用 event 跟其他自訂參數的需求,以 vue 來說可以使用 $event 來獲得。
簡單說一下邏輯,this.scrollEventObj 把滾輪事件存起來,接著判斷目前滾輪高度是否小於 1,如果是的話就隱藏回到頂端按鈕 (this.hideTopFlag = true) 並 clearInterval,反之則顯示回到頂端按鈕。
clearInterval 的部分要注意是迴圈結束條件的數字不能設訂的太小。
本來我只設定了 99,然後在手機裝置上就發生了清不掉 interval 的狀況 (直觀上看上去就是無限迴圈),但原因我不清楚...
純粹一個慘痛的踩坑分享。
主要因為我想要做出類似 window.scrollTo 的 smooth 行為,如果想要直接閃回頂端,拿掉 window.setInterval 直接把 scrollTop 設定成 0 即可。
大概就這樣了,之後如果有找到正式一點的解決辦法再更新上來。
目前至少是看起來可以滾動了XDDD
然後實作過程中還有遇到一個也是卡住蠻久的問題,就是:
當我滑鼠滾輪停在這顆回到頂端的 div 上面時,無法滾動畫面、不會觸發滾動事件。
後來大概發現原因在於我把要滾動的區塊與回到頂端的 div 都設置了
position: fixed。
兩個解決方法:
1. 把要滾動畫面的區塊設定成 fixed 以外的屬性
2. 把回到頂端按鈕加上比較低的 z-index。
以回到頂端這個功能來說,按鈕本身會被其他東西蓋住這個事情並不科學,所以最後我是採用第一個方法。
為了改這個 bug,幾乎把整個 html 又翻新了一遍,一開始架構沒想好寫到最後發現問題的代價真的都好慘痛(X
文章這邊是大致寫實作邏輯,樣式甚麼的都拿掉了,Demo 跟 程式碼 可以參考連結,本篇程式邏輯主要在 src/views/portfolioField.vue 裡。
參考文章
JavaScript scrollTo method does nothing?
How can I clearInterval() for all setInterval()?