Prevent overflow / rubberband scrolling on iOS - html

There are already multiple questions on the topic of overflow/rubberband scrolling on SO but
none of them provides a solution working for all cases on iOS 9.3.2
none of them gives extensive and complete information on the problem itself
which is why I created this post as a body of knowledge.
The problem:
The thing that was never mentioned in any other post is that iOS overflow scrolling is actually a two part behaviour.
1. Overflow scrolling of content with overflow: auto/scroll
This is the commonly known and mostly wanted behaviour of a element with -webkit-overflow-scrolling: touch where the continuous/momentum scrolling behaviour goes past the elements container to slow the scrolled content down smoothly.
It happens when you scroll the content of an element with a momentum high enough for the momentum scrolling to go past the length of the scrolled content.
With this behaviour the element.scrollTop property changing accordingly to the elements scroll position and being less than 0 or bigger than the maximum scroll (element.scrollHeight - element.offsetHeight).
2. Overflow scrolling of <body>
This behaviour occurs if you try to scroll any element already at its minimum/maximum scroll position even further than that (an element at top upwards or element at bottom downwards). Then the scroll seems to "bubble up" up to the <body> tag and the whole viewport is scrolled.
In contrary to above here the element.scrollTop property does not change but document.body.scrollTop changes instead.
Focus lock and switching between behaviours (1.5s delay)
The most irritating thing in this context is that the switch between the two types described above does not switch instantly.
After you enter one of both you cannot switch focus onto any other element (scrollable elements, buttons, links, ...) and thereby the scrolling behaviour does not change as well.
For instance: if you scroll a element already at its top position upwards you enter overflow scrolling type 2 and the most natural reaction for a user is to then try to scroll back down. Because the focus is locked to the body scroll instead of going to overflow scrolling type 1 it stays in type 2 and the whole body is scrolled downwards. The typical user then starts to arbitrarily starts to scroll up and down frequently without ever breaking out of type 2.
The switch of focus and thus the change of scrolling behaviour can only occur after the overflow animation has finished and the element stands still (even a bit longer [around 0.5s] than that).
thus going back to the previous example the correct reaction of the user would be to stop touching the screen for around 1s - 1.5s and then try to scroll downwards again.

The solution:
Type 1:
The most basic solution to prevent overflow scrolling on the element itself is to prevent default on touch events.
document.body.addEventListener('touchmove', function(e) {
e.preventDefault();
});
This method however disables the browsers native momentum scroll and is thereby not suitable for most applications. With some refinement however (only prevent if at top scrolling up or at bottom scrolling down, ...) this method fixes most problems. Many possible implementations can be found in this SO post.
Type 2:
Overflow scrolling on the body however is not prevented by methods described above.
One possible solution which seems reasonable is to prevent the element from ever being at its top or bottom position as described as best solution on mentioned question.
anElement.addEventListener('touchstart', function( event ){
if( this.scrollTop === 0 ) {
this.scrollTop += 1;
} else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
this.scrollTop -= 1;
}
}
This however did not reliably work on iOS 9.3.2.
What did work however is setting position: fixed on the <body> element to prevent the body from moving. Please note however that this still does not completely stop type 2 from happening, which is why sometimes you cannot scroll/focus any element because in the background type2 with its focus lock is still happening (again, after you stop touching the screen for a moment it again works as expected).
While this is still far from being the optimal solution it seems to be the best we can get for the time speaking.
Edit: Please note that I am not sure if it is safe to put position: fixed on a <body> element. To track possible issues I have created following SO post. Apparently it might be better to create a wrapper element as child of body and set that element to position: fixed to avoid zoom problemes.
Edit 2: The definite solution
The script iNoBounce works wonders. Just load it to the page and experience a bounce-free web application. So far I have not found any problems with this solution.

I see this issue is still relevant so...
Be aware of using position: fixed on body. It may do weird scroll freeze bug - actually it will still "rubberband" but you will not see it.
see: Div scrolling freezes sometimes if I use -webkit-overflow-scrolling

A nice, simple and native solution. No Javascript needed.
Just add overflow scroll to your children. Gives your app a nice and native 'touch'
body{
position: fixed;
}

Related

Textarea does not respect its position absolute if it's in wrapper with overflow hidden

