回到顶部的几种玩法

回到顶部的几种玩法

前不久优化 FE 的时候用 withRouter 封装了一个 ScrollRestoration 组件,里面用到了 window.scrollTo(0, 0)。而 FE 回到顶部的小猫用的是 requestAnimationFrame,然后去网上搜索一下发现还有其他一些冷门的方法,遂结合 caniuse 和例子总结一番。

scrollTop

在 Chrome 中, 通过 document.documentElement.scrollTop 来获取滚动条距离顶端的位置;而在 FF 和 Safari 中,则是通过 document.body.scrollTop .

在 Chrome 中,document.body.scrollTop 恒为 0, 在 FF 和 Safari 中,document.documentElement.scrollTop恒为 0.

因此做兼容的话,可以通过两者相加来获取滚动条距离顶端的位置。

这两个属性可以被设置,所以业界常用的回到顶部的做法如下。

document.body.scrollTop = document.documentElement.scrollTop = 0

最后扩充一个属性 window.pageYOffset, 这个属性同样是获取滚动条距离顶端的位置,在 Chrome 和 FF 都可以使用,⚠️ 但是这个属性不能够设置位置,并且在 Safari 上不能用

scrollTo()

用过 React 的同学应该对这个方法不陌生。因为用到了 hashHistory,它是建立在 history 之上的,当路由发生变化时会记住原路由的状态,跳转新页面后默认停留在原页面的位置。 所以我们经常用这个简单方法让页面回到顶部(当然关于 withRouter 是另外一个话题了,详情戳 scroll-restoration)

window.scrollTo() 接收两种参数形式。

第一种是 window.scrollTo(x-coord, y-coord ), 其中 x-coord 是文档中的横轴坐标, y-coord 是文档中的纵轴坐标,所以一般通过 window.scrollTo(0, 0) 的方式回到顶部。

window.scrollTo(options)

第二种是 window.scrollTo(options), 接受一个参数对象,其中 top 相当于 y-coord,left 相当于 x-coord,而 behavior 里有个属性值是 ‘smooth’ ,可以平滑的滚动到顶部,很赞。

window.scrollTo({
    left: 0,
    top: 0,
    behavior: 'smooth' / 'instant' / 'auto'
})

在挖一下,还有一个方法是 window.scroll(), 它和 window.scrollTo() 的用法一模一样。

scrollBy()

这个方法和 scrollTo() 的传参形式一样,都是传递 x-coord, y-coord 或者 option,不同的是, scrollTo() 传递的参数是一个对于文档的一个绝对的量,而 scrollBy() 传递的是一个相对于自身当前位置的一个量。

因此,可以用下面的方式回到顶部。

const top = document.body.scrollTop || document.documentElement.scrollTop
scrollBy(0,-top);

当然,FF 还有两个方法叫做 window.scrollByLines(lines)window.scrollByPages(), 两种方法在 Chrome 和 Safari 无效,当作了解即可。

其他

当然还看到别人在说在文档头部放一个隐藏的锚点,或者用 element.scrollIntoView() 之类的方式,这些就有些反人类了,这里不打算介绍。

番外篇:平滑滚动

这里主要谈一谈 requestAnimationFrame,我在 FE 回到顶部的小猫用到了这个 API,一开始是这样写的,但这样有个问题是写死了每次递归只往上走 160px,但如果页面很长的话,这个速度就会很慢。

   public scrollToTop1 = () => {
    let time: number = 0;
    document.documentElement.scrollTop -= 160;
    if (document.documentElement.scrollTop <= 0) {
      window.cancelAnimationFrame(time);
    } else {
      time = window.requestAnimationFrame(this.scrollToTop);
    }
  };

改进版:

  public scrollToTop = () => {
    let timer: number = 0;
    cancelAnimationFrame(timer);
    const startTime = +new Date();
    const b = document.body.scrollTop || document.documentElement.scrollTop;
    const d = 500;
    const c = b;
    timer = requestAnimationFrame(function func() {
      const t = d - Math.max(0, startTime - +new Date() + d);
      document.documentElement.scrollTop = document.body.scrollTop =
        (t * -c) / d + b;
      timer = requestAnimationFrame(func);
      if (t === d) {
        cancelAnimationFrame(timer);
      }
    });
  };
防抖和节流

PREVIOUS POST

防抖和节流

[正则表达式系列] 字符匹配

NEXT POST

[正则表达式系列] 字符匹配