Skip to content
This repository has been archived by the owner on Mar 27, 2019. It is now read-only.

Pull-to-refersh support in iScroll5? #378

Open
doochik opened this issue Jun 6, 2013 · 50 comments
Open

Pull-to-refersh support in iScroll5? #378

doochik opened this issue Jun 6, 2013 · 50 comments

Comments

@doochik
Copy link
Contributor

doochik commented Jun 6, 2013

Hi!
There was topOffset option for pull-to-referesh in iScroll4. But I can't find it in iScroll5. Is it supported?

@cubiq
Copy link
Owner

cubiq commented Jun 6, 2013

I'm working on pull to refresh for v5. I removed topOffset because I don't know yet which is the best way to support the pull to refresh.

@doochik
Copy link
Contributor Author

doochik commented Jun 6, 2013

Thanks!
When you plan to do it?

@ia3andy
Copy link

ia3andy commented Jun 9, 2013

👍
Seems awesome, I'm waiting for this and the infinite scrolling to use it on my app.
Thanks mate!

@SimonWaldherr
Copy link

hi, maybe you like SimonWaldherr/PullToRefresh or SimonWaldherr/infinity.js.

@ia3andy
Copy link

ia3andy commented Jun 9, 2013

Nativ ptr is not compatible with android since you can't bounce outside boundaries.
If you have solution to this problem, I would gladly switch to a nativ scroll..

@ia3andy
Copy link

ia3andy commented Jun 9, 2013

Sorry I mixed up with https://github.com/dantipa/pull-to-refresh-js

@SimonWaldherr
Copy link

i have no android device, but i will try it in the simulator. Maybe i can fix this problem.

@davidpfahler
Copy link
Collaborator

Related issue #510.

@zeligmanos
Copy link

Hello, any news on this great feature? Still using iScroll 4 because of that feature alone?

@orenagiv
Copy link

I didn't have time to prepare a standalone demo... But I hope the following will help.

I took the original iScroll-4 pull-to-refresh feature, and re-edited it to work with IScroll-5.
The following code is based on the iScroll-4 concept, and is intended for use with iscroll-probe.js.

Note:
The "isMobile" object that appears in the code below is an object that determines what mobile OS is used. It is required in order to workaround an issue with double-clicking

