alvarotrigo / fullPage.js

fullPage plugin by Alvaro Trigo. Create full screen pages fast and simple
http://alvarotrigo.com/fullPage/
GNU General Public License v3.0
35.28k stars 7.29k forks source link

Avoid accidental scrolling when reading content in the bottom of the slide when using scrollOverflow:true #941

Closed elahmo closed 2 years ago

elahmo commented 9 years ago

I am having one interesting behavior where some of the slides have content which is positioned at or near the bottom of the pages. So, on smaller screens where the content can't be shown all at once, when the user scrolls down to see the content, he or she might overscroll (via mouse or touch pad) and instead of just scrolling to see the content trigger the new page slide in as well, so the user can't read the content and has to go back.

One solution might be too change the content structure and add some padding or empty space below the actual content, but that seems like crude solution.

Is there a way to solve this so in the initial scroll, when the user is actually scrolling the page which has height larger than the screen/window height, doesn't trigger the scroll event?

Thanks

bogdan-r commented 7 years ago

@lanbomo it works great with MacBook trackpad. Thanks. I grab your solution.

lanbomo commented 7 years ago

@bogdan-r I'm glad to sound that. I will consider to put a Rull Request, hope it can help others more. Thanks for your test.

alvarotrigo commented 7 years ago

@lanbomo thanks for it! Seems it works as expected in some cases! I forgot to comment on this back on December, but as far as I could test on the day, it was only working properly on the 1st section? Or if you didn't scroll consecutively down section after section? I'll check it again whenever I have a Mac and provide you some feedback.

lanbomo commented 7 years ago

@alvarotrigo Thanks for your works. When scroll bar reached the end and scroll consecutively it will work. I don't know how about Mac's trackpad, but I think the option delay could help developer to adjust to an expected effect.

Finally, thanks for your works again.

Pixelous commented 7 years ago

Hi, @alvarotrigo will you merge @lanbomo solution? Did you test it?

alvarotrigo commented 7 years ago

@Pixelous I didn't test it yet.

LukeTongue commented 7 years ago

This works really well. Would be great if it can be merged into the master as i would like to use this as well as the parallax extension.

jk171505 commented 7 years ago

@alvarotrigo any chances to merge it into the master?

alvarotrigo commented 7 years ago

@alvarotrigo any chances to merge it into the master?

Maybe.

eliteproxy7 commented 7 years ago

how can i use this with fullpage.extensions.min.js ? i purchased 3/4 extensions and the fullpage.extensions.min.js file is obfuscated and i can't figure out this feature.. otherwise the overflow function becomes useless with the jump.. we did some usability testing and every single user complained that it jumped and they missed the content

alvarotrigo commented 7 years ago

how can i use this with fullpage.extensions.min.js ? i purchased 3/4 extensions and the fullpage.extensions.min.js file is obfuscated and i can't figure out this feature

@eliteproxy7 you are mixing things here. This issue has nothing to do with the extensions. You have the same issue with the jquery.fullpage.js file that you can examine as long as you want :)

jayelm commented 7 years ago

I minified @lanbomo's modifications and generated a production RawGit link for the minified version in this repo. Until this gets merged (which I hope it does), the quickest way to implement his fix is to swap out whatever version of jquery.fullpage.min.js you're loading with the one available here (the other files can stay the same):

https://cdn.rawgit.com/jayelm/fullPage.js/dev/dist/jquery.fullpage.min.js

Then add the following option to your fullpage call to use the default scrollOverflowEndPrevent settings, which work well:

$('#foo').fullpage({
    scrollOverflow: true,
    scrollOverflowEndPrevent: { delay: 500, reversal: false }
});

(@lanbomo, feel free to do the same for your repository, and I can update the links here.)

lanbomo commented 6 years ago

@jayelm Thanks for your work.

alieslamifard commented 6 years ago

