I have an iFrame that contains a div that should be scrollable (just the div, not the whole iFrame). This works now fine in most browsers, but not in iOS 10 (tested with iPhone 7), where nothing is moveable:
https://codepen.io/arichter83/full/OJLNjey
Fixes outside the iFrame
When adding the -webkit-overflow-scrolling:touch;overflow-y:scroll;height:300px to the iFrame itself it works: https://codepen.io/arichter83/full/xxKVpbL
But this seems to be a bug of iOS 10, because it also works if adding those parameters to an other div besides the iFrame: https://codepen.io/arichter83/full/PoYNEoX
Fixes inside the iFrame
So but actually my situation is, that I can't control the content outside the iFrame, so does anybody knows a workaround?
I tried:
Switching to -webkit-overflow-scrolling:auto;
Add -webkit-transform: translateZ(0); or -webkit-overflow-scrolling: touch; transform: translate3d(0, 0, 0); to the body
In the past while dealing with iOS, I've forced hardware acceleration on the body to deal with scrolling issues.
body {
-webkit-overflow-scrolling: touch;
transform: translate3d(0, 0, 0);
}
Didn't have enough points to add a comment - figured this might help.
I found one solution, but it's hacky:
function resetScroller() {
scroller = document.getElementById('scroller')
scroller.style.webkitOverflowScrolling = 'auto'
scroller.style.display = 'none'
window.setTimeout(() => {
scroller.style.display = 'block'
}, 0)
}
Change the -webkit-overflow-scrolling: touch; to -webkit-overflow-scrolling: auto;, hide the element and show it again after a bit.
This can be done only for iOS 10 with the following condition:
const ios_version = window.navigator.userAgent.match(/(iPad|iPhone|iPod).* OS ([0-9]+)/)[2] as unknown as number
if(ios_version > 0 && ios_version <= 10) {
...
}
I included a button in the iFrame content so it can be tested: https://codepen.io/arichter83/full/OJLNjey
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;
}
}
I've implemented a simple left-pull burger menu in a mobile webpage that lives inside an iframe. However, it's behaving strangely on iPhones. We are using Bootstrap for the general page layout and stuff.
Using WeInRe I've noticed the following behaviour: in an iframe with 320px in fixed width, if I add, say, left: 50px to the body of the page inside it, this body moves 50px to the left just fine, but also starts to display 370px in width, instead of 320px as before.
The problem is worse: as the correct left value is a percentage, the body gets that bigger width, and after that the left is recalculated, making the menu larger than the viewport.
What the hell is happening here? Is this some sort of known bug of Mobile Safari?
Unfortunately, there's no public available code for this issue yet...
This is the relevant code:
.offcanvas {
left: 0;
position: relative;
}
.offcanvas.active {
left: 75%;
overflow: hidden;
}
.sidebar {
position: fixed;
background-color: #5c008a;
top: 0;
left: -75%;
width: 75%;
height: 100%;
}
.offcanvas.active .sidebar {
left: 0;
}
$('[data-toggle="offcanvas"]').click(function() {
$('.offcanvas').toggleClass('active');
});
<body class="offcanvas">
[...]
<div class="sidebar">[...]</div>
[...]
</body>
Here's a sample, based on a series of side menus from a tutorial (click the left or right push options).
I've been trying to test my jquery mobile app on multiple devices. I currently have a panel that is opened via swipe or clicking on a 'menu' button.
However, on wide screens, the app just looks funky. WAY too wide. I understand this is meant for mobile, but, why not format it for ipads/surface/androids as well?
To do this, I'd like to shorten the width by requiring the panel to be open at all times when the width exceeds a specific value.
I've dug through the documentation, and the closest thing I found was:
class="ui-responsive-panel" from the following link: http://view.jquerymobile.com/master/docs/widgets/panels/panel-fixed.php
After adding it to my page header, I noticed that I can't 'swipe' the menu away when the window is wide. When I shrink the window (either on a pc browser, or by rotating the device), it can be swiped.
Is anyone familiar with this, and willing to shed some light?
I'm facing the same problem. I want the panel to stay open when the user turns the device in landscape mode (tablets) or if the window is wider than a specific width at the very beginning.
Unfortunately I did not find any solutions and the jQuery Mobilele example for responsive panels in this case.
So I found a way by using some javascript but I'm not happy with this solutions since a pure CSS solution with media queries would be nicer.
However, here is my "workaround" solution.
<script type="text/javascript">
window.onresize = function (event) {
if (window.innerWidth > 800) {
window.setTimeout(openPanel, 1);
}
if (window.innerWidth < 800) {
window.setTimeout(closePanel, 1);
}
};
function closePanel() {
$("#mypanel").panel("close");
}
function openPanel() {
$("#mypanel").panel("open");
}
$( "#mypanel" ).on( "panelcreate", function( event, ui ) {
if (window.innerWidth > 800) {
openPanel();
}
if (window.innerWidth < 800) {
closePanel();
}
});
</script>
So if the window inner width is higher than 800, the panel opens; if it is lower than 800 it closes. Furthermore the window.onresize function is required to provide the same functionality in case the user turns the device from portrait mode to landscape mode.
Hope it helped. But I'm still looking for a better solution ;)
I found a css-only solution for that issue that is much simpler.
In the media query for your responsive panel #media (min-width:55em){...} add/overwrite the following css classes:
.ui-panel-closed { width: 17em; }
.ui-panel-content-wrap.ui-body-c.ui-panel-animate.ui-panel-content-wrap-closed{ margin-left:17em; }
The second class might be different to yours depending on the swatch you are using; in this case it is "C". However, just take the content wrap class that wraps all your header,content, footer area.
In my example I used a panel with data-display="reveal" data-position="left" If you want it appearing on the right hand side just change margin-left:17em to margin-right:17em
If you want the panel to behave like "overlay", just forget about the second class i posted...
Best regards
I am facing the problem right now and I found the solution of mJay really useful. However it would be great to use media queries instead, something like this perhaps:
#media (min-width:35em){
.ui-panel{
width:30em;
}
.ui-panel-close { width:30em; }
}
Below is my "CSS" solution. What you need to know: mnuMenu is the id of the panel that I want to always have visible on the left side of the screen and lnkMenu is the id of the a tag for the button which normally shows the panel on smaller screen widths.
#media all and (min-width: 900px)
{
#mnuMenu
{
visibility: visible;
position: fixed;
left: 0;
float: left;
width: 300px;
height: 100vh;
background: none;
-webkit-transition: none !important;
-moz-transition: none !important;
transition: none !important;
-webkit-transform: none !important;
-moz-transform: none !important;
transform: none !important;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
#lnkMenu
{
display: none;
}
.ui-content
{
margin-left: 325px;
}
}
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~