yourObject:{
    pullDownAction:function(container) {
        yourObject.load_content('items', container, 0, 'refresh');
        $('#'+container+'_wrapper .list').data('page', 1);

        // Since "topOffset" is not supported with iscroll-5
        $('#'+container+'_wrapper > .scroller').css({top:0});

    },
    pullUpAction:function(container, callback) {
        if ($('#'+container+'_wrapper .list').data('page')) {
            var next_page = parseInt($('#'+container+'_wrapper .list').data('page'), 10) + 1;
        } else {
            var next_page = 2;
        }
        yourObject.load_content('items', container, 0, 'refresh', next_page);
        $('#'+container+'_wrapper .list').data('page', next_page);

        setTimeout(function() {
            if (callback) {
                callback();
            }
        }, 800);
    },
    pullActionCallback:function(container) {
        // This function is triggered from load_content() since iscroll-5 does not yet support topOffset
        if (yourObject.pullDownEl && yourObject.pullDownEl.className.match('loading')) {
            yourObject.pullDownEl.className = 'pullDown';
            yourObject.pullDownEl.querySelector('.pullDownLabel').innerHTML = yourObject.strings.pull_down_to_refresh;

            yourObject.items_scroll.scrollTo(0, parseInt(yourObject.pullUpOffset)*(-1), 200);

        } else if (yourObject.pullUpEl && yourObject.pullUpEl.className.match('loading')) {
            yourObject.pullUpEl.className = 'pullUp';
            yourObject.pullUpEl.querySelector('.pullUpLabel').innerHTML = yourObject.strings.pull_up_to_load_more;
        }
    },

    trigger_items_scroll:function(container) {
        yourObject.pullDownEl = document.querySelector('#'+container+'_wrapper .pullDown');
        if (yourObject.pullDownEl) {
            yourObject.pullDownOffset = yourObject.pullDownEl.offsetHeight;
        } else {
            yourObject.pullDownOffset = 0;
        }
        yourObject.pullUpEl = document.querySelector('#'+container+'_wrapper .pullUp'); 
        if (yourObject.pullUpEl) {
            yourObject.pullUpOffset = yourObject.pullUpEl.offsetHeight;
        } else {
            yourObject.pullUpOffset = 0;
        }

        yourObject.items_scroll = new IScroll('#'+container+'_wrapper', {
            // [ ! ] Important Note: if click:true then on Android > 4.0 (but < 4.4) the onclick is fired twice. So we must use click:false, however that does NOT affect functionality when using the actual mobile device.
            probeType:2, tap:true, click:(isMobile.Android && parseFloat(navigator.userAgent.match(/Android [\d+\.]{3,5}/)[0].replace('Android ','')) < 4.4 ? false : true), mouseWheel:true, scrollbars:true, fadeScrollbars:true, interactiveScrollbars:false, keyBindings:true, 
            startY:(parseInt(yourObject.pullUpOffset)*(-1))
        });
        yourObject.items_scroll.on('scrollStart', function () {
            yourObject.scroll_in_progress = true;
        });
        yourObject.items_scroll.on('scroll', function () {

            yourObject.scroll_in_progress = true;

            if (this.y > 5 && yourObject.pullDownEl && !yourObject.pullDownEl.className.match('flip')) {
                yourObject.pullDownEl.className = 'pullDown flip';
                yourObject.pullDownEl.querySelector('.pullDownLabel').innerHTML = yourObject.strings.release_to_refresh;
                this.minScrollY = 0;
            } else if (this.y < 5 && yourObject.pullDownEl && yourObject.pullDownEl.className.match('flip')) {
                yourObject.pullDownEl.className = 'pullDown';
                yourObject.pullDownEl.querySelector('.pullDownLabel').innerHTML = yourObject.strings.pull_down_to_refresh;
                this.minScrollY = -yourObject.pullDownOffset;
            } else if (this.y < (this.maxScrollY - 5) && yourObject.pullUpEl && !yourObject.pullUpEl.className.match('flip')) {
                yourObject.pullUpEl.className = 'pullUp flip';
                yourObject.pullUpEl.querySelector('.pullUpLabel').innerHTML = yourObject.strings.release_to_load;
                yourObject.maxScrollY = this.maxScrollY;
            } else if (this.y > (this.maxScrollY + 5) && yourObject.pullUpEl && yourObject.pullUpEl.className.match('flip')) {
                yourObject.pullUpEl.className = 'pullUp';
                yourObject.pullUpEl.querySelector('.pullUpLabel').innerHTML = yourObject.strings.pull_up_to_load_more;
                this.maxScrollY = yourObject.pullUpOffset;
            }
        });
        yourObject.items_scroll.on('scrollEnd', function () {

            setTimeout(function() {
                yourObject.scroll_in_progress = false;
            }, 100);

            if (yourObject.pullDownEl && yourObject.pullDownEl.className.match('flip')) {
                yourObject.pullDownEl.className = 'pullDown loading';
                yourObject.pullDownEl.querySelector('.pullDownLabel').innerHTML = yourObject.strings.loading;
                yourObject.pullDownAction(container);
            } else if (yourObject.pullUpEl && yourObject.pullUpEl.className.match('flip')) {
                yourObject.pullUpEl.className = 'pullUp loading';
                yourObject.pullUpEl.querySelector('.pullUpLabel').innerHTML = yourObject.strings.loading;
                yourObject.pullUpAction(container);
            }
        });

        // In order to prevent seeing the "pull down to refresh" before the iScoll is trigger - the wrapper is located at left:-9999px and returned to left:0 after the iScoll is initiated
        setTimeout(function() {
            $('#'+container+'_wrapper').css({left:0});
        }, 100);
    }
}

Hope this helps...

@zeligmanos
Copy link

OK, I figured out how your solution works.

Can you please elaborate a bit more on this part:
this.load_content('items', container, 0, 'refresh');

