Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle anchors (bookmarks) with Vue Router?

Tags:

I'm looking for a smart way to handle in-page anchors with Vue Router. Consider the following:

<router-link to="#app">Apply Now</router-link> <!-- some HTML markup in between... --> <div id="app">...</div> 

The "scroll to anchor" behavior described in the docs works fine except:

  • When you click on the anchor, it brings you down to the div id="app". Now scroll away from the div back to the anchor and try clicking it again -- this time you will not jump down to the div. In fact, the anchor will retain the class router-link-active and the URL will still contain the hash /#app;
  • With the steps above, if you refresh the page (the URL will still contain the hash) and click on the anchor, nothing will happen either.

This is very unfortunate from the UX perspective because a potential customer has to manually scroll all the way down again to reach the application section.

I was wondering if Vue Router covers this situation. For reference, here's my router:

export default new VueRouter({     routes,     mode: 'history',     scrollBehavior(to, from, savedPosition) {         if (to.hash) {             return { selector: to.hash }         } else if (savedPosition) {             return savedPosition;         } else {             return { x: 0, y: 0 }         }     } }) 
like image 558
Alex Avatar asked Jul 19 '17 21:07

Alex


People also ask

How do you pass props through the Vue route?

Vue Router 4 provides multiple ways of doing this: from setting the props property on the route record to true and automatically passing all params as props, setting the props property to a static object to provide static props, or setting it to a function that returns the desired props.

Can I use Vue router with CDN?

You can't use . vue files using the CDN version of Vue and Vue Router because the . vue filetype is part of the vue-loader project for Webpack. In other words, you need to transition over to using Webpack if you wanna use .

How do I remove the hashtag from my Vue router?

We map the URL paths to components. Then we call createRouter with an object that has the history property set to value returned by createWebHistory . createWebHistory set the router to HTML5 history mode to remove the hash. This will remove the hash from the paths that are mapped by Vue Router 4.


2 Answers

I haven't found anything in the resources to solve your issue but you could utitlize the $route.hash in your mounted hook of the component that holds your <router-view></router-view> to solve the refresh issue.

<script> export default {   name: 'app',   mounted: function()   {     // From testing, without a brief timeout, it won't work.     setTimeout(() => this.scrollFix(this.$route.hash), 1);   },   methods: {     scrollFix: function(hashbang)     {       location.hash = hashbang;     }   } } </script> 

Then to solve the issue of second clicks you could use the native modifier and bind to your <router-link></router-link>. It's a fairly manual process but will work.

<router-link to="#scroll" @click.native="scrollFix('#scroll')">Scroll</router-link> 

There may also be something you could do with the router's afterEach method but haven't figured that out yet.

like image 132
Steven B. Avatar answered Oct 12 '22 22:10

Steven B.


Possible solution which is more resusable IMO:

this.$router.push({ name: 'home' }, undefined, () => { location.href = this.$route.hash }) 

As the 3rd argument is the abort() function, it may have unwanted side effects though..

If you want to use it globally, add a function to your Router:

pushWithAnchor: function (routeName, toHash) {     const fromHash = Router.history.current.hash     fromHash !== toHash || !fromHash     ? Router.push({ name: routeName, hash: toHash })     : Router.push({ name: routeName, hash: fromHash }, undefined, () => { window.location.href = toHash })   } 

And use it in components with:

this.$router.options.pushWithAnchor('home', '#fee-calculator-section') 

Within a template you could do something like:

<a @click="this.$router.options.pushWithAnchor('home', '#fee-calculator-section')"></a> 

Sadly you cant use a scroll offset though

like image 27
Valentin Rapp Avatar answered Oct 12 '22 22:10

Valentin Rapp