I am trying to solve a case where <textarea /> positioned absolute with some top value does not follow it's position if it's inside of overflow hidden parent.
This tiny code example explains it perfectly:
https://jsbin.com/xitayayiza/edit?html,css,output
And here is the video how this code example works:
https://monosnap.com/file/TkEHFXaAXslh3XkakCjkzfvVqLLB0q
On the video you can see when <textarea /> grows up, covering the space above (althogh it has position: absolute; top: 10px;
The question is: Is there a way set via CSS the textarea in the way that it will always keep its top value?
As you can see on the video the top space (above the textarea) is equal to the value top: 10px; but while editing textarea and adding more content into it the space decrease, while I would like to keep it permanently the same.
If you are expecting any decent browser to allow the caret of a <textarea> to be placed into an "out-of-view" or "out-of-reach" area, you are in the wrong. Simply put, it will never happen. With only one exception: when the <textarea> has scrollbars and the user scrolls it so the caret goes out of view. However, any change to the caret position will immediately scroll it back into view.
Why? Because that is considered to be a critical state, as it makes not only the page, but also the browser seem (and, in fact, be) unusable. Which might not be important to you, but is extremely important to browser manufacturers.
When, due to some unfortunate design decisions, the caret ends up in a non-renderable area, as in your example, the browser steps in and makes it visible. It has to ignore/override some of your rules so the caret becomes visible again. Rather than removing the overflow:hidden altogether (which is, in fact, done by some browsers) - most browsers prefer to just "scroll" the contents of the element with overflow:hidden into view (it's not a proper scroll, there's usually no scrollbar involved - it's more of a "shift"); so they shift the contents into a position that allows the user to continue to see the immediate outcome of their input. This change is temporary and reverted as soon as the <textarea> loses focus or caret moves to a (normally) visible area of it.
Bottom line, whenever you place a <textarea> inside a smaller element with overflow:hidden expect it to be shifted around so the caret is always visible.
In practice, you'll notice a <textarea> becomes more predictable and controllable by placing it into a position:absolute parent rather than applying this property to itself. But, again, don't expect browsers to allow you to hide the caret.
You are a meager designer/coder, far less important than any of their active users.
Apparently, losing an active user is a big deal for most browser manufacturers.

overflow:hidden on body causes document repaint mid-transition of another element in Safari

I have a layout that when an element is clicked, it expands and opens a modal
dialog. When it's clicked a position:fixed clone is appended to the body with the same position properties so that it appears in the same position as the original element. The clone then expands to fill the page to act as a dialog box.
A noscroll class is added to the body
.noscroll {
overflow: hidden;
transform: none;
position: relative;
}
This is where the problem starts to happen. When the clone element is in the middle of transitioning its position properties, a document repaint happens causing the transition to freeze and then continue when the repaint is over.
This only happens in Safari for some reason. Here's a screenshot of the timelines
to show what I mean
When I don't add the noscroll class, this doesn't happen. This is driving me crazy, I've tried everything and this keeps happening.
I encountered this issue today and the solution was to resize all of the images on my page.
For me, the huge green layout event actually wasn't layout or paint but composite. I understand this phase to be when the browser actually draws everything to the screen.
This was (I think) because all of the images on the page were way too big for the size they were being displayed at; ~14mb for images that were at most 300px wide. Resizing all of my images to a more appropriate 300x400 got rid of this issue completely. It makes sense that a bunch of huge images being hidden and shown would be really hard for the browser to draw. Still at a complete loss as to why this problem is triggered by overflow: hidden, and on Safari only.

Sticky menu flickering intermittently

The sticky menu on our site (http://462184.hs-sites.com/) is experiencing issues on some pages and not others.
For example the homepage, if you scroll half way down the page and try to use the menu, it hides momentarily. On the other hand we don't get this issue when on another page such as (http://462184.hs-sites.com/bookkeeping-plans).
Therefore I can only imagine it is some type of element on those pages conflicting?
I've already modified the overflow: hidden, to be set to overflow: auto, and this works quite well in the .header-container, although it seems that although this fixes the problem, there is still the issue when this is set that the images (such as the iPad at the top of the page, then overlaps the next section below it).
Thoughts on how to make sure either the image extends the container so it doesn't run into the next one, or how to fix this once and for all?
Just a thought, could this possibly be the Javascript, as I noticed that the menu bar when scrolled down doesn't dissapear until hovering over an active/Javascript link in the nav bar.
Your support is greatly appreciated :)
If you put a console.log in your call:
if ($(window).scrollTop() > 500 && getScreenWidth > 767) console.log("true")
else console.log("false")
Do you see in your console log the value changing when you scroll down at your homepage? And is this the same output as on your bookkeeping-plans page?
and does it also appear if you remove the slideup and slidetoggle functions?
$(this).parent().siblings('.hs-item-has-children').find('.hs-menu-children-wrapper').slideUp(250);
$(this).next('.hs-menu-children-wrapper').slideToggle(250);
This is what I can think of that is conflicting your code based on what I see on your website

scrolling is choppy in chrome when background image is fixed

I'm trying to create a parallax website. But then I have an issue with fixed positioning.
I have several sections , each with a background-attachment:fixed.
A position:fixed menu bar on the top with an hidden element in it on top of all sections.
A google map 100% with in one of the sections.
Now, the problem is when I scroll the page with animation in google chrome, the scrolling does not go smooth and it flashes several times while scrolling.
I do the scrolling with greensock scrollTo plugin , but that is not the problem as I also tested it with jquery .animate() method. Same result.
I did a research , and found out that chrome has a bug or problem with Fixed positioning ( and sometimes when you put hidden element in it )
Some pages suggested to use these two with the fixed elements :
-webkit-backface-visibility:hidden;
-webkit-transform: translateZ(0);
I added this to the fixed menu and some of the choppy behavior of it reduced , but still not smooth.
If I add this to the sections with background-attachment:fixed elements, the scrolling animation goes smooth but does not act as fixed anymore.
Somebodies says that chrome has problem with large images, some says it has issue with fixed position and somebodies had a solution that did not work for me :D
I uploaded the page :
http://www.FarzanMohajerani.com/test/parallax
just click anywhere on the page to scroll.
I also created a jsFiddle with the exact same code. But I don't know why it doesn't have the problem in jsFiddle :
http://jsfiddle.net/Farzanmc/cRqxT/5/
It would be great if anyone could direct me to the right solution or remind me if I'm doing anything wrong.
Thanks
This solved the issue for me:
-webkit-transform: translate3d(0,0,0);
Adding this rule turns the element into a layer in Chrome, which avoids repainting. In my unique situation the error was caused by browser re-painting.
I', having the same problem with Chrome at the moment and I narrowed down the cause to the following combination:
1) background: fixed;
2) transform: (any transform, even just putting scale(1), would instantly break it).
As long as an element that contains fixed background image doesn't have any "transform" on it, it works fine. But as soon as you even add "transform: scale(1);" which doesn't actually make any real transformation, it completely breaks the fixed background image. You can start scrolling, but it disappears. If it was outside of the screen, it will never appear at all, no matter how far you scroll.
So essentially, the problem is that Chrome at the moment can't handle fixed background images in transformed elements. No matter which level of descendent or ancestor we are talking about.
The thing is, this is pretty much an essential stuff that and I'm really hoping that it gets fixed as soon as possible, because it's extremely limiting. You can't disregard Chrome as if it's IE6.
And you can't apply "position: fixed;" on an "img" element, because it will be fixed to the first "transformed" ancestor, not to the real screen, since that's apparently how it's supposed to be treated, according to W3C. Although, some new value would be welcomed, some that can break all the way to the very window, and fixit to those coordinates.
I ran i to a same problem and fixed it this way:
I had to deal with fixed header on a website and anytime i would scroll with a mouse wheel the header would get choppy.... I had a display:none element in a header and as soon as i removed the element, header became fixed and steady, now it displays well no matter how fast i scroll.