Do I need to create an extra method named 'load_content'?

@orenagiv
Copy link

load_content is a method of mine that refreshes the list contents - injects some Ajax data into the scroller container - it either performs a "refresh" and replaces the existing scroll list, or loads a new page.

You should replace the load_content with your own function that does the same.

Let me know if that's understood :)

@zeligmanos
Copy link

thanks for the clarification - your resolution works like a charm - I'll try to create an example using your code and post it here

@orenagiv
Copy link

NP :)

@amritk
Copy link

amritk commented Mar 26, 2014

Is there any way to combine this pull to refresh with the infinate scroll?

@nalinwijayasinghe
Copy link

Can't we use pull to refresh yet?

@zhengyinhui100
Copy link

Is there a plan to support pull to refresh in iscroll 5,or i have to modify the code by myself?

@ms88privat
Copy link

something new? thank you very much

@orenagiv
Copy link

As far as I know - until the next version which might support it - you'll need to add it to the code yourself.
It's not such a biggie - please take a look at my example above - it's based on the SAME code of iScroll-4.

First - make sure you use iscroll-probe.

Then, basically you have 3 functions that you need to understand:
pullUpAction()
pullDownAction()
pullActionCallback()

And then:

  1. You need to bind the pullUpAction and pullDownAction to "on" scroll-end.
  2. You need to handle the icon of the "pull arrow" and spinning loader - and that's done inside the "scroll" event.

Hope that helps...

@ms88privat
Copy link

thank you very much, that looks like a very good explanation. I will try it
later :)

2014-06-20 22:05 GMT+02:00 Oren Agiv [email protected]:

As far as I know - until the next version which might support it - you'll
need to add it to the code yourself.
It's not such a biggie - please take a look at my example above - it's
based on the SAME code of iScroll-4.

First - make sure you use iscroll-probe.

Then, basically you have 3 functions that you need to understand:
pullUpAction()
pullDownAction()
pullActionCallback()

And then:

  1. You need to bind the pullUpAction and pullDownAction to "on" scroll-end.
  2. You need to handle the icon of the "pull arrow" and spinning loader -
    and that's done inside the "scroll" event.

Hope that helps...


Reply to this email directly or view it on GitHub
#378 (comment).

@aherrick
Copy link

Would anyone be willing to leave a JS Fiddle to show this code in action? Thank you!

@orenagiv
Copy link

I've prepared a demo - hope this helps:
http://pnc.co.il/dev/iscroll-5-pull-to-refresh-and-infinite-demo.html

The DEMO includes both Pull to Refresh and "infinite" scroll (but not using the iscroll-infinite.js).

@lsycxyj
Copy link

lsycxyj commented Aug 21, 2014

Is pull-to-refresh still not supported in iScroll 5 now?

@superbland
Copy link

This would still be an incredibly useful feature to have. I got the above example to work, but this breaks if there arent enough items to overflow the container.

@normanhh3
Copy link

Hi folks, I've got an iScroll 5 compatible jqm widget that has had some testing done by me. At this point it has been setup as a Meteor project (just 'cuz I want to learn more about it). At this point its pretty raw but I am planning on using it with a jQuery Mobile Apache Cordova app I maintain so it will see the light of day. ;-) http://github.com/normanhh3/jqm-iscroll

@frankie-loves-jesus
Copy link

@normanhh3 why do you need a jQM widget when iScroll 5 works just fine by itself?

@normanhh3
Copy link

It works by itself, I wanted a more native feel for jqm as well as handling of some of the peculiarities that make getting iscroll work in jqm easier.

Implemented all of the code for handling the pull-to-refresh as well as manipulating the layout to work well within jqm was a bit of a challenge. I just wanted to keep as clean of a separation as possible for my own re-use.

@frankie-loves-jesus
Copy link

Hi. Thanks for getting back at me. What peculiarities though? I haven't found any peculiarities yet at my latest fiddle over at watusi/jquery-mobile-iscrollview#118. And it's simple too, just add data-iscroll to the divs you want scrollable.

