PeachScript / vue-infinite-loading

An infinite scroll plugin for Vue.js.
https://peachscript.github.io/vue-infinite-loading/
MIT License
2.67k stars 368 forks source link

"loaded" doesn't stop loading #55

Closed DaveSanders closed 7 years ago

DaveSanders commented 7 years ago

v2.1.3 - for some reason the "loaded" emit isn't stopping it from continuing to load results down the page. I'm pretty sure this was working before, but I can't figure out why this is happening.

So, basically even though I haven't scrolled down, it keeps hitting my server and loading more results. Here's my onInfinite function:

onInfinite () {
        api.tracks.get(PAGE_AMOUNT, this.tracks.length, tr => {
          console.log(`TRACKS: ${this.tracks.length}`)

          if (tr.length > 0) {
            this.tracks = this.tracks.concat(tr)
            if (tr.length < PAGE_AMOUNT) {
              this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete')
            } else {
              this.$refs.infiniteLoading.$emit('$InfiniteLoading:loaded')
            }
          } else {
            this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete')
          }
        })
      }

My template:

<template>
  <div id="track-list">
    <TrackItem v-for='(track, index) in tracks' :track='track' :index="index" :key='track.id'/>
    <infinite-loading :on-infinite="onInfinite" ref="infiniteLoading" spinner="waveDots"></infinite-loading>
  </div>
</template>

I did put console logs in and it IS hitting that else where it sends the loaded emit. I also changed it to :complete and it stopped calling at that point in the code. So it is emitting, it just seems like its not caring - or for some reason it thinks that its scrolled down.

Anything I'm doing wrong? Or any ideas on how to debug it further?

DaveSanders commented 7 years ago

Simplified the onInfinite, but same result:

 onInfinite () {
        api.tracks.get(PAGE_AMOUNT, this.tracks.length, tr => {
          if (tr.length > 0) {
            this.tracks = this.tracks.concat(tr)
            console.log(`TRACKS: ${this.tracks.length}`)
            this.$refs.infiniteLoading.$emit('$InfiniteLoading:loaded')
          } else {
            this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete')
          }
        })
      }
PeachScript commented 7 years ago

I have changed the distance calculation method since v2.1.2, perhaps there has something wrong in the latest version if you were using a version earlier than v2.1.2 before, could you reproduce this problem on JSFiddle or other website like it?

Akari1987 commented 7 years ago

When I use in nested component, I have same issue.

Akari1987 commented 7 years ago

I'm sorry it was my misunderstanding. I used vue within Laravel, so it may be caused by laravel blade tempalte in my case :)

PenAndPapers commented 7 years ago

I currently experiencing this kind of issue, it creates multiple request during page loading I'm using Laravel 5.4 and axois. Below is my JS code

axios.get(config.API + config.API.PLAYERSLEADERBOARD, {
    params: {
        splits: self.splits,
        playingPosition: self.playingPosition,
        competitionId: self.competitionId,
        page: self.pagination,
        sort: self.sort,
        order: self.order,
        limit: self.limit,
    }
}).then( (response) => {
    let data = response.data.data;
    let lastpage = response.data.last_page;

    if ( data.length ) {
        self.leagueLeaders = self.leagueLeaders.concat(data);
        self.$refs.infiniteLoading.$emit('$InfiniteLoading:loaded');
        self.pagination += 1;
        if(self.pagination == lastpage){
            self.$refs.infiniteLoading.$emit('$InfiniteLoading:complete');
            console.log(123);
        }
    }
    else {
        self.$refs.infiniteLoading.$emit('$InfiniteLoading:complete');
        if( url.includes('orderby') ) {
            let order = url.split('/').pop();
            let highlight = document.getElementsByClassName(order);
            _.forEach(highlight, (elem, key) => {
                elem.className += ' hightlight';
            });
        }
        else {
            let highlight = document.getElementsByClassName('points');
            _.forEach(highlight, (elem, key) => {
                elem.className += ' hightlight';
            });
        }
    }
});
rodwin commented 7 years ago

I am also experiencing this issue. The event keeps on firing even if I am not scrolling. Its weird because I just followed the guide. I am using elementUI's table

PeachScript commented 7 years ago

@PenAndPapers @rodwin which version of Vue.js and this component do you use? If it is possible, please reproduce your problem on JSFiddle, thanks!

