window.scrollTo() 無法動作的替代方式

做網站的時候,「回到頂端」應該也算一個蠻常見的功能,通常實作上大概就是用 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
    }
  }

 

接著設定點選了要回到頂端的按鈕。

<div @click="scrollToTop"></div>
    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()?