Div scrolling freezes sometimes if I use -webkit-overflow-scrolling - ios

if I use -webkit-overflow-scrolling for a scrolling div, it scrolls perfectly with native momentum. But, div itself sometimes freezes and does not respond my finger moves. After 2-3 seconds later, it becomes again scrollable.
I don't know how I am reproducing this problem. But, as I see there are two main behavior creates this situation.
First, If I wait for a while, for instance, 20 seconds, and touch the div, it does not respond. I wait a couple of seconds, and it becomes working again.
Second, I touch several times quickly, and then, it becomes freezing, and again, after a couple of seconds later, it starts working again.
How can I prevent this freezing?

For me, the freezing was repeatable and happened when trying to scroll up or down when already at the top or bottom, respectively. The fix was to add some listeners for touchstart and touchmove and detect these cases and event.preventDefault() on ’em.
Something like the following, where .scroller is the div that will actually scroll (changes to scrollTop).
var lastY = 0;
var targetElt = document.querySelector(".scroller");
targetElt.addEventListener('touchstart', function(event) {
lastY = event.touches[0].clientY;
});
targetElt.addEventListener('touchmove', function(event) {
var top = event.touches[0].clientY;
var scrollTop = event.currentTarget.scrollTop;
var maxScrollTop = event.currentTarget.scrollHeight -
$(event.currentTarget).outerHeight();
var direction = lastY - top < 0 ? 'up' : 'down';
if (
event.cancelable && (
(scrollTop <= 0 && direction === 'up') ||
(scrollTop >= maxScrollTop && direction === 'down')
)
)
event.preventDefault();
lastY = top;
});
I hope this helps the next poor soul that encounters this horrible bug! Good luck and keep fighting!

Try using overflow: hidden on body. This should resolve the issue: https://codepen.io/cppleon/pen/vYOgKzX
HTML
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div id="scrollable-content">
<div class="site-header"></div>
<div class="main-content"></div>
</div>
</body>
</html>
CSS
body {
/* magic is here */
overflow: hidden;
}
#scrollable-content {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50%;
background-color: gray;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.site-header {
width: 100%;
height: 120px;
background-color: orange;
}
.main-content {
height: 200%;
}

Stable solution
After many days to try to fix it, i saw that the problem comes from fixed body element, maybe because you don't want your users to see your page body bounce when the scroll is blocked: cf this example.
When the body is fixed and you're experiencing scrolling freeze bug, if you inspect the body with Desktop Safari on you iOS device, you can see it's kind of "artificially" moving... yes webkit stuff...
I tried all solutions listed on this threat but also on github similar issues. No one was working.
The only stable fix for me is to use this package : body-scroll-lock and remove the fixed on your body element. Right now you can both enjoy fixed body and no scrolling freezing bugs.
Hope it will help people who are currently creating progressive web apps on IOS.

I used the below code I think is working.
var scrollTimer;
$('.scroller').on('scroll',function(e){
clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
this.scrollTop = Math.max(1, Math.min(this.scrollTop, this.scrollHeight - this.clientHeight - 1));
}, 300);
});

I got the same problem. But that solved easily.
This is what i have done:
deleted height property of div that was scroll-able.
Maybe you are not in the same situation as me and this will not work for you.