Hi, this is my solution,

  1. first of all, put a div with class: section-end in every section with this style: .section{position:relative} .section-end{position:absolute;bottom:0;left:0;right:0;width:100%;opacity:0;height:1px}

  2. then, use this callback:

    $('#fullpage').fullpage({
    onLeave: function(index, nextIndex, direction){
            var leavingSection = $(this);
            if (direction =='down') {
                if(index == 2 || index == 3 || index == 4){
                    var w = $(window).height(),
                        end = leavingSection.find('.section-end').offset().top;
                        console.log(w, ' ', end);
                    if ( end > w  ) {
                        return false;
                    }
                }
            }
        }
    });

this is just a trick, NOT an absolute solution :)

monfortm commented 6 years ago

@jayelm @lanbomo thanks guys, I could test the solution quite fast thank to the CDN, it's a great work from @jayelm and work as expected.

For me a value of 100ms is just enough to prevent brute πŸ’ͺ users to accidentally go down 1 section when they were just checking for more content. And it does not prevent normal behavior on section without scrollOverflow.

@alvarotrigo Thanks for your work, great lib. I would really like to see @jayelm 's solution merged into the master branch πŸ˜„

I personally tested it on mac: firefox 58.0.2, chrome 64.0.3282.140 and safari 11.0.3 (13604.5.6) πŸ€–

cheers, m

jayelm commented 6 years ago

@monfortm not my solution, all @lanbomo's, I just packaged it up!

monfortm commented 6 years ago

@jayelm I understood that, but with your package and CDN I could test the solution in 3sec πŸ‘ , that is why I was thanking both of you, cheers.

alvarotrigo commented 6 years ago

I would need to review this with more calm, it is a big amount of the code the one added for such a small enhancement.

