vue-router URL里存在锚点导致不能再次跳转的可能解决办法

有时候我们会碰到这么一个场景:我们可能希望点击页面上的某个按钮,然后就会滚动到页面的某一块上,也就是所谓的“滚动到锚点”的功能。这个其实很好实现,vue-router的官方文档里已经给出了写法:

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    if (to.hash) {
      return {
        selector: to.hash
      }
    }
  }
});

但是这么写存在一个问题。比如我在页面顶部有一个链接,点击之后就会跳转到底部(demo在这里):

<router-link to="#bottom">jump to bottom</router-link>
<!--...-->
<h1 id="bottom">This is bottom!</h1>

这应该是很常见的实现方式了。

但是这么实现有个问题,如果我先点击了这个链接跳转到了底部(此时URL为/#bottom),然后我再手动从底部把页面滚动到顶部,此时再次点击链接,就不会跳转了。为什么?因为URL已经是/#bottom了,当然不会再次跳转。

所以,我们就会有这么一个想法:能不能让目标组件离开可视范围之后就自动修改URL呢?这样我们就能反复跳转了。比如,当这个bottom不可见的时候,我们就自动把URL修改成/,这样我们再次点击链接的时候,就能再次跳转。但是,摆在我们面前的是这样几个问题:

  • 如何修改URL而不造成页面刷新、滚动?
  • 如何尽可能让URL美观?
  • 如何判断组件是否可见?

让URL改变而不刷新页面的API早就有了,就是H5的history API,而且兼容性也已经很好了。因为vue-router的history模式是基于history API的,所以,我们最直接的想法就是这么写:

this.$router.replace('/')

但是这样会报重复路由Avoided redundant navigation to current location: "/"的错,而且可能会导致页面回到顶部,并不是我们想要的方案。

那么,直接用history API呢?比如,我们也许可以这么写:

history.replaceState(
  null,
  document.title,
  location.pathname + location.search
);

但是这样并不会起到作用,因为vue-router内部的hash值并没有变化,我们只是修改了URL而已,仍然无法完成跳转。

我们可能还会想到直接修改URL的hash部分:

location.hash = '';

但是这样第一个是不美观(会让URL变成/#),第二个是在相当多的浏览器上会直接跳转到页面顶部,因为/#代表的就是页面的顶部。

我们可能还会想到直接修改location.href,但这就会触发页面刷新,显然是不行的。

所以,综合一下,其实我们可以采用这样的办法:

location.hash = '_';
history.replaceState(
  null,
  document.title,
  location.pathname + location.search
);

不用$router的理由已经说过了,不再赘述。这一段是什么意思呢,第一个是先清除现有的锚点,设置到另一个不存在的锚点上,这样就不会触发页面滚动,同时也能确保正常跳转;然后再使用history API去修改URL,达到美观的目的。

完成了核心逻辑之后,下面就是如何判断组件的可见性。传统的方法想必大家都会,监听scroll事件,然后判断目标组件和顶部的距离;不过在现代浏览器里,有个新的API IntersectionObserver,就是为了解决可见性的问题。具体的写法这篇文章里有提到,我就不再多说了。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值