vue-infinite-loading
vue-infinite-loading copied to clipboard
How can I get this working for horizontal scrolling?
I want to have a infinite single line horizontal scrolling container, but I cannot seem to get this working with this component. Is it possible or do I need to look elsewhere?
Yeah, that isn't currently possible with this component.
Ok, I see, how difficult would you think it would be to implement? I have not looked at the code yet, but maybe I could try to modify it myself and perhaps make a PR if it works out ok...
This particular block of code currently assumes vertical scrolling -- uses scrollTop/yOffsets/innerHeight/top-bottom positions.
Perhaps if you created another method that would be used for a horizontal scrolling mode, then it may be something @PeachScript want to include in this component.
@PatrLind @syn-zeta thank you, please give me some time to think about it.
For the temporary solution, you can use the extend feature to override the getCurrentDistance method and make it support calculate horizontal distance.
谢谢@PeachScript , I will give it a try :)
It seems my test component I made works as I need it to. In case anyone can make use of it, here it is (TypeScript):
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
import InfiniteLoading from 'vue-infinite-loading'
@Component({
extends: InfiniteLoading,
})
export class InfiniteLoadingExtra extends Vue {
direction: 'top' | 'left' | 'right' | 'bottom'
scrollParent: any
forceUseInfiniteWrapper: boolean | null
getCurrentDistance(this: InfiniteLoadingExtra) {
let distance: number = 0
if (this.direction === 'top') {
distance = isNaN(this.scrollParent.scrollTop) ?
this.scrollParent.pageYOffset :
this.scrollParent.scrollTop
} else if (this.direction === 'left') {
distance = isNaN(this.scrollParent.scrollLeft) ?
this.scrollParent.pageXOffset :
this.scrollParent.scrollLeft
} else if (this.direction === 'bottom') {
const infiniteElmOffsetTopFromBottom = this.$el.getBoundingClientRect().top
const scrollElmOffsetTopFromBottom = this.scrollParent === window ?
window.innerHeight :
this.scrollParent.getBoundingClientRect().bottom
distance = infiniteElmOffsetTopFromBottom - scrollElmOffsetTopFromBottom
} else if (this.direction === 'right') {
const infiniteElmOffsetLeftFromRight = this.$el.getBoundingClientRect().left
const scrollElmOffsetLeftFromRight = this.scrollParent === window ?
window.innerWidth :
this.scrollParent.getBoundingClientRect().right
distance = infiniteElmOffsetLeftFromRight - scrollElmOffsetLeftFromRight
}
return distance
}
getScrollParent(elm = this.$el): any {
let result: any | undefined
if (elm.tagName === 'BODY') {
result = window
} else if (!this.forceUseInfiniteWrapper && (
((this.direction === 'top' || this.direction === 'bottom') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY!) > -1) ||
((this.direction === 'left' || this.direction === 'right') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX!) > -1)
)) {
result = elm
} else if (elm.hasAttribute('infinite-wrapper') || elm.hasAttribute('data-infinite-wrapper')) {
result = elm
}
return result || this.getScrollParent(elm.parentNode as any)
}
}
Note that I needed to get the latest source code for this to work. The one I got thru npm was not extendable.
Haha, @PatrLind your Chinese is excellent! Thanks for your solution!
@PatrLind this is something I need to use in my project too, but we do not use typescript. Do you have a JS snippet i can use?
I cant get the @Component({....}) to work. Webpack fails to compile
@demonmind sorry for that, I made a vanilla JS version for you, but I have not tested it very well, so please give it a try:
<script>
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
import InfiniteLoading from 'vue-infinite-loading'
export default {
name: 'infinite-loading-extra',
extends: InfiniteLoading,
methods: {
getCurrentDistance() {
let distance = 0
if (this.direction === 'top') {
distance = isNaN(this.scrollParent.scrollTop) ?
this.scrollParent.pageYOffset :
this.scrollParent.scrollTop
} else if (this.direction === 'left') {
distance = isNaN(this.scrollParent.scrollLeft) ?
this.scrollParent.pageXOffset :
this.scrollParent.scrollLeft
} else if (this.direction === 'bottom') {
const infiniteElmOffsetTopFromBottom = this.$el.getBoundingClientRect().top
const scrollElmOffsetTopFromBottom = this.scrollParent === window ?
window.innerHeight :
this.scrollParent.getBoundingClientRect().bottom
distance = infiniteElmOffsetTopFromBottom - scrollElmOffsetTopFromBottom
} else if (this.direction === 'right') {
const infiniteElmOffsetLeftFromRight = this.$el.getBoundingClientRect().left
const scrollElmOffsetLeftFromRight = this.scrollParent === window ?
window.innerWidth :
this.scrollParent.getBoundingClientRect().right
distance = infiniteElmOffsetLeftFromRight - scrollElmOffsetLeftFromRight
}
return distance
},
getScrollParent(elm = this.$el) {
let result
if (elm.tagName === 'BODY') {
result = window
} else if (!this.forceUseInfiniteWrapper && (
((this.direction === 'top' || this.direction === 'bottom') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY) > -1) ||
((this.direction === 'left' || this.direction === 'right') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX) > -1)
)) {
result = elm
} else if (elm.hasAttribute('infinite-wrapper') || elm.hasAttribute('data-infinite-wrapper')) {
result = elm
}
return result || this.getScrollParent(elm.parentNode)
}
}
}
</script>
Thank you! will try it and let you know
@PatrLind this is working !, dont forget to set props direction to right on your infinite-loading-extra component
@demonmind sorry for that, I made a vanilla JS version for you, but I have not tested it very well, so please give it a try:
<script> import Vue from 'vue' import { Component } from 'vue-property-decorator' import InfiniteLoading from 'vue-infinite-loading' export default { name: 'infinite-loading-extra', extends: InfiniteLoading, methods: { getCurrentDistance() { let distance = 0 if (this.direction === 'top') { distance = isNaN(this.scrollParent.scrollTop) ? this.scrollParent.pageYOffset : this.scrollParent.scrollTop } else if (this.direction === 'left') { distance = isNaN(this.scrollParent.scrollLeft) ? this.scrollParent.pageXOffset : this.scrollParent.scrollLeft } else if (this.direction === 'bottom') { const infiniteElmOffsetTopFromBottom = this.$el.getBoundingClientRect().top const scrollElmOffsetTopFromBottom = this.scrollParent === window ? window.innerHeight : this.scrollParent.getBoundingClientRect().bottom distance = infiniteElmOffsetTopFromBottom - scrollElmOffsetTopFromBottom } else if (this.direction === 'right') { const infiniteElmOffsetLeftFromRight = this.$el.getBoundingClientRect().left const scrollElmOffsetLeftFromRight = this.scrollParent === window ? window.innerWidth : this.scrollParent.getBoundingClientRect().right distance = infiniteElmOffsetLeftFromRight - scrollElmOffsetLeftFromRight } return distance }, getScrollParent(elm = this.$el) { let result if (elm.tagName === 'BODY') { result = window } else if (!this.forceUseInfiniteWrapper && ( ((this.direction === 'top' || this.direction === 'bottom') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY) > -1) || ((this.direction === 'left' || this.direction === 'right') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX) > -1) )) { result = elm } else if (elm.hasAttribute('infinite-wrapper') || elm.hasAttribute('data-infinite-wrapper')) { result = elm } return result || this.getScrollParent(elm.parentNode) } } } </script>
I have formated the code, but even that way, didn't work
import InfiniteLoading from 'vue-infinite-loading';
export default {
name: 'InfiniteLoadingExtra',
extends: InfiniteLoading,
methods: {
getCurrentDistance() {
switch (this.direction) {
case 'top':
return isNaN(this.scrollParent.scrollTop)
? this.scrollParent.pageYOffset
: this.scrollParent.scrollTop;
case 'left':
return isNaN(this.scrollParent.scrollLeft)
? this.scrollParent.pageXOffset
: this.scrollParent.scrollLeft;
case 'bottom':
return (
this.$el.getBoundingClientRect().top -
(this.scrollParent === window
? window.innerHeight
: this.scrollParent.getBoundingClientRect().bottom)
);
case 'right':
return (
this.$el.getBoundingClientRect().left -
(this.scrollParent === window
? window.innerWidth
: this.scrollParent.getBoundingClientRect().right)
);
default:
return 0;
}
},
getScrollParent(elm = this.$el) {
if (elm.tagName === 'BODY') {
return window;
}
if (
!this.forceUseInfiniteWrapper &&
(((this.direction === 'top' || this.direction === 'bottom') &&
['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY) > -1) ||
((this.direction === 'left' || this.direction === 'right') &&
['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX) > -1))
) {
return elm;
}
if (
elm.hasAttribute('infinite-wrapper') ||
elm.hasAttribute('data-infinite-wrapper')
) {
return elm;
}
return this.getScrollParent(elm.parentNode);
}
}
};
works great! This should be merged into the core @PeachScript
@rivajunior I think maybe it's not working for you because you didn't set the direction? I guess it was not mentioned anywhere but in order for this to work with horizontal scrolling you have to set the direction to "right" because otherwise it defaults to "bottom"
like so:
<InfiniteScrollingHorizontal
direction="right"
@infinite="infiniteHandler"
/>
One thing however is that when using horizontal scrolling, it only works if I trigger the first call to infiniteHandler manually.. which results in some warnings in the debug console Uncaught (in promise) TypeError: Cannot read property 'loaded' of undefined but it does otherwise seem to work fine
I don't have this problem with vertical scrolling.
Did you have the same problem @PatrLind with having to trigger the first load manually? If not.. any insight on how you got around that?
Thanks!
@demonmind sorry for that, I made a vanilla JS version for you, but I have not tested it very well, so please give it a try:
<script> import Vue from 'vue' import { Component } from 'vue-property-decorator' import InfiniteLoading from 'vue-infinite-loading' export default { name: 'infinite-loading-extra', extends: InfiniteLoading, methods: { getCurrentDistance() { let distance = 0 if (this.direction === 'top') { distance = isNaN(this.scrollParent.scrollTop) ? this.scrollParent.pageYOffset : this.scrollParent.scrollTop } else if (this.direction === 'left') { distance = isNaN(this.scrollParent.scrollLeft) ? this.scrollParent.pageXOffset : this.scrollParent.scrollLeft } else if (this.direction === 'bottom') { const infiniteElmOffsetTopFromBottom = this.$el.getBoundingClientRect().top const scrollElmOffsetTopFromBottom = this.scrollParent === window ? window.innerHeight : this.scrollParent.getBoundingClientRect().bottom distance = infiniteElmOffsetTopFromBottom - scrollElmOffsetTopFromBottom } else if (this.direction === 'right') { const infiniteElmOffsetLeftFromRight = this.$el.getBoundingClientRect().left const scrollElmOffsetLeftFromRight = this.scrollParent === window ? window.innerWidth : this.scrollParent.getBoundingClientRect().right distance = infiniteElmOffsetLeftFromRight - scrollElmOffsetLeftFromRight } return distance }, getScrollParent(elm = this.$el) { let result if (elm.tagName === 'BODY') { result = window } else if (!this.forceUseInfiniteWrapper && ( ((this.direction === 'top' || this.direction === 'bottom') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY) > -1) || ((this.direction === 'left' || this.direction === 'right') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX) > -1) )) { result = elm } else if (elm.hasAttribute('infinite-wrapper') || elm.hasAttribute('data-infinite-wrapper')) { result = elm } return result || this.getScrollParent(elm.parentNode) } } } </script>I have formated the code, but even that way, didn't work
import InfiniteLoading from 'vue-infinite-loading'; export default { name: 'InfiniteLoadingExtra', extends: InfiniteLoading, methods: { getCurrentDistance() { switch (this.direction) { case 'top': return isNaN(this.scrollParent.scrollTop) ? this.scrollParent.pageYOffset : this.scrollParent.scrollTop; case 'left': return isNaN(this.scrollParent.scrollLeft) ? this.scrollParent.pageXOffset : this.scrollParent.scrollLeft; case 'bottom': return ( this.$el.getBoundingClientRect().top - (this.scrollParent === window ? window.innerHeight : this.scrollParent.getBoundingClientRect().bottom) ); case 'right': return ( this.$el.getBoundingClientRect().left - (this.scrollParent === window ? window.innerWidth : this.scrollParent.getBoundingClientRect().right) ); default: return 0; } }, getScrollParent(elm = this.$el) { if (elm.tagName === 'BODY') { return window; } if ( !this.forceUseInfiniteWrapper && (((this.direction === 'top' || this.direction === 'bottom') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowY) > -1) || ((this.direction === 'left' || this.direction === 'right') && ['scroll', 'auto'].indexOf(getComputedStyle(elm).overflowX) > -1)) ) { return elm; } if ( elm.hasAttribute('infinite-wrapper') || elm.hasAttribute('data-infinite-wrapper') ) { return elm; } return this.getScrollParent(elm.parentNode); } } };
I use this code but it doesn't work.
I set the distance to the right, but the return value is less than 100, so the scroll loading event is always triggered.
The following is my template code, and version vue-infinite-loading": "^2.4.5
Can someone help me? Thanks
<template>
<section>
<div class="scroll">
<div>
<div class="item" v-for="item in items">
{{ item }}
</div>
<InfiniteLoadingExtra
direction="right"
@infinite="infiniteHandler"
ref="infiniteLoading"
>
</InfiniteLoadingExtra>
</div>
</div>
</section>
</template>
<style scoped>
.scroll {
white-space: nowrap;
overflow-x: scroll;
}
.item {
min-width: 500px;
min-height: 50px;
background-color: aqua;
border-right: 1px solid;
white-space: nowrap;
display: inline-block;
}