1

When switching between sections I want to toggle between hide / show and scroll the target section into view.

This code works but it takes two clicks instead of one to complete the process: first click toggles visibility and it takes a second click to actually scroll the target element into view. Try here.

Why is that? How to solve?

Furthermore: advice on how to accomplish this without a separate show variable for each section and without having to pass source section ref is also welcome.

Vue.js:

<template>
  <section id="1" ref="1" v-show="show1" style="margin-bottom: 500px">
    <p>Section 1</p>
    <button @click="showScrollInto('1', '2')">Goto section 2</button>
    <button @click="showScrollInto('1', '3')">Goto section 3</button>
  </section>
  <section id="2" ref="2" v-show="show2">
    <p>Section 2</p>
    <button @click="showScrollInto('2', '1')">Goto home</button>
  </section>
  <section id="3" ref="3" v-show="show3">
    <p>Section 3</p>
    <button @click="showScrollInto('3', '1')">Goto home</button>
  </section>
</template>

<script>

export default {
  name: "App",
  data() {
    return {
      show1: true,
      show2: false,
      show3: false
    }
  },
  methods: {
    showScrollInto(from, to) {
    switch(from) {
      case "2":
        this.show2 = false
        break
      case "3":
        this.show3 = false
        break
      default:
        this.show1 = true
      }
    
    switch(to) {
      case "2":
        this.show2 = true
        break
      case "3":
        this.show3 = true
        break
      default:
        this.show1 = true
      }
      this.$refs[to].scrollIntoView({ behavior: 'smooth'})
    }
  }
}

</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
section {
  height: 400px;
  border: solid 1px black;
}
</style>
user1837293
  • 1,466
  • 3
  • 16
  • 30

2 Answers2

2

After you have changed the data controlling visibility, the DOM still needs to update. When you call scrollIntoView() immediately, there is still no node to scroll to.

You can wait until the update is finished using nextTick():

await this.$nextTick()
this.$refs[to].scrollIntoView({ behavior: "smooth" })

or using a callback:

this.$nextTick(() => this.$refs[to].scrollIntoView({ behavior: "smooth" }))

Now the scroll event happens after the update when the element is available.

Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34
2

Hi I have tried various iterations and I found this.

In your sample code, you are trying to toggle the visibility and instantly scrolling to the section elements. However, this might take a little time to update the accordingly on the DOM.

I have used the nextTick vuejs utility to wait for the DOM update and scroll to the section.

Here is the change

this.$nextTick(() => {
    this.$refs[to].scrollIntoView({ behavior: "smooth" });
  });