Codesandbox: https://codesandbox.io/s/romantic-pond-ehixi?file=/pages/index.vue
Hi, let me explain the design first before the problem so we all are on the same view 🙂
I am currently new to Vue and working on a Nuxtjs project with the Vuex store.
In my layout/default.vue
, I have a Header
, Footer
component, and <Nuxt />
between them.
Both Header
& Footer
will be fixed/sticky in the top and bottom.
The main content will be scrollable and it has cards inside it.
⚙️ Problem:
The Header
component will have navigation which will get highlighted when the correct card is in the viewport (at least 100px offset).
In the Footer
component, there are buttons to navigate the user to previous or next card (100px offset included) depending on what card the user is looking at.
I am using vue2-scrollspy but it's not working (see codesandbox). I am also not sure if this is the right module to do the job.
⚠️ UPDATE 01
Scroll Spy is working now when the user scrolls the page & I added a watcher to update the store values. The remaining issue is to scroll the user to the previous or next card depending on what they are viewing. On vue2-scrollspy
document, it says the following.
$scrollTo(index: int) is provided on scope Vue instance to invoke a scroll to the given section index.
However, calling $scrollTo(0)
or this.$scrollTo(0)
giving me an error:
this.$scrollTo is not a function
Codesandbox has been updated with the latest changes 🙏
Ok so this is how I fix my issue 🙏
On the /pages/index.vue
I added ID on every card and a watcher to check every time scroll spy change count
value. If count
change, I update the value on the store.global [currentView, previousView, nextView].
📄 /pages/index.vue
<template>
<main class="main" v-scroll-spy="{ data: 'count', offset: 250 }">
<div
:key="item"
v-for="(item, index) in sections"
:id="`view-${index}`"
class="main-child card"
:class="[`bg-${index}`]"
>
<h1>{{ item }}</h1>
<p>{{ `${index} ${item} ${count}` }}</p>
</div>
</main>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
computed: {
sections() {
return this.$store.state.sections;
}
},
watch: {
count(newValue) {
this.$store.dispatch("updateCurrentView", newValue);
this.$store.dispatch(
"updatePreviousView",
newValue === 0 ? 0 : newValue - 1
);
this.$store.dispatch(
"updateNextView",
newValue === this.sections.length - 1
? this.sections.length - 1
: newValue + 1
);
}
}
};
</script>
🌍 /store/index.js
export const state = () => ({
global: {
currentView: 0,
previousView: 0,
nextView: 1
},
sections: ["first", "second", "third", "fourth", "fifth", "sixth"]
});
export const mutations = {
UPDATE_CURRENT_VIEW(state, value) {
state.global.currentView = value;
},
UPDATE_PREVIOUS_VIEW(state, value) {
state.global.previousView = value;
},
UPDATE_NEXT_VIEW(state, value) {
state.global.nextView = value;
}
};
export const actions = {
updateCurrentView({ commit }, value) {
commit("UPDATE_CURRENT_VIEW", value);
},
updatePreviousView({ commit }, value) {
commit("UPDATE_PREVIOUS_VIEW", value);
},
updateNextView({ commit }, value) {
commit("UPDATE_NEXT_VIEW", value);
}
};
On the component where the button up
& down
is located, I can then call the global state and add actions to scroll to previous or next element base on the global values:
📦 /components/WithUpDownButtons.vue
export default {
computed: {
global() {
return this.$store.state.global
}
...
},
methods: {
scrollTopPrevious() {
// scroll to previous element into view
// this.$scrollTo(this.previousView) < not working
document
.getElementById(`view-${this.global.previousView}`)
.scrollIntoView(true);
},
scrollTopNext() {
// scroll to next element into view
// this.$scrollTo(this.nextView) < not working
document
.getElementById(`view-${this.global.nextView}`)
.scrollIntoView(true);
}
}
};
If we need to add scroll animation, we can update the scrollIntoView(true)
with the following:
📦 /components/WithUpDownButtons.vue
...
methods: {
scrollTopPrevious() {
// scroll to previous element into view
document
.getElementById(`view-${this.global.previousView}`)
.scrollIntoView({
top: 0,
behavior: 'smooth'
});
},
scrollTopNext() {
// scroll to next element into view
document
.getElementById(`view-${this.global.nextView}`)
.scrollIntoView({
top: 0,
behavior: 'smooth'
});
}
}
...