Original code:

        isScrolled: function(type, scrollable) {
            var scroller = scrollable.data('iscrollInstance');

            //no scroller?
            if (!scroller) {
                return true;
            }

            if (type === 'top') {
                return scroller.y >= 0 && !scrollable.scrollTop();
            } else if (type === 'bottom') {
                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight;
            }

Suggested code:

     isScrolled: function(type, scrollable) {
            var scroller = scrollable.data('iscrollInstance');

            //no scroller?
            if (!scroller) {
                return true;
            }

            var endPreventLastY = scrollable.data('iscrollInstanceEndPreventLastY');
            var endPreventDirection = scrollable.data('iscrollInstanceEndPreventDiriction');
            var endPreventScrollTop = scrollable.data('iscrollInstanceEndPreventScrollTop');
            var endPreventTopTime = scrollable.data('iscrollInstanceEndPreventTopTime');
            var endPreventBottomTime = scrollable.data('iscrollInstanceEndPreventBottomTime');

            // reversal action work with the scroll-end-prevent function?
            if(!iscrollEndPreventOptions.reversal && 
                ((endPreventLastY >= 0 && !endPreventScrollTop && type === 'bottom')
                || ((0 - endPreventLastY) + endPreventScrollTop + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight && type === 'top'))
            ){
                if(endPreventDirection !== scroller.directionY){
                    scrollable.data('iscrollInstanceEndPreventLastY', scroller.y);
                    scrollable.data('iscrollInstanceEndPreventDiriction', scroller.directionY);
                    scrollable.data('iscrollInstanceEndPreventScrollTop', scrollable.scrollTop());
                }
            }

            if (type === 'top') {
                scrollable.data('iscrollInstanceEndPreventBottomTime', 0);
                if(scroller.y >= 0 && !scrollable.scrollTop()){
                    if(endPreventTopTime === 0){
                        var time = new Date().getTime();
                        scrollable.data('iscrollInstanceEndPreventTopTime', time);
                        endPreventTopTime = time;
                    }
                    if(endPreventLastY >= 0 || new Date().getTime() - endPreventTopTime >= iscrollEndPreventOptions.delay){
                        scrollable.data('iscrollInstanceEndPreventLastY', scroller.y);
                        return true;
                    }
                }
                return false;
            } else if (type === 'bottom') {
                scrollable.data('iscrollInstanceEndPreventTopTime', 0);
                if((0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight){
                    if(endPreventBottomTime === 0){
                        var time = new Date().getTime();
                        scrollable.data('iscrollInstanceEndPreventBottomTime', time);
                        endPreventBottomTime = time;
                    }
                    if((0 - endPreventLastY) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight || new Date().getTime() - endPreventBottomTime >= iscrollEndPreventOptions.delay){
                        scrollable.data('iscrollInstanceEndPreventLastY', scroller.y);
                        return true;
                    }
                }
                return false;
            }
        },
alvarotrigo commented 6 years ago

@monfortm just notice you will be using version 2.9.2 instead of the current 2.9.6 (tends of bugs solved since then).

I would encourage you to modify the scrolloverflow.js isScrolled function instead of using the forked fullpage.js file, which is far from being up to date and contains bugs.

monfortm commented 6 years ago

@alvarotrigo Thanks for the input, no worries, I am actually not using this forked version from @lanbomo, I just used it for testing the feature.

The website I have is in prod so I am using the official fullpage.js. I have a fullPage.js @ fc34fb06 as git submodule in my repo (should be 2.9.5, no noticeable bugs for me).

my post was more to convey the fact that I tried the feature and I like it 😸

alvarotrigo commented 6 years ago

@monfortm you can always check the list of solved bugs in the releases description. You'll notice 16 bugs were fixed since 2.9.5.

IharSivakLeverx commented 6 years ago

Hello @alvarotrigo you forgot about iscrollEndPreventOptions in function

alvarotrigo commented 6 years ago

@IharSivakLeverx thanks for pointing it out!

drew-haas commented 6 years ago

@jayelm this looks like a great solution but I can't get it to work on my project. I get the error Uncaught ReferenceError: IScroll is not defined. It looks like this was fixed in a more recent version than the one being used (2.9.2) (https://github.com/alvarotrigo/fullPage.js/issues/2250).

I was wondering if there is a way to update your code with the latest version of fullpage (2.9.6).

Thanks again!

cc: @lanbomo

minlare commented 6 years ago

@alvarotrigo Is there any decision on whether this change will be merged into main branch?

alvarotrigo commented 6 years ago

@minlare not yet. I'm working on other things at the moment and didn't have time to check this with calm.

Paperweb commented 6 years ago

I ran across the same issue, as probably did many people using scrollOverflow, if not all of them since it is such an important UX matter.

So I decided to try @Canubiz 's solution after attempting in vain to modify scrollOverflow.js and jquery.fullPage.js with @lanbomo's + @alvarotrigo's excerpt and suggestion in a way that would'nt be cumbersome to update at every new fullPage release.

For those interested, I adapted Canubiz's solution to jquery.fullPage.j 2.9.7 :

From line 1055

  // Added lines to prevent early section scrolling : variables
  var lastScrollTime = 0;                                           /* <-- added */
  var scrollTimeDelay = 250;                                        /* <-- added */

  function scrolling(type){
     if (!isScrollAllowed.m[type]){
           return;
     }

  var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;

  if(options.scrollOverflow){
     var scrollable = options.scrollOverflowHandler.scrollable($(SECTION_ACTIVE_SEL));
     var check = (type === 'down') ? 'bottom' : 'top';

     // Added lines to prevent early section scrolling : get the scroll interval
     var currentScrollTime = new Date().getTime();                  /* <-- added */
     var lastScrollTimeDiff = currentScrollTime - lastScrollTime;   /* <-- added */
     lastScrollTime = currentScrollTime;                            /* <-- added */

     if(scrollable.length > 0 ){
        //is the scrollbar at the start/end of the scroll?
        if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
           if (lastScrollTimeDiff>scrollTimeDelay) {                /* <-- added */
              scrollSection();
           }                                                        /* <-- added */
        }else{
           return true;
        }
     }else{
        // moved up/down
        if (lastScrollTimeDiff>scrollTimeDelay) {                   /* <-- added */
           scrollSection();
        }                                                           /* <-- added */
     }
     }else{
        // moved up/down
        // Add IF here too when delay is needed and scrollOverflow : false
        scrollSection(); 
     }
  }

It's working pretty well for me. Maybe @reddo's commented that it didn't work because he replaced the whole snippet with Canubiz's?

As AlvarΓ² says, I guess further testing is still necessary to make sure it doesn't alter the experience on momentum driven devices.

But for now, it is a workaround that is easy to update and works as intended for my needs. Thanks @Canubiz and everyone else involved here! I'll be following that issue.

jscti commented 6 years ago

That's perfect @Paperweb Thanks.

Hope a official fix get released some day ;)

goowikns commented 6 years ago

This thread is really confusing.

Paperweb talks about a solution being adapted to jquery.fullPage.j 2.9.7... alvarotrigo closes the thread, which means in my opinion that the fix would be pushed, but when I check the file (https://cdnjs.cloudflare.com/ajax/libs/fullPage.js/2.9.7/jquery.fullpage.js), I can't seem to find any of it.

And this has been going on since 2015?

Could there, please, be a clear answer on when this will be released on a 2.9.x

alvarotrigo commented 6 years ago

Could there, please, be a clear answer on when this will be released on a 2.9.x

Nothing else will be released for version 2.X. Only version 3 is maintained.

alvarotrigo closes the thread,

Nop, the issue stills open :)

SavageSage commented 4 years ago

no luck on this since 2015?

alvarotrigo commented 4 years ago

no luck on this since 2015?

Apparently nobody was able to come with a solution as you can read in the comments πŸ€” So nope. No solution for it yet.

Perhaps you can give a try? πŸ˜‰

JulianBroudy commented 4 years ago

Just throwing ideas here, would it be possible to disable scrolling entirely once a section with the scrollOverflow is activated and maybe create some kind of listener to the added scroll so once it hits the end, then enable the scrolling again? If it would result in the same behavior then maybe set a timeout before enabling it?

Regarding this:

The problem comes when dealing with Apple trackpads / trackpads in general and Apple Magic mouse. There scroll behavior keeps firing even after 1 second depending on the acceleration of the scrolling.

It seems like a special case where this wouldn't work. I personally think a solution that helps all other cases is still good. Unless there is a way to detect these special cases and then disable the solution so it works as it did before the fix?

alvarotrigo commented 3 years ago

@JulianBroudy most people complaining about this are using kinetic scrolling devices, rather on Apple devices or laptops trackpads.

Fixing it for normal devices can also become annoying for visitors. If they want to keep on scrolling down and we prevent them to do so based on a timeout and they keep on trying to scroll, it won't slide down. They'll have to stop scrolling for some time before scrolling down.

rstanuwijaya commented 3 years ago

Hi, I'm currently working on merging the @lanbomo solution with v3.x.

For people who would like to manually implement the @lanbomo solution, you can just simply use my modified version of scrolloverflow.js here

@alvarotrigo I think it is way better to apply a delay, as in the current version I cannot see the top and bottom of the page without accidentally scrolling it.

alvarotrigo commented 3 years ago

@rstanuwijaya I'm working on fullPage.js version 4, which will NOT include iScroll.js.

@alvarotrigo I think it is way better to apply a delay, as in the current version I cannot see the top and bottom of the page without accidentally scrolling it.

Using a mouse wheel? A trackpad? Or a touch device?

rstanuwijaya commented 3 years ago

I'm having this problem especially when I'm using my mouse wheel. It always jumps to the next section without being able to reach the most bottom part of the page.

alvarotrigo commented 3 years ago

Good to know.

oussamatb commented 3 years ago

So Guys apparently when we add speed to the scroll it helps avoiding overscroling the page
scrollingSpeed: 1000, I hope this helps !

alvarotrigo commented 3 years ago

So Guys apparently when we add speed to the scroll it helps avoiding overscroling the page

Yeap.

alvarotrigo commented 2 years ago

Version 4 has gotten rid of the vendor file scrolloverflow.min.js.

And this should now be fixed! πŸ₯³

alvarotrigo commented 2 years ago

It feels good closing an issue that is 7 years old! :)