As for pull to refesh, surely #510 is a lot simpler? Both in terms of code and the parties involved.

@normanhh3
Copy link

@frankie-loves-jesus , I hadn't seen #510 until now. It is a good start at a basic solution, but lacks the finesse of the UI interaction provided by @orenagiv in his solution mentioned previously. I leaned heavily on his solution to craft my solution.

@invlol
Copy link

invlol commented Jan 22, 2015

I create a pull to refresh example but i have problems with scrollEnd and scrollTo, When i call scrollTo seems that scrollEnd fuck up and is calling a lot. I resolve a lot of consecutive animation problems with jquery .stop() and others that stop events on queue etc.. but here i dont know why scrollTo fuck me up the scrollEnd event.

Example: http://jsfiddle.net/kL28ct7b/

Hope someone can fix this, the code is simple i think.

@thorsent
Copy link

thorsent commented Feb 7, 2015

There's a simple way using iscroll probe:

var refreshing=false;
iscroll=new IScroll(..., {probeType:3});
iscroll.on("scroll", function(){
   if(this.y>5 && ! refreshing){
        refreshing=true;
        // load new data
   }
});
iscroll.on("scrollEnd", function(){
   refreshing=false;
});

@TobbeLino
Copy link

TobbeLino commented Feb 23, 2015

Hello!
I just implemented my own "pull-down-to-refresh" (and "pull-up-to-load") for iScroll 5.

It's based on the official demo for iScroll 4, and it should be quite polished and well-behaved (we plan to use it in production quite soon). It tries to handle all the the different cases without glitches (e.g. it works also when the content fits the screen and iScroll has disabled scrolling, the scrollbars are working in all cases, and I've tried to address all visual glitches, etc). It also supports multiple instances on the same page (just create one or more new IScrollPullUpDown() with different wrapper-IDs)

Here's a demo if anyone's interested:
http://www.gatekeepers.se/test/iScroll5/iscroll5-pull-test.html

@Chener
Copy link

Chener commented Mar 6, 2015

@TobbeLino Hi, I tried to use your implementation. With the demo, everything works perfect.
However, after I changed the hard-coded lists to ajax pulling data from a local data.json file, the page had two issues:

  • on page loaded, the page can not be pulled up or down until it was pulled once, i.e. the pulling won't work until the second pulling.
  • the page won't show .pullDown or .pullUp part, which are the indicators. After digging out, the .pullDown had display:none set, which must be the reason causing this problem. But .pullUp had not got any css set, still it was not visible when using ajax

I tried to set those two class in css with display:block !important; but this had some other side effects.

The following is my code, it will work perfectly if the ajax parts are replaced by the commented out parts.

        if (!refresh || (refresh && !next_page)) {
            // Loading the initial content and refreshing
            $('#wrapper > #scroller > ul').html('');    
            $.ajax({
                url: 'service/data.json',
                dataType: 'json',
            })
            .done(function(data) {
                var content = "";
                for (var i = 9; i >= 0; i--) {
                    content += ('<li>' + data.clientList[i] + 'aaaaaaaaaaaa</li>');
                }
                $('#wrapper > #scroller > ul').append(content);
            })
            .fail(function() {
                console.log("error");
            })
            .always(function() {
                console.log("complete");
            });

            // var content = "";
            // for (var i = 9; i >= 0; i--) {
            //  content += ('<li>' + i + 'aaaaaaaaaaaa</li>');
            // }
            // $('#wrapper > #scroller > ul').append(content);
        } else if (refresh && next_page) {
            // Loading the next-page content
            $.ajax({
                url: 'service/data.json',
                dataType: 'json',
            })
            .done(function(data) {
                var content = "";
                for (var i = 9; i >= 0; i--) {
                    content += ('<li>' + data.clientList[i] + 'aaaaaaaaaaaa</li>');
                }
                $('#wrapper > #scroller > ul').append(content);
            })
            .fail(function() {
                console.log("error");
            })
            .always(function() {
                console.log("complete");
            });

            // var content = "";
            // for (var i = 9; i >= 0; i--) {
            //  content += ('<li>' + i + 'aaaaaaaaaaaa</li>');
            // }
            // $('#wrapper > #scroller > ul').append(content);
            // $('#wrapper > #scroller > ul').append('<li>Pretty row initial content</li>');
        }

