28
votes

I'm developing a Vue.js application and I'm having trouble to link an anchor to a certain div within a component.

I have the following anchor:

<a href="#porto" class="porto-button">Porto, Portugal</a>

and the following div:

<div id="porto" class="fl-porto">

I'm using vue-router in hash mode.

The problem is, whenever I click the "porto-button" it will redirect me to the "home" page ( ' / ' )

I'm using Vue.js 1.X and I tried using history mode (URL without the hashbang) but it gives me a cannot GET '/page' error upon refreshing a page.

Am I doing something wrong? What can I do about this?

5
I think just take the '#' out of your id? Should just be <div id="porto" class="fl-porto">, no?Kori John Roys
I did that but to no avail. That's the first thing I tried to do actually but I am up to anything at this point. (I'll edit the question nonetheless)fmlopes
Put a named anchor tag inside your div? <a name="porto">, then your a href="#porto"> should work.Kori John Roys
Ah sorry I didn't see you were in hash mode. If you switch to history mode, it should work, but you'll need a catchall route, per: router.vuejs.org/en/essentials/…Kori John Roys
I tried using history mode too. However, the link breaks with cannot GET /page whenever I refresh the page I am infmlopes

5 Answers

38
votes

Because you are using router in hash mode, you will not be able to scroll that easily because scrolling to /#something will actually redirect you to 'something' page.

You will have to emulate scrolling behaviour on your own, try doing something like that:

//P.S. the code is written for Vue 2.
//You will have to adjust it to Vue 1.

//Your view:
<a class="porto-button" @click="scrollMeTo('porto')">Porto, Portugal</a>
...
<div ref="porto" class="fl-porto">
//Your code:
methods: {
  scrollMeTo(refName) {
    var element = this.$refs[refName];
    var top = element.offsetTop;

    window.scrollTo(0, top);
  }
}

How it works:

  1. Set the references through ref attribute to the element you would like to scroll to;
  2. Write a function that will programmatically set window.scrollY to the top of the referenced element.
  3. Job is done :)

Update 1:

jsfiddle https://jsfiddle.net/5k4ptmqg/4/

Update 2:

Seems that in Vue 1 ref="name" looked like el:name (docs), here is an updated example:

https://jsfiddle.net/5y3pkoyz/2/

13
votes

Another method is to use "scrollIntoView()"

So, euvl's code still stands, except you would change the method slightly:


    new Vue({
      el: '#app',
      methods: {
        goto(refName) {
            var element = this.$els[refName];
          element.scrollIntoView();
        }
      }
    })

If you wanted to get fancy and make the scroll smooth, you can even add the following:

element.scrollIntoView({ behavior: 'smooth' });

Note that this will need a polyfill for older browsers.

9
votes

What worked for me

<router-link to="#leaders">Leaders</router-link>

or dynamic

<router-link :to="`#${subMenuItem.linkTarget}`" class="page-submenu-list__link">
                    {{subMenuItem.linkTitle}}
                </router-link>

in router

routes:[],
scrollBehavior (to, from, savedPosition) {
    //https://router.vuejs.org/guide/advanced/scroll-behavior.html
    if (to.hash) {
            return { selector: to.hash }
        } else if (savedPosition) {
            return savedPosition;
        } else {
            return { x: 0, y: 0 }
        }
  }
4
votes

An alternative solution is to use the v-scroll-to directive (webpage, github). I find this solution to be clean, simple, flexible and effective. To use:

  1. Install it:

    npm install --save vue-scrollto
    
  2. Have Vue 'use' it:

    var VueScrollTo = require('vue-scrollto');
    Vue.use(VueScrollTo)
    
  3. Apply it as a directive in your Vue component's template:

    <a href="#" v-scroll-to="'#element'">Scroll to #element</a>
    
    <div id="element">
      Hi. I'm #element.
    </div>
    
  4. Or apply it programmatically in your Vue component's methods:

    this.$scrollTo('#element', 500, { easing: 'ease-in-out' })
    
  5. Or apply it programmatically in your Vuex actions:

    import { scrollTo } from 'vue-scrollto'
    
    scrollTo('#element', 500, { easing: 'ease-in-out' })
    

Another solution, if you're already using Vuetify, you may prefer to use Vuetify's built-in programmatic scrolling method, $vuetify.goTo():

<v-btn @click="$vuetify.goTo('#element', {duration: 500, easing: 'easeInOutCubic'})">
  Scroll to #element
</v-btn>

<div id="element">
  Hi. I'm #element.
</div>
1
votes

If you set a ref="something" on an element, you could also use this oneliner with @click:

<a @click="$refs.something.$el.scrollIntoView()">
    Go to something
</a>