Drag/drop/scroll on mobiles between/within div blocks w/jQuery, and also block overall page scroll

Situation:
Mobile phone equipped with jquery, jquery mobile, jquery-ui.touch and jquery.ui.touch-punch, the latter to make quite some things like drag/drop etc available for touch screens.
Two div blocks from which to drag bullets from one sortable div block into other sortable div block.
On the screen those two blocks are visually adjacent to one another to make things work although suboptimal, because bullet disappears when dragged into the other div block. Nevertheless it can be dropped somehow because afterwards bullets appear indeed in target div block and disappear in original div block.
With help of css option overflow: auto, one can make scrolling appear in both blocks to scroll through the list of bullets.
Problem:
These scrolling sometimes work and sometimes not because in the latter case either the whole page starts scrolling or (after clicking on local div scrollbar) it starts zooming-in to such a level/depth that the whole page becomes useless.
In Firefox Mobile div scrollbars not visible but scrolling sometimes possible; in Opera Mobile div scrollbars visible but at random "deeply" zooms in after clicking a div scrollbar.
Tried out different solutions given on the Internet to block page scrolling or zooming-in, but to no avail. Some "solutions" make page scroll is locked, but then either all scrolling is locked and/or dragging is also locked.
Question:
Anybody any experience with above situation or could give some hint? I know this is bleeding edge because even JQ/JQM (or any other framework?) has not reach this point at the moment, though they are working on it.
jquery.ui.touch-punch is a plugin, although experimental, itÅ› the only way I know so far who make drag/drop etc available for mobiles/touchscreens.
Thank you very much in advance!