@TobbeLino
Copy link

@Chener: Not sure, but didn't you just forget to call theScroller.refresh() after modifying the DOM in the ajax callback? You need to do that to make iScroll aware of the changes.

@Chener
Copy link

Chener commented Mar 8, 2015

@TobbeLino I do have this under the above code:

        if (refresh) {
            myScroll.refresh();
            pullActionCallback();           
        } else {
            if (myScroll) {
                myScroll.destroy();
                $(myScroll.scroller).attr('style', ''); // Required since the styles applied by IScroll might conflict with transitions of parent layers.
                myScroll = null;
            }
            trigger_myScroll();     
        }

@TobbeLino
Copy link

@Chener: It's a bit hard to help without seeing all of your code. What happens in pullActionCallback() and why do you call refreh() before (and not after)? And I really think you need a refresh()-call in the end of the ajax-callback on done(), as this is where the DOM is changes. I'm not using jQuery myself so I'm not sure - are the DOM-manipulation always done synchronously/instantly with .append() in jQuery? If there is any delay (due to transitins, etc), maybe you'll have to call myScroll.refresh() with a small delay, e.g.:
setTimeout(function(){myScroll.refresh();},100);
(and of course, this has do be done after all DOM-changes in the ajax-callback)

@Chener
Copy link

Chener commented Mar 9, 2015

@TobbeLino Sorry, I did not actually dig too much in iScroll, just copied the code from your demo, thought it would be ok just to replace the hard-coded parts.

I will see into it, should be able to solve it under your pointing direction. Thank you!

@Chener
Copy link

Chener commented Mar 9, 2015

@TobbeLino Nailed it ! Thank you, and it seems that DOM-manipulation is done instantly.

@dongdh
Copy link

dongdh commented Mar 11, 2015

@TobbeLino
Hello, I tried ur implementation of code, I have a problem that the pull-up div, it seems act as a regular element of the list, I want it to act as pulldown div, can it be done?

great solution by the way, it really works.

@Zhuinden
Copy link

Zhuinden commented Apr 2, 2015

@TobbeLino amazingly done!

I extracted the code from the demo here (uses iscroll-probe.js):

// Functions to simulate "refresh" and "load" on pull-down/pull-up
/*var generatedCount = 0;*/
function pullDownAction(theScroller) {
    var el, li, i;  
    //TODO: do your things
    theScroller.refresh(); //just in case
}
function pullUpAction(theScroller) {
    var el, li, i;
    //TODO: doYourThings();
    theScroller.refresh(); //just in case
}


