vue-infinite-loading icon indicating copy to clipboard operation
vue-infinite-loading copied to clipboard

How can I get this working for horizontal scrolling?

Open PatrLind opened this issue 8 years ago • 16 comments

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?

PatrLind avatar Sep 14 '17 04:09 PatrLind

Yeah, that isn't currently possible with this component.

a-kriya avatar Sep 16 '17 09:09 a-kriya

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...

PatrLind avatar Sep 16 '17 10:09 PatrLind

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.

a-kriya avatar Sep 16 '17 21:09 a-kriya

@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 avatar Sep 17 '17 07:09 PeachScript

谢谢@PeachScript , I will give it a try :)

PatrLind avatar Sep 18 '17 01:09 PatrLind

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.

PatrLind avatar Sep 18 '17 09:09 PatrLind

Haha, @PatrLind your Chinese is excellent! Thanks for your solution!

PeachScript avatar Sep 19 '17 14:09 PeachScript

@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?

demonmind avatar Oct 13 '17 15:10 demonmind

I cant get the @Component({....}) to work. Webpack fails to compile

demonmind avatar Oct 13 '17 18:10 demonmind

@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>

PatrLind avatar Oct 16 '17 02:10 PatrLind

Thank you! will try it and let you know

demonmind avatar Oct 16 '17 17:10 demonmind

@PatrLind this is working !, dont forget to set props direction to right on your infinite-loading-extra component

yagogak avatar Mar 01 '18 09:03 yagogak

@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);
    }
  }
};

rivajunior avatar Aug 05 '19 21:08 rivajunior

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"
          />

vesper8 avatar May 02 '20 00:05 vesper8

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!

vesper8 avatar May 02 '20 00:05 vesper8

@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;
}

joe94113 avatar Dec 28 '22 04:12 joe94113