vue.jsvuejs2vue-componentvue-propsanchor-scroll

Scroll to section from child component in Vue.js


I am trying to understand how to emit an event from a child component to the parent that scrolls to the correct section via a ref or id. So for example the navigation menu is a child component with links. when I click a link, the corresponding section in the parent scrolls into view. I am coming from React so I am a bit thrown off. How do I pass parameters up to scroll to correct ref or id? basically the way anchorlinks act.

Here is what I got so far.. I am not sure I should be using links , I might need buttons since i'm not navigating pages.

Navigation.vue (Child)

<template>
  <div class="navigation">
    <img class="logo" alt="Vue logo" src="../assets/logo.png" />
    <nav>
      <ul>
        <li>
          <a @click="goSection" href="/">Contact</a>
        </li>
        <li>
          <a href="/">Projects</a>
        </li>
        <li>
          <a href="/">Skills</a>
        </li>
        <li>
          <a href="/">Education</a>
        </li>
      </ul>
    </nav>
  </div>
</template>

<script>
export default {
  name: "Navigation",
  //I want to emit an event in the parent where the page section scrolls in to view//
  methods: {
    goSection() {
      this.$emit("go-section", this.value);
    },
  },
};
</script>

App.vue (Parent)

<template>
  <div id="app">
    <Navigation @go-section="goToSection" />
    
    <section class="section" ref="projects">
      <p>Projects</p>
    </section>
    <section class="section" ref="skills">
      <p>Skills</p>
    </section>
    <section class="section" ref="contact">
      <p>Contacts</p>
    </section>
  </div>
</template>

<script>
import Navigation from "./components/Navigation.vue";
export default {
  name: "App",
  components: {
    Navigation,
  },
  methods: {
    goToSection(event, value) {
      var element = this.$refs[(event, value)];
      var top = element.offsetTop;
      window.scrollTo(0, top);
    },
  },
};
</script>

Solution

  • You can use a tag but prevent default link behavior, and pass ref to your goSection method :

    Vue.component('navigation', {
      template: `
        <div class="navigation">
          <img class="logo" alt="Vue logo" src="../assets/logo.png" />
          <nav>
            <ul>
              <li>
                <a @click.prevent="goSection('contact')" href="/">Contact</a>
              </li>
              <li>
                <a href="#" @click.prevent="goSection('projects')">Projects</a>
              </li>
              <li>
                <a href="#" @click.prevent="goSection('skils')">Skills</a>
              </li>
              <li>
                <a href="#" @click.prevent="goSection('contact')">Education</a>
              </li>
            </ul>
          </nav>
        </div>
      `,
       methods: {
        goSection(val) {
          this.$emit("go-section", val);
        },
      },
    })
    
    new Vue({
      el: '#demo',
      methods: {
        goToSection(value) {
          const top = this.$refs[(value)].offsetTop;
          window.scrollTo({
            top: top,
            left: 0,
            behavior: 'smooth'
          });
        },
      },
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="demo">
      <navigation @go-section="goToSection"></navigation>
      <section class="section" ref="projects">
        <p>Projects</p>
      </section>
      <section class="section" ref="skills">
        <p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p><p>Skills</p>
      </section>
      <section class="section" ref="contact">
        <p>Contacts</p>
      </section>
    </div>