I know this is very old, but maybe somebody else has the same problem. For me the issue was caused by iNoBounce (https://github.com/lazd/iNoBounce). Y scrolling was fine but X scrolling was causing a lot of issues, the element would get stuck and you'd have to touch and move many times until it finally scrolled.
After removing iNoBounce there were no issues anymore besides the obvious scroll-bounce (and specially the "overscrolling") that was iNoBounce removed. To disable the overscrolling I used the following, however the scroll-bounce is now there.
html { height: 100%; position: fixed; overflow: hidden; }
body { height: 100%; position: relative; overflow: auto; }

I ran into this bug recently, and after trying a lot of hacky solutions the one that worked best for us was simply scrolling the view by a pixel if its at the bottom. This prevents the "freeze", which I think is actually the body/window receiving the scroll event if the nested container is fully scrolled down. This is using React but you get the idea.
const listener = () => {
window.requestAnimationFrame(() => {
if (!this.scrollRef.current) {
return;
}
const { scrollTop, scrollHeight, clientHeight } = this.scrollRef.current;
if (scrollTop === scrollHeight - clientHeight) {
// at the bottom
this.scrollRef.current.scrollTo(0, scrollHeight - clientHeight - 1);
}
});
}
this.scrollRef.current.addEventListener("scroll", listener);

Related

How can I properly position a cursor on IOS11 Safari in popup forms?

After we upgraded my iPhone to IOS11, I started seeing a cursor in a random position in my login window. This also happens on Chrome / IOS11. The position of the cursor is marked red on screenshots below.
Try adding position: fixed to the body of the page.
Piggybacking off of ybentz's answer. If you use the bootstrap modal, you can add this to your main.js file:
var savedScrollPosition;
$(document).on('show.bs.modal', '.modal', function() {
savedScrollPosition = $(window).scrollTop();
});
$(document).on('hidden.bs.modal', '.modal', function() {
window.scrollTo(0, savedScrollPosition);
});
And then this to your css because you'll already have the modal-open class being added anytime the modal pops:
body.modal-open {
position: fixed;
width: 100%;
}
Thanks for the help ybentz!! I would've responded to your comment, but I don't have the reputation to do so yet.
Ignacios Answer solved the Problem for me.
If i show an overlayer/modal i add the class fixed to the body.
Also add to css this rule:
body.fixed{
position: fixed;
}
I had the same problem and the position: fixed solution on the body does solve it so that's great. One thing to note though is that adding the class to the body causes the browser to "jump" to the top of the page so when you remove it when the popup/modal is closed it might be confusing for the user.
If your popup/modal is full screen on iOS what you can do to fix it is save the scroll position before adding the position: fixed class with something like this (using jQuery but can be done easily with vanilla js):
var savedScrollPosition = $(window).scrollTop()
$('body').addClass('has-fullscreen-modal')
and then restore it on popup close like this:
$('body').removeClass('has-fullscreen-modal')
window.scrollTo(0, savedScrollPosition)
and your css will be
body.has-fullscreen-modal {
position: fixed;
}
Hope that helps!
Personally, position: fixed scroll to top automatically. Quite annoying !
To avoid penalizing other devices and versions I apply this fix only to the appropriate versions of iOS.
**VERSION 1 - All modals fix**
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
var ua = navigator.userAgent,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
// Add CSS class to body
$("body").addClass("iosBugFixCaret");
}
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
**VERSION 2 - Selected modals only**
I modified the function to fire only for selected modals with a class .inputModal
Only the modals with inputs should be impacted to avoid the scroll to top.
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
(function iOS_CaretBug() {
var ua = navigator.userAgent,
scrollTopPosition,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
$(document.body).on('show.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Get scroll position before moving top
scrollTopPosition = $(document).scrollTop();
// Add CSS to body "position: fixed"
$("body").addClass("iosBugFixCaret");
}
});
$(document.body).on('hide.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Remove CSS to body "position: fixed"
$("body").removeClass("iosBugFixCaret");
//Go back to initial position in document
$(document).scrollTop(scrollTopPosition);
}
});
}
})();
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
For the HTML
Add the class inputModal to the modal
<div class="modal fade inputModal" tabindex="-1" role="dialog">
...
</div>
Nota bene
The javascript function is now self-invoking
REF : iOS 11 Safari bootstrap modal text area outside of cursor
I have fixed this issue with this CSS
#media(max-width:767px) {
body {
position:fixed !important;
overflow:auto !important;
height:100% !important;
}
}

How to make fixed-content go above iOS keyboard?