PenAndPapers commented 7 years ago

I'm using "vue": "^2.0.1" and "axios": "^0.15.2".

PenAndPapers commented 7 years ago

I thought it was not compatible with axios so I tried to use "vue-resource": "^1.2.1", but still no luck

PeachScript commented 7 years ago

@PenAndPapers I think it was not caused by ajax lib, if you are using vue-router and enable the keep-alive feature, I recommend you to try to use the Vue.js v2.2.0 and later, refer issue.

mcmillion commented 7 years ago

I'm seeing this issue as well with VueJS 2.3.4. I'm also seeing it send out two events each time.

PenAndPapers commented 7 years ago

@PeachScript I've tried Vue.js v2.2.6 still not working, im not using vue router on my project

PeachScript commented 7 years ago

@mcmillion @PenAndPapers I need your help to find the real reason, could you reproduce this problem on JSFiddle? You can use setTimeout instead of ajax requests, thanks!

vbabenko commented 7 years ago

I have same issue (Vue 2.3) and vue-infinite-loading (2.1.3) sends event all time. Similar case to @DaveSanders. I tried to create jsFiddle but I could not reproduce https://jsfiddle.net/vbabenko/fb6r82eb/3/ even with flexbox. It's pretty hard to move part of project to jsfiddle for reproducing...

@PeachScript Could the issue be related to css?

DaveSanders commented 7 years ago

To make matters worse (at least for finding the bug) its stopped doing it for me, and seems to be working fine now. I'm really not sure what I've changed. I'm guessing its related to page structure, and I did move some things around when we redesigned the screen. I was going to go back to look at old commits to see if I can find the difference, but I've been slammed with crunch time here.

vbabenko commented 7 years ago

@DaveSanders yeah, it's my assumption too, I am using vue-infinite-loading on several projects and only one has such behavior. Looks like it sensitive to html/css structure.

adamyarger commented 7 years ago

@DaveSanders Ive run into the same problem when I nest 'infinite-loading' in divs while using window as the scroll parent. What I've noticed in the infiniteLoading.vue file is that the getCurrentDistance function does not account for the infinite-loading component to be further down the page then it is.

When i change the function to the following everything is resolved.

  function getCurrentDistance(elm, dir) {
    let distance;
    const scrollTop = isNaN(elm.scrollTop) ? elm.pageYOffset : elm.scrollTop;
    if (dir === 'top') {
      distance = scrollTop;
    } else {
      let scrollElmHeight;
      if (elm === window) {
        scrollElmHeight = window.innerHeight;
        distance = this.$el.getBoundingClientRect().top - scrollTop - scrollElmHeight - (elm.offsetTop || 0);
      } else {
        scrollElmHeight = elm.getBoundingClientRect().height;
        distance = this.$el.offsetTop - scrollTop - scrollElmHeight - (elm.offsetTop || 0);
      }
    }
    return distance;
  }
PeachScript commented 7 years ago

@vbabenko I'm not sure how this issue is caused, maybe related to CSS/HTML. The tricky place is, we have no way to reproduce it, so it is difficult to find the real reason...

PeachScript commented 7 years ago

@DaveSanders waiting for your good news, thank you very much!

PeachScript commented 7 years ago

@vbabenko is there has some specials in your problem project? Such as the different scroll container, the different front-end router, the different nested HTML structure, etc.

duyhung85 commented 7 years ago

I'm having this issue again today too. In my case I think its because the component that have infiniteLoading init and running is hidden and it keeps loading till I open it. Here is my setup: I have a toolbar tab component (using Bootstrap for HTML&CSS):

<div class="toolbar-content nano">
            <div class="tab-content nano-content" id="toolbar-tab">
                <tab-layout></tab-layout>
                <tab-bkgrnd></tab-bkgrnd>           
                </div>
            </div>
        </div>

The tab-bkgrnd component is hidden by default and that is where I have infiniteLoading setup and running. When I first access the page infiniteLoading keeps loading forever until I click on tab-bkgrnd. If I change tab-bkgrnd to open by default then its only load once and working as expected when scrolling down.

PeachScript commented 7 years ago

@duyhung85 yep, because the getBoundingClientRect method will all return 0 if the element was hidden, so this plugin cannot calculate correct distance if it was hidden, perhaps you can try v-if to solve it.