var IScrollPullUpDown = function(wrapperName, iScrollConfig, pullDownActionHandler, pullUpActionHandler) {
    var iScrollConfig, pullDownActionHandler, pullUpActionHandler, pullDownEl, pullDownOffset, pullUpEl, scrollStartPos;
    var pullThreshold = 5;
    var me = this;

    function showPullDownElNow(className) {
        // Shows pullDownEl with a given className
        pullDownEl.style.transitionDuration = '';
        pullDownEl.style.marginTop = '';
        pullDownEl.className = 'pullDown ' + className;
    }

    var hidePullDownEl = function(time, refresh) {
        // Hides pullDownEl
        pullDownEl.style.transitionDuration = (time > 0 ? time + 'ms' : '');
        pullDownEl.style.marginTop = '';
        pullDownEl.className = 'pullDown scrolledUp';

        // If refresh==true, refresh again after time+10 ms to update iScroll's "scroller.offsetHeight" after the pull-down-bar is really hidden...
        // Don't refresh when the user is still dragging, as this will cause the content to jump (i.e. don't refresh while dragging)
        if(refresh) setTimeout(function() {
            me.myScroll.refresh();
        }, time + 10);
    }

    function init() {
        var wrapperObj = document.querySelector('#' + wrapperName);
        var scrollerObj = wrapperObj.children[0];

        if(pullDownActionHandler) {
            // If a pullDownActionHandler-function is supplied, add a pull-down bar at the top and enable pull-down-to-refresh.
            // (if pullDownActionHandler==null this iScroll will have no pull-down-functionality)
            pullDownEl = document.createElement('div');
            pullDownEl.className = 'pullDown scrolledUp';
            pullDownEl.innerHTML = '<span class="pullDownIcon"></span><span class="pullDownLabel">Pull down to refresh...</span>';
            scrollerObj.insertBefore(pullDownEl, scrollerObj.firstChild);
            pullDownOffset = pullDownEl.offsetHeight;
        }
        if(pullUpActionHandler) {
            // If a pullUpActionHandler-function is supplied, add a pull-up bar in the bottom and enable pull-up-to-load.
            // (if pullUpActionHandler==null this iScroll will have no pull-up-functionality)
            pullUpEl = document.createElement('div');
            pullUpEl.className = 'pullUp';
            pullUpEl.innerHTML = '<span class="pullUpIcon"></span><span class="pullUpLabel">Pull up to load more...</span>';
            //scrollerObj.appendChild(pullUpEl);
        }

        me.myScroll = new IScroll(wrapperObj, iScrollConfig);

        me.myScroll.on('refresh', function() {
            if((pullDownEl) && (pullDownEl.className.match('loading'))) {
                pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
                if(this.y >= 0) {
                    // The pull-down-bar is fully visible:
                    // Hide it with a simple 250ms animation
                    hidePullDownEl(250, true);
                } else if(this.y > -pullDownOffset) {
                    // The pull-down-bar is PARTLY visible:
                    pullDownEl.style.marginTop = this.y + 'px';

                    // CSS-trick to force webkit to render/update any CSS-changes immediately: Access the offsetHeight property...
                    pullDownEl.offsetHeight;

                    var animTime = (250 * (pullDownOffset + this.y) / pullDownOffset);
                    this.scrollTo(0, 0, 0);   
                    setTimeout(function() { 
                        hidePullDownEl(animTime, true);
                    }, 0);

                } else {
                    hidePullDownEl(0, true);
                    this.scrollBy(0, pullDownOffset, 0);
                }
            }
            if((pullUpEl) && (pullUpEl.className.match('loading'))) {
                pullUpEl.className = 'pullUp';
                pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
            }
        });

        me.myScroll.on('scrollStart', function() {
            scrollStartPos = this.y; // Store the scroll starting point to be able to track movement in 'scroll' below
        });

        me.myScroll.on('scroll', function() {
            if(pullDownEl || pullUpEl) {
                if((scrollStartPos == 0) && (this.y == 0)) {
                    this.hasVerticalScroll = true;

                    // Set scrollStartPos to -1000 to be able to detect this state later...
                    scrollStartPos = -1000;
                } else if((scrollStartPos == -1000) &&
                    (((!pullUpEl) && (!pullDownEl.className.match('flip')) && (this.y < 0)) ||
                    ((!pullDownEl) && (!pullUpEl.className.match('flip')) && (this.y > 0)))) {
                    this.hasVerticalScroll = false;
                    scrollStartPos = 0;
                    this.scrollBy(0, -this.y, 0);   // Adjust scrolling position to undo this "invalid" movement
                }
            }

            if(pullDownEl) {
                if(this.y > pullDownOffset + pullThreshold && !pullDownEl.className.match('flip')) {
                    showPullDownElNow('flip');
                    this.scrollBy(0, -pullDownOffset, 0);   // Adjust scrolling position to match the change in pullDownEl's margin-top
                    pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Release to refresh...';
                } else if(this.y < 0 && pullDownEl.className.match('flip')) { // User changes his mind...
                    hidePullDownEl(0, false);
                    this.scrollBy(0, pullDownOffset, 0);    // Adjust scrolling position to match the change in pullDownEl's margin-top
                    pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Pull down to refresh...';
                }
            }
            if(pullUpEl) {
                if(this.y < (this.maxScrollY - pullThreshold) && !pullUpEl.className.match('flip')) {
                    pullUpEl.className = 'pullUp flip';
                    pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Release to load more...';
                } else if(this.y > (this.maxScrollY + pullThreshold) && pullUpEl.className.match('flip')) {
                    pullUpEl.className = 'pullUp';
                    pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Pull up to load more...';
                }
            }
        });

        me.myScroll.on('scrollEnd', function() {
            if((pullDownEl) && (pullDownEl.className.match('flip'))) {
                showPullDownElNow('loading');
                pullDownEl.querySelector('.pullDownLabel').innerHTML = 'Loading...';
                pullDownActionHandler(this);    // Execute custom function (ajax call?)
            }
            if((pullUpEl) && (pullUpEl.className.match('flip'))) {
                pullUpEl.className = 'pullUp loading';
                pullUpEl.querySelector('.pullUpLabel').innerHTML = 'Loading...';
                pullUpActionHandler(this);  // Execute custom function (ajax call?)
            }
            if(scrollStartPos = -1000) {
                this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
            }
        });

        me.myScroll.refresh();
    }

    window.addEventListener('load', function() {
        init()
    }, false);
};