I can only find questions where people have the opposite problem.
I want my fixed content to go above the iOS keyboard.
Image of the problem:
I want iOS to behave like Android.
Is there a simple way to achieve this?
Parent element css:
.parent{
position:fixed;
top: 0;
left 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
Button css:
.button{
position:fixed;
left 0;
right: 0;
bottom: 0;
width: 100%;
height: 5rem;
}
We can use VisualViewport to calculate keyboard height. So we can set fixed-content pos correct.
Small demo: https://whatwg6.github.io/pos-above-keyboard/index.html
Code snippet:
const button = document.getElementById("button");
const input = document.getElementById("input");
const height = window.visualViewport.height;
const viewport = window.visualViewport;
window.addEventListener("scroll", () => input.blur());
window.visualViewport.addEventListener("resize", resizeHandler);
function resizeHandler() {
if (!/iPhone|iPad|iPod/.test(window.navigator.userAgent)) {
height = viewport.height;
}
button.style.bottom = `${height - viewport.height + 10}px`;
}
function blurHandler() {
button.style.bottom = "10px";
}
html,
body {
margin: 0;
padding: 0;
}
#button {
position: fixed;
width: 100%;
bottom: 10px;
background-color: rebeccapurple;
line-height: 40px;
text-align: center;
}
<input type="text" inputmode="decimal" value="0.99" id="input" onblur="blurHandler()" />
<div id="button">Button</div>
Problems: https://developers.google.com/web/updates/2017/09/visual-viewport-api#the_event_rate_is_slow
Why not innerHeight?: Iphone safari not resizing viewport on keyboard open
Mobile Safari does not support position: fixed when an input focused and virtual keyboard displayed.
To force it work the same way as Mobile Chrome, you have to use position: absolute, height: 100% for the whole page or a container for your pseudo-fixed elements, intercept scroll, touchend, focus, and blur events.
The trick is to put the tapped input control to the bottom of screen before it activates focus. In that case iOS Safari always scrolls viewport predictably and window.innerHeight becomes exactly visible height.
Open https://avesus.github.io/docs/ios-keep-fixed-on-input-focus.html in Mobile Safari to see how it works.
Please avoid forms where you have several focusable elements because more tricks to fix position will be necessary, those were added just for demonstration purposes.
Note that for rotation and landscape mode, additional tricks are necessary. I'm working on a framework called Tuff.js which will provide a full-screen container helping mobile web developers to build web applications much faster. I've spend almost a year on the research.
By the way, to prevent scrolling of the whole window when virtual keyboard is active, you can use this super simple trick
var hack = document.getElementById('scroll-hack');
function addScrollPixel() {
if (hack.scrollTop === 0) {
// element is at the top of its scroll position, so scroll 1 pixel down
hack.scrollTop = 1;
}
if (hack.scrollHeight - hack.scrollTop === hack.clientHeight) {
// element is at the bottom of its scroll position, so scroll 1 pixel up
hack.scrollTop -= 1;
}
}
if (window.addEventListener) {
// Avoid just launching a function on every scroll event as it could affect performance.
// You should add a "debounce" to limit how many times the function is fired
hack.addEventListener('scroll', addScrollPixel, true);
} else if (window.attachEvent) {
hack.attachEvent('scroll', addScrollPixel);
}
body {
margin: 0 auto;
padding: 10px;
max-width: 800px;
}
h1>small {
font-size: 50%;
}
.container {
display: flex;
align-items: top;
justify-content: space-between;
}
.container>div {
border: #000 1px solid;
height: 200px;
overflow: auto;
width: 48%;
-webkit-overflow-scrolling: touch;
}
<h1>iOS Scroll Hack</h1>
<p>Elements with overflow:scroll have a slightly irritating behaviour on iOS, where when the contents of the element are scrolled to the top or bottom and another scroll is attempted, the browser window is scrolled instead. I hacked up a fix using minimal,
native JavaScript.</p>
<p>Both lists have standard scrolling CSS applied (<code>overflow: auto; -webkit-overflow-scrolling: touch;</code>), but the list on the right has the hack applied. You'll notice you can't trigger the browser to scroll whilst attempting to scroll the list
on the right.</p>
<p>The only very slight drawback to this is the slight "jump" that occurs when at the top or bottom of the list in the hack.</p>
<div class='container'>
<div id='scroll-orig'>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
</ul>
</div>
<div id='scroll-hack'>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
</ul>
</div>
</div>
Got this answer from here
This is a well known problem, and unfortunately one must resort to hacky tricks like the accepted answer for now. The W3C is however in the process of specifying The VirtualKeyboard API.
Note: At the time of writing, this answer is not yet ready for prime time. It's important to understand that this specification must also be forward looking, to adapt to the myriad possible virtual keyboards of the future. It may be a few years before reliable cross platform browser support begins to appear and this answer becomes the correct one.
I found an interesting solution to this problem.
The solution is to create a hidden input and focus on it on the touchstart event.
<input id="backinput" style="position:absolute;top:0;opacity:0;pointer-events: none;">
<input id="input" style="position:absolute;bottom:0;">
Using JQuery:
$('#backinput').on('focus',function(e)
{
e.preventDefault();
e.stopPropagation();
const input = document.getElementById('input');
input.focus({ preventScroll: true });
})
$('#input').on("touchstart", function (event) {
if(!$(this).is(":focus"))
{
event.stopPropagation();
event.preventDefault();
$('#backinput').focus();
}
})
Finally, resize the viewport so that the bottom input moves above the keyboard (if needed)
window.visualViewport.addEventListener("resize", (event) => {
$('body').height(parseInt(visualViewport.height));
});
For me it works perfect. I am building a messenger.