vbabenko commented 7 years ago

@PeachScript I think the issue appear when styles wrote in bad manner, like this https://jsfiddle.net/vbabenko/fb6r82eb/7/

css class 'top' has overflow-x: hidden css class 'deploy' has overflow: auto

in this case it will invoke function to the end while data exists. (open console to see invocation)

when you comment overflow: auto in last class - everything will work.

Maybe it can help.

When I have time I will try debug source file to be sure on 100%

PeachScript commented 7 years ago

@vbabenko it's a very useful information, thank you very much!

PeachScript commented 7 years ago

@DaveSanders @PenAndPapers @rodwin @vbabenko @adamyarger @mcmillion hi everyone, sorry to bother, could you tell me does your scroll container has a fixed height?

vbabenko commented 7 years ago

@PeachScript, it's okay, thank you for your library, it's awesome.

In my case - no (everything positioning by flexbox), my scroll container has flex-grow: 1; overflow: auto;

adamyarger commented 7 years ago

@PeachScript, no im using the window to scroll. I believe i may have been using an older version of the component before when when offsetTop was being used to measure the distance, it now looks like getBoundingClientRect().top is being used. So everything works as expected now.

PeachScript commented 7 years ago

@vbabenko thanks for your appreciation! This plugin will calculate distance through the getBoundingClientRect().top of infinite-loading element and the getBoundingClientRect().bottom of scroll container, the distance is expected when the top - bottom <= the passing distance, but if the scroll container has automatic height, the distance will always be expected...

So I think should to add a additional condition to process it, I will as soon as possible.

PeachScript commented 7 years ago

@adamyarger got it, thanks for your feedback!

Best regards.

PeachScript commented 7 years ago

@vbabenko @Akari1987 I have encountered a thorny problem. For now, this plugin will find the closest parent node that has overflow-y: auto CSS style as the scroll container, but in the example that provided by @vbabenko , the closest overflow-y: auto parent node is not the real scroll container, so I have planed to check the height property at the same time, if the element has overflow-y: auto, and it's height property not be auto or it's max-height property not be none, auto or 0px, it should be the real scroll container. The problem is, I did not found a better way to detect whether a element's height property is auto, do you have any idea?

vbabenko commented 7 years ago

@PeachScript yeah, it's not easy solution. I will think about any other ideas. But for now, I cannot imagine better option than your. @PeachScript what do you think if you add extra properties like settings object where user can manually provide css selector to scroll container? By default it will work with your approach and in rare case when something goes wrong a user can provide container selector

Akari1987 commented 7 years ago

@PeachScript mmm... I do not have any idea at the moment. If I hit on good idea, I'll write here.

PeachScript commented 7 years ago

@vbabenko for the manual scroll container, we can easily do it now:

mounted() {
  this.$refs.infiniteLoading.scrollParent = this.$refs.yourManuallyContainer;
}

But the key point is, how the users know the scroll container is wrong 😞

PeachScript commented 7 years ago

@Akari1987 thanks, waiting for your good news!

PeachScript commented 7 years ago

Hi all participants @DaveSanders @Akari1987 @PenAndPapers @rodwin @mcmillion @vbabenko @adamyarger @duyhung85 , I have no idea about how to find the real scroll parent, but this issue has been created for 2 months, so I plan to add a special property to solve it, looks like this:

<div infinite-wrapper>
  <div style="overflow: auto;">
    <infinite-loading force-use-infinite-wrapper="true"></infinite-loading>
  </div>
</div>

The special property called force-use-infinite-wrapper, it will find the closest parent node which has infinite-wrapper attribute, and it will ignore other parent node, even if some parents has overflow-y: auto|scroll CSS styles.

What do you think about the solution? And how about the attribute name force-use-infinite-wrapper?

Best regards.

vbabenko commented 7 years ago

Sounds good. It would be nice to update documentation, this property is not obvious for those who doesn't aware about this issue

PeachScript commented 7 years ago

@vbabenko perhaps we can observe the number of times when this component be trigger, and we can throw a warning if it was triggered many times in a short time, do you think it is feasible?

JefferyHus commented 7 years ago

I dunno but I have been testing around, what i have noticed is that first you have to set the height of your body to auto then this listen will do a great work:

window.onscroll = function(ev) {
    if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
        // scroll hits the floor, then you can call the function
    }
};
PeachScript commented 7 years ago

@JefferyHus Sorry, I don't know what do you want to solve...Is your solution related to this issue?

JefferyHus commented 7 years ago

yes it is, when ever I scroll down once the loading never stops and keeps calling my function to the infinite, so I had to change the function on the scroll listener and it worked now.

PeachScript commented 7 years ago

@JefferyHus thanks for your reply! Perhaps there are many reasons for this issue, I need your help, could you reproduce your problem on JSFiddle?

JefferyHus commented 7 years ago

@PeachScript emmm, I will try even tho im not sure if it will reproduce again but will also put the solution I did, once im free

PeachScript commented 7 years ago

Hi everyone, v2.2.0 has been released, include the force-use-infinite-wrapper feature, you can try it now, please feedback to here if you find any problem, thanks :D

junpls commented 7 years ago

@PeachScript thanks a lot for force-use-infinite-wrapper! My current problem with it though is that I cannot chance this property during runtime. I have a responsive design in which I only want this behavior in portrait mode. What I'm trying is :force-use-infinite-wrapper="!landscape"

PeachScript commented 7 years ago

@junpls this component will only search scroll wrapper when it is initialized, what is the situation that you need to change scroll wrapper during runtime?

junpls commented 7 years ago

@PeachScript this is what I'm trying: (It should say "infinite" on the left side as well) ab2e398604bab119

PeachScript commented 7 years ago

@junpls I see, thanks for your explanation, I will add responsive support the force-use-infinite-wrapper property in the next version.

lautiamkok commented 6 years ago

This works for me:

mounted() {
       window.addEventListener('scroll',this.onscroll)
 },
onscroll: async function (event) {
      const scrollY = window.scrollY
      const visible = document.documentElement.clientHeight
      const pageHeight = document.documentElement.scrollHeight
      const bottomOfPage = visible + scrollY >= pageHeight

      if (bottomOfPage || pageHeight < visible) {
            this.fetchPosts()
           console.log('At the bottom');
      }
},

Ref: https://scotch.io/tutorials/simple-asynchronous-infinite-scroll-with-vue-watchers

yasin7044 commented 3 years ago

v2.1.3 - for some reason the "loaded" emit isn't stopping it from continuing to load results down the page. I'm pretty sure this was working before, but I can't figure out why this is happening.

So, basically even though I haven't scrolled down, it keeps hitting my server and loading more results. Here's my onInfinite function:

onInfinite () {
        api.tracks.get(PAGE_AMOUNT, this.tracks.length, tr => {
          console.log(`TRACKS: ${this.tracks.length}`)

          if (tr.length > 0) {
            this.tracks = this.tracks.concat(tr)
            if (tr.length < PAGE_AMOUNT) {
              this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete')
            } else {
              this.$refs.infiniteLoading.$emit('$InfiniteLoading:loaded')
            }
          } else {
            this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete')
          }
        })
      }

My template:

<template>
  <div id="track-list">
    <TrackItem v-for='(track, index) in tracks' :track='track' :index="index" :key='track.id'/>
    <infinite-loading :on-infinite="onInfinite" ref="infiniteLoading" spinner="waveDots"></infinite-loading>
  </div>
</template>

I did put console logs in and it IS hitting that else where it sends the loaded emit. I also changed it to :complete and it stopped calling at that point in the code. So it is emitting, it just seems like its not caring - or for some reason it thinks that its scrolled down.

Anything I'm doing wrong? Or any ideas on how to debug it further?

please add infinite-wrapper attriubte and :force-use-infinite-wrapper="true" <infinite-loading @infinite="infiniteHandler" :identifier="indetifyId" :force-use-infinite-wrapper="true" spinner="circles">

<TrackItem v-for='(track, index) in tracks' :track='track' :index="index" :key='track.id'

infinite-wrapper />

greetfish commented 3 years ago

I also experiencing this issue, but not about CSS/html ,it was coursed by the array increase method, when i increase array like this Array.prototype.push.apply(this.infiniteArticles.content, newArticles.content) i get uncontrol infinite loading, and it work well like this this.infiniteArticles.content = this.infiniteArticles.content.concat(newArticles.content)