And use like so

var scroller1 = new IScrollPullUpDown('yourWrapper', {
    probeType: 2,
    bounceTime: 250,
    bounceEasing: 'quadratic',
    mouseWheel: false,
    scrollbars: true,
    fadeScrollbars: true,
    interactiveScrollbars: false,
    click: true,
    tap: true
}, pullDownAction, pullUpAction);

Although it's better if you use tap instead of click, because click is triggered twice. Also, make sure to put some delay on refresh.

setTimeout(function() {
    scroller1.myScroll.refresh();
}, 250);

Also, you must block scrolling on the page.

function blockTouchMove(e) {
    e.preventDefault();
}

document.addEventListener('touchmove', blockTouchMove, false);

As I said, all credit to TobbeLino.

@phoeson
Copy link

phoeson commented Jul 9, 2015

Any perfect solution?

@Zhuinden
Copy link

Zhuinden commented Jul 9, 2015

The one that TobbeLino created works without a problem if you get it to work.

@zhn4
Copy link

zhn4 commented Sep 24, 2015

How about this?

if($('#scroller').offset().top > 100) {
  setTimeout('window.location.reload(true)', 1500);
}

@erkanogum
Copy link

Hey guys, I am a UI designer and now trying to implement the iscroll 5 pull to refresh in my hybrid mobile project. However, as I don't know javascript, I just really need a final packet of pull to refresh iscroll 5. Can anybody put a github link or anything that works well finally so I can directly download to implement it on my project. Thanks a lot in advance.

@kaansoral
Copy link

args={
        onScrollMove: function () {
            if (this.y > 100 && !up_flip) {
                up_flip=1;
            } else if (this.y < 100 && up_flip) {
                up_flip=0;
            }
        },
        onScrollEnd: function () {
            if (up_flip) {
                up_flip=0;
                ui_success("Refreshing",600);
                get_stuff(1);

            }
        },
        "momentum":true
    };
    window.contentScroll = new iScroll('scroller',args);

Copy pasted without editing, you may need to initialize some variables

@Rmannn
Copy link

Rmannn commented Jan 19, 2016

If someone is interested, i just wrote a little react component to work with iscroll and pull refresh.
It's just a draft