Phonegap: Keyboard changes window height in iOS 7

In iOS 6 everything works fine. The keyboard opens and moves the input into view. When the keyboard closes everything goes back where it should.
In iOS 7 the keyboard opens fine and the input remains in view. When the keyboard is closed the whole bottom half of the app is gone, though. I've tracked the issue down to the height of the window changing when the keyboard is opened, and not changing back when it's closed.
Right before the keyboard is opened the window height is 568 according to $(window).height() and after it's opened and after it's closed it is 828. The height of the document also changes accordingly.
I've attempted preventing the window from resizing with:
$(window).resize(function(e) {
e.preventDefault();
e.stopPropagation();
window.resizeTo(320,480);
return false;
});
I've also attempted to set the size back after the keyboard closes with no success.
I'm using phonegap 2.7 and have KeyboardShrinksView set to true.
I was seeing this too. After the height changes, some of our absolute positioned elements disappear off the bottom of the screen.
I found that with KeyBoardShrinksView = false in ios7, window.height stayed constant. This was the opposite of ios6 though, so a bit of a catch 22.
Not sure if there's a better way of handling this in Phonegap, but I put this in CDVViewController.m, created to config.xml files for ios < v7 and ios > v6, and my app works the way I want. Seemed a bit hacky, but not too disruptive of the rest of my code.
// read from config.xml in the app bundle
NSString* path = [[NSBundle mainBundle] pathForResource:#"config" ofType:#"xml"];
if (IsAtLeastiOSVersion(#"7.0")) {
path = [[NSBundle mainBundle] pathForResource:#"config_ios7" ofType:#"xml"];
}
(I also tried an app preference plugin at https://github.com/phonegap/phonegap-plugins/tree/master/iPhone/ApplicationPreferences but don't think this was designed for this kind of preference.)
After I upgraded my project to iOS with cordova 3.1 I start having similar problems for the input fields in where I did not have the code listed above. The keyboard pushes things up and the header and footer did not returned to their original positions. I have tested and that solve the problem (maybe not very elegantly but it is a workaround). I just put that code on my pageinit event.
/*************************************************************************************************
* FIX: to avoid the buggy header and footer to jump and stick not
* to the top/bottom of the page after an input or textfield lost focus and the keyboard dissapear *
*************************************************************************************************/
$('input, textarea')
.on('focus', function (e) {
$('header, footer').css('position', 'absolute');
})
.on('blur', function (e) {
$('header, footer').css('position', 'fixed');
//force page redraw to fix incorrectly positioned fixed elements
setTimeout( function() {
window.scrollTo( $.mobile.window.scrollLeft(), $.mobile.window.scrollTop() );
}, 20 );
});
add code into CDVViewController.m
for example it added into webViewDidFinishLoad function
CGRect newFrame = self.webView.bounds;
NSLog(#"%f" , newFrame.size.height);
NSString *JS = [NSString stringWithFormat:#"viewport = document.querySelector('meta[name=viewport]'); viewport.setAttribute('content', 'user-scalable=no, initial-scale=1.0, maximum-scale=0.5, minimum-scale=0.5, width=device-width, height=%d, target-densitydpi=device-dpi');", (int) newFrame.size.height*2 ];
[self.webView stringByEvaluatingJavaScriptFromString:JS];
this code change <meta name="viewport" content="..."> and set height of device
set your viewport meta tag to your html
<meta name="viewport" content="width=device-width,height=**yourheight**, initial-scale=1, maximum-scale=1, user-scalable=0" >
or
<meta name="viewport" content="width=device-width,height=**device-height**, initial-scale=1, maximum-scale=1, user-scalable=0" >
The Petrash's solution worked for me. But I had still problems supporting rotations on iPad.
So, in the same CDVViewController.m I've added this method:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
if (self.webView){
CGRect newFrame = self.webView.bounds;
//NSLog(#"%f" , newFrame.size.height);
NSString *JS = [NSString stringWithFormat:#"viewport = document.querySelector('meta[name=viewport]'); viewport.setAttribute('content', 'user-scalable=no, initial-scale=1.0, maximum-scale=1, minimum-scale=1, width=device-width, height=%d, target-densitydpi=device-dpi');", (int) newFrame.size.height*1 ];
[self.webView stringByEvaluatingJavaScriptFromString:JS];
}
}
and, to support the "non scale" behaviour, edited the Petrash's solution in this way:
CGRect newFrame = self.webView.bounds;
//NSLog(#"%f" , newFrame.size.height);
NSString *JS = [NSString stringWithFormat:#"viewport = document.querySelector('meta[name=viewport]'); viewport.setAttribute('content', 'user-scalable=no, initial-scale=1.0, maximum-scale=1, minimum-scale=1, width=device-width, height=%d, target-densitydpi=device-dpi');", (int) newFrame.size.height*1 ];
[self.webView stringByEvaluatingJavaScriptFromString:JS];
KeyboardShrinksView = false
This is hacky, but it works from 5.1 to 7.0.3. Tested on Cordova 3.0.
After hours of investigating I've managed to get it to work:
My div, that is pushed up and never get's down again,
had the css attribute
position:fixed;
I switched this to
position:absolute;
and everything worked!
The best way I found was to put everything into a div and fix its height via javascript.
Works on modern Versions of both iOS (5, 6, 7) and Android (4.2, ...).
<style>
body {
overflow-y: scroll;
overflow-x: hidden;
webkit-overflow-scrolling: touch;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
body > .viewport{
position: absolute;
top: 0;
left: 0;
right: 0;
}
</style>
<body>
<div class='viewport'>
<!-- Put everything here -->
</div>
</body>
<script type="text/javascript">
$("body > .viewport").height($(document).height());
// WARNING: if your app works in both landscape and portrait modus, then you should reset the height of the container when the app changes orientation
</script>
I had a similar issue that drove me nuts for days. Not sure if this will help anyone else, but this is what solved it for me: (note I'm using jquery finger library to listen to tap events):
$('body').delegate("#send_feedback_button","tap", function(event){
$('textarea').blur();
event.stopImmediatePropagation();
// do my stuff
});
For me calling blur on any textarea in the view was the trick. The stopImmediatePropagation got rid of some other funkiness.
I had the same problem and I managed to track it down to dynamic content.
I had initially an empty div that was filled with text using javascript.
When I pre filled the div with static text the problem was gone.
Looks like this div's height was not counted when resizing.

jQuery Mobile: .animate({scrollTop}) doesn't work after fixing page transitions

jsFiddle links are at the bottom.
I have a Phonegap app that I have created with jQuery Mobile. The page transitions were really choppy and inconsistent in the native iOS app until I found this solution. It made my scrolling not so great, so I made a few changes per this follow-up article.
After the first solution and still after I implemented the second solution, the following code stopped working for me in my app:
$('html, body').animate({scrollTop: $('#specificID').offset().top}, 2500);
The above code scrolls the user down the page over 2.5 seconds to the DIV with the ID of specificID.
I have tried multiple things, but nothing seems to work:
$('#container').animate({scrollTop: $('#specificID').offset().top}, 2500);
$('html, body, #container').animate({scrollTop: $('#specificID').offset().top}, 2500);
$('.scrollable').animate({scrollTop: $('#specificID').offset().top}, 2500);
$(".scrollable").animate({ scrollTop: $("#specificID").scrollTop() }, 2500);
So, here is how I adjusted my jquery mobile code to fix the page transitions:
1. I wrapped [data-role="page"] DIV with container DIV
<body>
<div id="container">
<div data-role="page">
2. I added the following Javascript
$(document).one('mobileinit', function () {
// Setting #container div as a jqm pageContainer
$.mobile.pageContainer = $('#container');
// Setting default page transition to slide
$.mobile.defaultPageTransition = 'slide';
});
3. I added the following CSS
body {
margin: 0;
}
div#container {
position: absolute;
width: 100%;
top: 0;
bottom: 0;
}
div[data-role="header"] {
position: absolute;
top: 0;
left: 0;
right: 0;
}
div[data-role="content"] {
position: absolute;
top: 41px;
bottom: 0;
left: 0;
right: 0;
}
.scrollable {
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
/* iOS specific fix, don't use it on Android devices */
.scrollable > * {
-webkit-transform: translateZ(0px);
}
I setup three jsFiddles to show this:
Plain jQuery - scrollTop working: http://jsfiddle.net/pxJcD/1/
Transition Fix - scrollTop NOT working: http://jsfiddle.net/ytqke/3/
Transition Fix w/ Native Scrolling - scrollTop NOT working: http://jsfiddle.net/nrxMj/2/
The last jsFiddle is the solution that I am using and the one that I need to work. I provided the second one to show that the scrollTop functionality stopped before any of the native scrolling changes I made. Any thoughts on what I can do to be able to scroll down the page using javascript?
I’m using jQuery 1.8.2, jQuery Mobile 1.2.0, and Phonegap 2.2.0 (via Build).
Thank you for any help you can offer.
In your CSS, you have set your container's position property to Absolute.
Remove your div#container
It should work.
http://jsfiddle.net/nrxMj/16/

ipad safari: disable scrolling, and bounce effect?

I'm working on a browser based app, currently I'm developing and styling for the ipad safari browser.
I'm looking for two things on the ipad: How can I disable vertical scrolling for pages that don't require it? & how can I disable the elastic bounce effect?
This answer is no longer applicable, unless you are developing for a very old iOS device... Please see other solutions
2011 answer: For a web/html app running inside iOS Safari you want something like
document.ontouchmove = function(event){
event.preventDefault();
}
For iOS 5 you may want to take the following into account: document.ontouchmove and scrolling on iOS 5
Update September 2014:
A more thorough approach can be found here: https://github.com/luster-io/prevent-overscroll. For that and a whole lot of useful webapp advice, see http://www.luster.io/blog/9-29-14-mobile-web-checklist.html
Update March 2016: That last link is no longer active - see https://web.archive.org/web/20151103001838/http://www.luster.io/blog/9-29-14-mobile-web-checklist.html for the archived version instead. Thanks #falsarella for pointing that out.
You can also change the position of the body/html to fixed:
body,
html {
position: fixed;
}
To prevent scrolling on modern mobile browsers you need to add the passive: false. I had been pulling my hair out getting this to work until I found this solution. I have only found this mentioned in one other place on the internet.
function preventDefault(e){
e.preventDefault();
}
function disableScroll(){
document.body.addEventListener('touchmove', preventDefault, { passive: false });
}
function enableScroll(){
document.body.removeEventListener('touchmove', preventDefault);
}
You can use this jQuery code snippet to do this:
$(document).bind(
'touchmove',
function(e) {
e.preventDefault();
}
);
This will block the vertical scrolling and also any bounce back effect occurring on your pages.
overflow: scroll;
-webkit-overflow-scrolling: touch;
On container you can set bounce effect inside element
Source: http://www.kylejlarson.com/blog/2011/fixed-elements-and-scrolling-divs-in-ios-5/
I know this is slightly off-piste but I've been using Swiffy to convert Flash into an interactive HTML5 game and came across the same scrolling issue but found no solutions that worked.
The problem I had was that the Swiffy stage was taking up the whole screen, so as soon as it had loaded, the document touchmove event was never triggered.
If I tried to add the same event to the Swiffy container, it was replaced as soon as the stage had loaded.
In the end I solved it (rather messily) by applying the touchmove event to every DIV within the stage. As these divs were also ever-changing, I needed to keep checking them.
This was my solution, which seems to work well. I hope it's helpful for anyone else trying to find the same solution as me.
var divInterval = setInterval(updateDivs,50);
function updateDivs(){
$("#swiffycontainer > div").bind(
'touchmove',
function(e) {
e.preventDefault();
}
);}
Code to To remove ipad safari: disable scrolling, and bounce effect
document.addEventListener("touchmove", function (e) {
e.preventDefault();
}, { passive: false });
If you have canvas tag inside document, sometime it will affect the usability of object inside Canvas(example: movement of object); so add below code to fix it.
document.getElementById("canvasId").addEventListener("touchmove", function (e) {
e.stopPropagation();
}, { passive: false });
none of the solutions works for me. This is how I do it.
html,body {
position: fixed;
overflow: hidden;
}
.the_element_that_you_want_to_have_scrolling{
-webkit-overflow-scrolling: touch;
}
Try this JS sollutuion:
var xStart, yStart = 0;
document.addEventListener('touchstart', function(e) {
xStart = e.touches[0].screenX;
yStart = e.touches[0].screenY;
});
document.addEventListener('touchmove', function(e) {
var xMovement = Math.abs(e.touches[0].screenX - xStart);
var yMovement = Math.abs(e.touches[0].screenY - yStart);
if((yMovement * 3) > xMovement) {
e.preventDefault();
}
});
Prevents default Safari scrolling and bounce gestures without detaching your touch event listeners.
Tested in iphone. Just use this css on target element container and it will change the scrolling behaviour, which stops when finger leaves the screen.
-webkit-overflow-scrolling: auto
https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling
improved answer #Ben Bos and commented by #Tim
This css will help prevent scrolling and performance issue with css re-render because position changed / little lagging without width and height
html,
body {
position: fixed;
width: 100%;
height: 100%
}
For those who are using MyScript the Web App and are struggling with the body scrolling/dragging (on iPad and Tablets) instead of actually writing:
<body touch-action="none" unresolved>
That fixed it for me.
You can use js for prevent scroll:
let body = document.body;
let hideScroll = function(e) {
e.preventDefault();
};
function toggleScroll (bool) {
if (bool === true) {
body.addEventListener("touchmove", hideScroll);
} else {
body.removeEventListener("touchmove", hideScroll);
}
}
And than just run/stop toggleScroll func when you opnen/close modal.
Like this toggleScroll(true) / toggleScroll(false)
(This is only for iOS, on Android not working)
Try this JS solution that toggles webkitOverflowScrolling style. The trick here is that this style is off, mobile Safari goes to ordinary scrolling and prevents over-bounce — alas, it is not able to cancel ongoing drag. This complex solution also tracks onscroll as bounce over the top makes scrollTop negative that may be tracked. This solution was tested on iOS 12.1.1 and has single drawback: while accelerating the scroll single over-bounce still happens as resetting the style may not cancel it immediately.
function preventScrollVerticalBounceEffect(container) {
setTouchScroll(true) //!: enable before the first scroll attempt
container.addEventListener("touchstart", onTouchStart)
container.addEventListener("touchmove", onTouch, { passive: false })
container.addEventListener("touchend", onTouchEnd)
container.addEventListener("scroll", onScroll)
function isTouchScroll() {
return !!container.style.webkitOverflowScrolling
}
let prevScrollTop = 0, prevTouchY, opid = 0
function setTouchScroll(on) {
container.style.webkitOverflowScrolling = on ? "touch" : null
//Hint: auto-enabling after a small pause makes the start
// smoothly accelerated as required. After the pause the
// scroll position is settled, and there is no delta to
// make over-bounce by dragging the finger. But still,
// accelerated content makes short single over-bounce
// as acceleration may not be off instantly.
const xopid = ++opid
!on && setTimeout(() => (xopid === opid) && setTouchScroll(true), 250)
if(!on && container.scrollTop < 16)
container.scrollTop = 0
prevScrollTop = container.scrollTop
}
function isBounceOverTop() {
const dY = container.scrollTop - prevScrollTop
return dY < 0 && container.scrollTop < 16
}
function isBounceOverBottom(touchY) {
const dY = touchY - prevTouchY
//Hint: trying to bounce over the bottom, the finger moves
// up the screen, thus Y becomes smaller. We prevent this.
return dY < 0 && container.scrollHeight - 16 <=
container.scrollTop + container.offsetHeight
}
function onTouchStart(e) {
prevTouchY = e.touches[0].pageY
}
function onTouch(e) {
const touchY = e.touches[0].pageY
if(isBounceOverBottom(touchY)) {
if(isTouchScroll())
setTouchScroll(false)
e.preventDefault()
}
prevTouchY = touchY
}
function onTouchEnd() {
prevTouchY = undefined
}
function onScroll() {
if(isTouchScroll() && isBounceOverTop()) {
setTouchScroll(false)
}
}
}
Consider the following architecture:
<body> <div id="root"></div> </body>
this css will work:
#root { position: fixed; height: 100%; overflow: auto; }
For those of you who don't want to get rid of the bouncing but just to know when it stops (for example to start some calculation of screen distances), you can do the following (container is the overflowing container element):
const isBouncing = this.container.scrollTop < 0 ||
this.container.scrollTop + this.container.offsetHeight >
this.container.scrollHeight
Disable safari bounce scrolling effect:
html,
body {
height: 100%;
width: 100%;
overflow: auto;
position: fixed;
}
I had an issue with grabbing the html element in the background, when a menu with scroll was open and either at the top or at the bottom at the scroll height. I tried lots of things. Setting html position to fixed was the closest I got to lock the screen, but in the PWA it resulted in a white area at the bottom, that I couldn't fix.
Finally I've found a solution, that worked for me 🎉:
html {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
margin: 0;
height: calc(100vh - 1px)
overflow: hidden;
background-color: 'Whatever color you need to hide the 1px at the bottom'
}
Because it only seems to be an issue on iOS, I have targeted the devices from iPhone X to 12 Pro Max:
body {
margin: 0;
overflow: hidden;
background-color: '#TIP: You can use the color picker from the inspector';
#media only screen and (min-width: 375px) and (max-height: 926px) and (-webkit-device-pixel-ratio: 3) {
height: calc(100vh - 1px);
}
}
This is preventing any kind of scroll, touch or grab in the html or body elements, and scroll is still working in the menu or where else specified. Cheers.
body {
touch-action:none;
}
Using JQuery
// Disable
$("body").css({ "touch-action": "none" })
// Enable
$("body").css({ "touch-action": "auto" })
css overscroll-behavior is now supported in iOS 16. If you are targeting > iOS 16 devices, to prevent elastic bounce effect, add the following CSS to the html root
html {
overscroll-behavior: none;
}
Please note, the solution provided only disables elastic bounce effect when content is larger than viewport.
If you also want to completely disable scrolling in main page on iOS devices, use
html body {
overflow: hidden;
}
Similar to angry kiwi I got it to work using height rather than position:
html,body {
height: 100%;
overflow: hidden;
}
.the_element_that_you_want_to_have_scrolling{
-webkit-overflow-scrolling: touch;
}
Solution tested, works on iOS 12.x
This is problem I was encountering :
<body> <!-- the whole body can be scroll vertically -->
<article>
<my_gallery> <!-- some picture gallery, can be scroll horizontally -->
</my_gallery>
</article>
</body>
While I scrolling my gallery, the body always scrolling itself (human swipe aren't really horizontal), that makes my gallery useless.
Here's what I did while my gallery start scrolling
var html=jQuery('html');
html.css('overflow-y', 'hidden');
//above code works on mobile Chrome/Edge/Firefox
document.ontouchmove=function(e){e.preventDefault();} //Add this only for mobile Safari
And when my gallery end its scrolling...
var html=jQuery('html');
html.css('overflow-y', 'scroll');
document.ontouchmove=function(e){return true;}
Hope this helps~

Resources