Closed henrikruscon closed 6 years ago
I'm not really sure what you mean. What exactly do you want to achieve?
Well everything has position absolute and overflow hidden except one container that allows overflow-y scrolling. Similar to how https://stripe.com/docs/api does it. Meaning being able to define which container that gets scrolled. For example https://www.npmjs.com/package/vue-scrollto provides a container option. Right now vue-scrollactive
doesn't work since it doesn't target my container ✌️
Simply put I think providing a container means codewise that it doesn't look for scroll data from window/document/body but instead the provided container.
Ohh, now I think I understand. You mean for the click to scroll functionality, right? If you click on an item it will try to scroll the window, and you want to scroll a specific element acting as a scroll container, is that correct?
You cannot do that currently, but I think it's a quick add. I'll try to work on this tonight and I'll come back with a response.
Yeah that's exactly what I mean. Thanks for looking into it!
Got it working in a local version, want a PR? ✌️
I would appreciate it :)
Still didn't have time to look into it. Thanks!
@henrikdahl @eddiemf any news about the PR ? need this too !
@damienroche I ended up coding my own local ScrollSpy. It's different enough that doing a PR would require some time. Here's the code, specifically the container prop is what enables it.
<template>
<nav role="navigation">
<slot/>
</nav>
</template>
<script>
export default {
name: 'ScrollSpy',
props: {
container: {
type: String,
default: '.content'
},
activeClass: {
type: String,
default: 'is-active'
},
offset: {
type: Number,
default: 1
}
},
data() {
return {
items: [],
currentItem: null,
lastActiveItem: null
}
},
computed: {
scrollContainer() {
return document.querySelector(this.container)
}
},
mounted() {
this.initItems()
this.scrollContainer.addEventListener('scroll', this.onScroll)
},
updated() {
this.initItems()
},
beforeDestroy() {
this.scrollContainer.removeEventListener('scroll', this.onScroll)
},
methods: {
onScroll(event) {
this.currentItem = this.getItemInsideWindow()
if (this.currentItem) {
if (this.currentItem !== this.lastActiveItem) {
this.removeActiveClass()
this.lastActiveItem = this.currentItem
}
this.currentItem.classList.add(this.activeClass)
}
},
getItemInsideWindow() {
let currentItem
this.items.forEach(item => {
const target = document.getElementById(item.hash.substr(1))
const isScreenPastSection = this.scrollContainer.scrollTop >= this.getOffsetTop(target) - this.offset
if (isScreenPastSection) currentItem = item
})
return currentItem
},
initItems() {
this.items = this.$el.querySelectorAll('.sidebar__navigation__item')
this.items.forEach(item => {
item.addEventListener('click', this.handleClick)
})
this.removeActiveClass()
this.currentItem = this.getItemInsideWindow()
if (this.currentItem) this.currentItem.classList.add(this.activeClass)
},
handleClick(event) {
event.preventDefault()
const { hash } = event.currentTarget
const target = document.getElementById(hash.substr(1))
this.scrollContainer.removeEventListener('scroll', this.onScroll)
this.removeActiveClass()
event.currentTarget.classList.add(this.activeClass)
target.scrollIntoView()
this.scrollContainer.addEventListener('scroll', this.onScroll)
if (window.history.replaceState) {
window.history.replaceState(null, null, hash)
} else {
window.location.hash = hash
}
},
getOffsetTop(element) {
let yPosition = 0
let nextElement = element
while (nextElement) {
yPosition += (nextElement.offsetTop)
nextElement = nextElement.offsetParent
}
return yPosition
},
removeActiveClass() {
this.items.forEach(item => {
item.classList.remove(this.activeClass)
})
}
}
}
</script>
@henrikdahl thanks for sharing this, it works like a charm.
Notice for other people, don't forget to a sidebar__navigation__item
class to links or add a custom prop.
Also target.scrollIntoView()
causes some displaying bugs because i'm scrolling into an overflowed div, I replaced this line by
const top = target.offsetTop
this.scrollContainer.scrollTop = top
@damienroche Glad it helped you out!
Since none of the scroll spies did what I needed, I decided to simply maintain my own local version. I'm not really interested in maintaining such a plugin publicly and I think my needs differ too much from vue-scrollactive
for it to be a useful PR in its entirety.
With that said. I'm going to add some additional features to my local ScrollSpy such as expandable groups. Additionally I need the sidebar navigation to scroll if the active item is outside of the view (overflowing) while you scroll the actual container.
Similar to how https://stripe.com/docs/api handles their ScrollSpy.
If you need any of those features, I could provide you the code here and either @eddiemf or someone else could add it to vue-scrollactive
as a PR ✌️
Hello guys, thanks for sharing a workaround for this. Last weeks I've been working on updating another plugin that I maintain, this weekend I'll update this one with refactors and try to add some of the functionalities you mentioned. I really liked how stripe does it in the API docs, I'll use it as a reference and aim to implement all of its functionalities.
Thanks for the support!
@henrikdahl interesting... I have to deal with sidebar scrolling too. I also added some lines of code to allow scroll to current section if hash is present in url
@damienroche
This is how I handle scroll into current section based upon hash ✌️
async beforeCreate() {
const { hash } = window.location
if (hash) {
const target = document.getElementById(hash.substr(1))
target.scrollIntoView()
}
},
What's your approach?
@henrikdahl @damienroche As mentioned in the commit, I'll still work on the features you guys proposed and will implement them very soon. You can open a new issue for them if you want to, but I'll basically just follow the stripe API documentation sidebar as a reference and make sure you can have the same results with this plugin.
Thanks again for the help.
@eddiemf looking forward for it !
Is it possible to target scroll a container instead of body? ✌️