Prevent scroll bounce for the body element, but keep it for child elements in iOS [duplicate]
Asked Answered
H

4

48

I've been working on a mobile webapp as of late. I'm optimizing mobile-first, focusing specifically on iOS for the iPhone right now. I don't want the precise look of a native app, but I think that the feeling of being native is of absolute importance. I've made the markup and CSS to reflect this idea, leaving me with this (annotated to better understand the problem I'm having later on):

Webapp markup

This all works problem-free, and it also has the advantage of having a native feel, with static header and footer, and a scrollable inner view (thanks to -webkit-overflow-scrolling: touch).

As anyone who has used iOS for more than 5 minutes will know, when you scroll up or down, you get some nice momentum scrolling. Also, when you hit the top of a list, you get a nice 'bounce' effect:

List bouncing in Settings.app

I feel that this helps to define the feeling of iOS, and such a small detail can go a long way. Luckily, when you are below the top of a list in a scrollable element in a webapp, and scroll past the top, you get the same effect. This is the desired behavior in action:

Scrolling down and up in a webapp produces this bouncing effect

However, when you are at the top of the list, and try to recreate the same bouncing behavior a the top of the list (à la Setting.app, seen above), we get the following behavior, which is not desired: Scrolling down produces the bouncing effect for the entire chrome of the app

I've seen some similar questions on Stack Overflow, but these all opt for disabling bouncing. I'm wondering if it's at all possible to keep bouncing, but always make it occur on body section section#main, not on webapp's chrome. I'm not using jQuery, so I'd prefer answers to be in straight-up JavaScript (bonus points for a CSS solution, though).

Here's a GitHub repo with all of the code (Sinatra, HAML, and Sass; current branch is so), or a JSFiddle with broken images and links, but shows the issue in question on an iPhone (best to add to homescreen to test).

Hyetography answered 30/9, 2012 at 17:54 Comment(4)
A CSS solution would be ideal. I really want a good answer to this question, so I'm going to add a bounty to it.Varia
Did you have a look at jQtouch already? I use an older version of the Datazombies for almost 2 years now, and it looks exactly like what you are asking for. If that is kind of overkill, you can take a look at iScrollMeantime
@barts I just checked out jQtouch, and it doesn't seem to rectify the issue at hand. It might just be me, but I tried the demo on my iPhone, and it seemed to have the same issue. Also, I've seen iScroll before, and it's always felt 'off' to me. The momentum logic is always a liiiitle bit off. Even if it were 1:1 with iOS' logic, it wouldn't feel right on other platforms.Hyetography
I have posted a CSS solution for this here: #15732966 The key was to wrap the content in three -webkit-overflow-scrolling: touch divs. I have tested the solution in iOS7 GM too.Democratize
E
22

OLD INFO: I've solved this: http://www.hakoniemi.net/labs/scrollingOffset/nonbounce.html

NEW INFO: This is now a jQuery plugin that can be found from here: http://www.hakoniemi.net/labs/nonbounce/.

There are several issues, like losing the zooming capability when this is applied or it's dynamic updating isn't working fluently.

But now the simplest way is to define: <div class="nonbounce">...</div> and then $.nonbounce();

Erine answered 8/1, 2013 at 10:46 Comment(12)
If I'm not mistaken, your small library opts for disabling scrolling when going past an element's 'boundaries'. Sorry if it wasn't clear enough, but I was asking for a means of allowing the rubber-banding on an element, but not on the body.Hyetography
This does seem to work. Can you go into a little more detail about how you accomplished this?Varia
Correct me if I'm not understanding your approach correctly. It looks like you're disabling touchstart events on whatever element is passed into the nobounce function. Why are you also disabling touchmove events when the content is at the top and bottom?Varia
Did you ever post any more information on the topic? Of the solutions I've seen, this seems to be the best.Hyetography
@zvona Great. Though one issue remains: If the scrollable content is on top, and we try to scroll up again to make the "scrollable content" bounce (not the body) nothing happens. But this might be a os thing. Though, I think it could be enabled by simply telling the list to set itself at 1px when scrolling to the top (instead of 0px). Then the list could still be bouncy, right? Maybe room for an update ? =DWooten
Would it also be possible to make this work by class, not only id?Wooten
I'll keep that in mind and will refactor + update this when I have time.Erine
The answer for "touchmove" / "disabling the scrollable content bounce" is that the document/window bounces if one tries to scroll up when on top of the content or when on bottom tries to scroll down.Erine
This looks like a good idea, but when I implement it it just disable scrolling everywhere. I don't know if I'm doing something wrong.Engler
@zvona Hi, I'm looking at using this plugin, does exactly what I need it to. But it seems to disable the bouncing on the div I want scroll able (so its just a plain scroll). On the demo on the site it seems to bounce. Any ideas?Barathea
Here's the link to the actual js file: gist.github.com/justVitalius/7143040#file-nonbounce-jsComedietta
Maybe I did something wrong, but this plugin forced me to use two fingers to scroll and prevent single finger scrolling..Thromboplastin
I
5

I ran into the same problem and came up with this solution:

http://demo.josefkjaergaard.com/stackoverflow/element_scroll/index.html

Basically I prevent the scroll-content from being in its maximum positions.This prevents the body-scroll from being activated.

It works but it does create a little snap at the end of the easing. With a little more work this behavior could probably be hidden by offsetting the content in opposite direction.

Interbreed answered 18/4, 2013 at 17:35 Comment(3)
This got me as the closest so far. Any updates on that 1 2px "snap" that happens?Nystrom
I love this kind of logic. Rather than preventing the result of a condition (e.g., page bounce when a div is scrolled to the top), you prevent the condition. Fantastic. I have tried a few different "solutions". This is the best I have come across.Cinthiacintron
Why: document.getElementById("content").scrollHeight - $("#content").height(); vs document.getElementById("content").scrollHeight - document.getElementById("content").height(); or var content = document.getElementById("content") -> and then using the variable?Alikee
B
1

Here are some useful resources I found on the subject:

Baughman answered 11/7, 2013 at 17:13 Comment(1)
Please don't just link to a bunch of other websites. An explanation of what each one is and why it is relevant would be very helpful. Otherwise, you just lead users down a link clicking rabbit hole.Quesnay
D
0

How about this pseudo-code:

document.body.addEventListener("ontouchstart", function(event) {
  if(document.getElementById("main").scrollTop > 0) return;
  event.preventDefault();
  event.stopPropagation();
}, false);
Darice answered 10/1, 2013 at 8:10 Comment(1)
From what I can tell, this code simply opts for disabling scrolling beyond the top. Sorry if I didn't make it clear enough, but this was the behavior I didn't want. Instead, I was hoping to keep the rubber-banding on the child element (#main), but disable it on the body.Hyetography

© 2022 - 2024 — McMap. All rights reserved.