var React = require('react');
var IScroll = require('iscroll/build/iscroll-probe');
var classnames = require('classnames');
var PullToRefresh = React.createClass({
    getDefaultProps: function() {
        return {
            refreshText: 'Pull to refresh',
            releaseText: 'Release to refresh',
            loadingText: 'Loading',
            refreshIconClass: 'fa fa-arrow-down',
            releaseIconClass: 'fa fa-arrow-down fa-rotate-180',
            loadingIconClass: 'fa fa-refresh fa-spin',
            pullToRefreshThreshold: 100,
            refreshThreshold: 50
        };
    },
    getInitialState: function() {
        return {
            loading: false,
            pulling: false,
            waitingReleaseToRefresh: false
        };
    },
    getScrollWrap: function() {
        return this.refs.scroller;
    },
    componentDidMount: function() {
        this.iscroll = new IScroll(this.getScrollWrap(), {
            mouseWheel: true,
            probeType: 3,
            scrollbars: false
        });
        var me = this;
        this.iscroll.on('scroll', function() {
            me.scrollHandler(this)
        });
        this.getScrollWrap().addEventListener("touchend", this.handlePullRefresh, false);
    },
    componentDidUpdate: function() {
        if (!this.state.pulling) {
            this.iscroll.refresh();
        }
    },
    handlePullRefresh: function() {
        if (this.state.waitingReleaseToRefresh) {
            this.showLoader();
            this.props.onRefresh(this.hideLoader);
        }
    },
    showLoader: function() {
        this.setState({
            waitingReleaseToRefresh: false,
            loading: true
        });
    },
    hideLoader: function() {
        this.setState({
            loading: false
        });
    },
    scrollHandler: function(scroll) {
        var state = this.state;

        //normal scroll
        if (scroll.y <= 1) {
            state.pulling = false;
        }

        //we are pulling
        if (scroll.y > 1) {
            state.pulling = true;
            if (this.props.pullToRefreshThreshold < scroll.y) {
                console.log('REFRESH');
                state.waitingReleaseToRefresh = true;
            } else {
                state.waitingReleaseToRefresh = false;
            }
        }
        this.setState(state);
    },

    renderLoading: function() {
        return (
            <div className="loading">
                <p>{this.props.loadingText}</p>
                <div className="icon"><i className={this.props.loadingIconClass}></i></div>
            </div>
        );
    },
    renderRefresh: function() {

        //prevent the block to be displayed if we are nit pulling
        if (!this.state.pulling) {
            return null;
        }

        var text = this.state.waitingReleaseToRefresh ? this.props.releaseText : this.props.refreshText;
        var iconClass = this.state.waitingReleaseToRefresh ? this.props.releaseIconClass : this.props.refreshIconClass;

        return (
            <div className="refresh">
                <p>{text}</p>
                <div className="icon"><i className={iconClass}></i></div>
            </div>
        );
    },
    render: function() {

        var classNames = classnames({
            'pull-to-refresh': true,
            'loading': this.state.loading,
            'pulling': this.state.pulling,
            'waiting-pull-to-refresh': this.state.waitingPullToRefresh
        });

        var scrollerStyle = {
            position: 'absolute',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            transition: 'all .2s ease-out',
            transform: this.state.loading ? 'translate3d(0,' + this.props.refreshThreshold + 'px,0)' : 'translate3d(0,0,0)'
        };

        var refreshInfos = this.state.loading ? this.renderLoading() : this.renderRefresh();

        return (
            <div className={classNames}>
                {refreshInfos}
                <div ref="scroller" style={scrollerStyle}>
                    <div className="scroller" ref="scroller" style={scrollerStyle}>
                        {this.props.children}
                    </div>
                </div>
            </div>
        );
    }
});
module.exports = PullToRefresh;

Usage:

var React = require('react');
var PullToRefresh = require('../pulltorefresh/PullToRefresh');
var List = React.createClass({
    onRefresh: function(done){
        //fetch data here.
        setTimeout(function(){
            done();
        }, 2000);
    },
    render: function() {
        return (
            <div>
                <PullToRefresh onRefresh={this.onRefresh}>
                    {this.props.children}
                </PullToRefresh>
            </div>
        );
    }
});
module.exports = List;

@dt1973
Copy link

dt1973 commented Jan 19, 2016

Could somebody put up a real live testcase at jsbin.com?

@dt1973
Copy link

dt1973 commented Jan 19, 2016

Or will this be a feature of iScroll 6 @cubiq? Thank you.

@glebmachine
Copy link
Collaborator

@dt1973, yes, it will)

@allanwolski
Copy link

Any predictions for release iScroll 6 or to implement this feature?

I'm using the solution of @TobbeLino, but scrollBy is causing a small delay on Android devices.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests