So I made a fixed dropdown menu that changes to a hamburger menu for mobile. Everything works fine on desktop but on mobile I'm unable to scroll the menu items. I have tried a plethora of suggested fixes for this but none of them fix my issue. Most of the fixes I've come across have included some form of the following but have not worked:
max-height: 300px;
overflow-y: scroll;
Here is a fiddle of what I have right now:
https://jsfiddle.net/doitliketyler/2gqd0hLs/3/
The black square is the mobile hamburger button. Does anyone know how to get this working properly and smoothly for mobile? Any help would be greatly appreciated. Thanks so much.
A position of static will prevent scrolling.
So to fix this, you have to set your menu to a position of something like relative for mobile.
So for the .header selector inside the #media only screen and (max-width:960px) media query, set the position to relative.
#media only screen and (max-width: 960px) {
.header {
padding-bottom: 0;
position: relative;
}
}
Edit 1:
To keep the fixed menu, one option is to set the dropdown portion to be a position of absolute with an overflow-y.
#media only screen and (max-width: 960px)
.header .header__column--navigation {
margin-top: 80px;
position: absolute; //Added
min-height: calc(100vh - 110px); //Added: set the second parameter of calc to the height of your header. ex: https://c.flm.pw/2018-06/6oiip.png
height: 100%; //Added: Tell the absolute div to take up as much height as it needs.
overflow-y: auto; //Added: Make the absolute div have the ability to scroll, but hide the scrollbar when it doesn't.
}
}
Here is a simplified version of my layout:
<body>
[... a bunch of content ...]
<div id="modal-overlay">
</div>
</body>
body contains enough content that the entire page scrolls.
#modal-overlay is styled like this:
#modal-overlay {
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
In response to a user action, I'll display the #modal-overlay by setting display: block;
The #modal-overlay then fills the entire viewport.
Here's the trouble...
SOMETIMES, when you swipe vertically on the the #modal-overlay - its content scrolls as it should.
However, SOMETIMES, the body scrolls instead, and the content in #modal-overlay doesn't scroll at all. It's as if I'm scrolling body through the #modal-overlay, which is exactly what I don't want.
In fact - it seems totally random whether #modal-overlay or body scrolls in response to the swipe gesture.
I've read about a few hacks (e.g., applying overflow:hidden to the body but I don't want to do that, since it loses the correct scroll position, and causes other problems as well.) I also would like to have a solution that works with any number of nested layers. I'm really trying to prevent scrolling through the uppermost layer, not fiddling with the underlying layers.
This is particularly problematic on iOS, since scrolling the body reduces the height the browser chrome, which expands the viewport, which messes with the layout of the #modal-overlay, since it's sized to fill the viewport. Aaargh.
Thanks in advance for any advice or guidance!
I haven't tried to achieve this on ios, but you can at least give a try using slimscroll jquery plugin if you are dealing with multiple layers and prevent scrolling of other layers.
Hope it helps
Body should be overflow-hidden and
add dynamically position: absolute; width: 100%; height: 100% CSS properties to other content wrapper (content container inside the body) in the body when modal open.
When open the Modal
<style>
.modal-open {
overflow: hidden;
}
.modal-open .container {
position: absolute;
width: 100%;
height: 100%;
}
</style>
<body class="modal-open">
<section class="container">
Site content will be here
</section>
<div class="modal">
Modal content will be here
</div>
</body>
Add this "modal-open" class dynamically when open the modal and remove it when it close.
Since your modal-overlay covers the body 100%, I believe adding position:fixed to body when you display the #modal-overlay will fix your issue.
I am trying to implement a solution to prevent the iOS bounce effect in Safari for iOS when a web page content is larger than the viewport.
The page I am working on is quite specific in it's structure, and is very similar to this page http://new.salt.ch/
The basic structure is bootstrap-based.
It has a fixed navbar at the top.
It has a full-screen background slideshow.
The slideshow has an overlay that is fixed to the bottom of the viewport.
There is a footer element that loads off-canvas and is only visible on scrolling the content.
The content scrolls behind the navbar.
The content consistes of a title which is positioned 20px below the navbar and a series of buttons that are positioned 20px above the viewport.
When scrolling, the buttons and title all move up the screen to display the footer.
The problem I am having is the same as the problem on the page http://new.salt.ch/ in that when you scroll up, you get a bounce effect at the bottom of the screen and which reveals the background and overlay.
I have tried various solutions, including iNoBounce.js, Nonbounce.js and a few other suggestions I have found on SO.
I have the same issue always...when I try to disable the bounce, all scrolling gets disabled. I am guessing this is because the content (other than the footer) is always just large enough that the scroll is not needed, and so scrolling gets disabled and the footer no longer is accessible on scroll.
This code should stop the bounce as it's the HTML tag that bounces
html {
height : 100%;
overflow: hidden;
position: relative;
}
body {
height : 100%;
overflow: auto;
position: relative;
}
I went through a few answers on SO and things were looking bleak until stumbled upon this code.
html {
position: fixed;
height: 100%;
overflow: hidden;
}
body {
width: 100vw;
height: 100vh;
overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
The style declarations for body can be put on any element that you want to have the ability to scroll. You can also alter overflow-x and overflow-y as needed. I personally needed it to NOT scroll to the sides so I declared it as so.
update Sept 15 2017: I had to use this fix for another project and I was able to do without these declarations position: fixed and height: 100%; on the html selector. YMMV
Update April 12 2018 (mentioned in comments): If you're using fixed elements on the page, those elements may have a visual "shakiness" when scrolling.
If I'm interpreting your question correctly, we've been having the same issue for years developing cross-platform mobile web apps, trying to get all the different proprietary scroll features to work correctly on each device: Apple iOS, Google Android, Windows Phone, laptop Chrome, laptop Safari, IE, and laptop Edge.
jQuery Mobile continues to try and fix this within the confines of their framework, but it's too much whack-a-mole, with the constant updates from each device maker / OS maker.
Yes, we've got solutions for each individual mobile device. And we have tested, but not seriously considered developing device-selective paging frameworks for each device, requiring us to detect each device and present a slightly different framework for each. Insanely bad idea with basically maintaining at least 3 and really up to a dozen different versions of your code.
SOLUTION: We've had the most luck by just putting your persistent header and footer on top of your page framework. Here is the general solution using in-line styles for simplicity:
<html>
<head>
<title>Fixed Header and Footer on All Mobile Web Apps</title>
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
<style>
html, body { height:100%; width:100%; }
</style>
</head>
<body>
<div style="position: fixed; height:100%; width:100%; top:0; left:0;">
<div style="height:calc(100% - 1px); width:100%; margin-top: 60px; z-index: 1; overflow-y: scroll; -webkit-overflow-scrolling: touch;">
[Body Content That Can Scroll if it extends beyond screen...]
</div>
<div style="position: absolute; top:0; left:0; width:100%; height: 60px; background:#dddddd; z-index:10;">
[Header Content...]
</div>
<div style="position: absolute; bottom:0; left:0; width:100%; height: 40px; background:#cccccc; z-index:11;">
[Footer Content...]
</div>
</div>
</body>
</html>
So, the Body could be any jQuery Mobile set of pages. In fact, theoretically, the Body could be almost any content from any framework.
Special note, the line with height:calc(100% - 1px); is critical to the magic.
The seemingly infinite combinations or permutations of this issue have almost become a crusade for us over the years, trying to find the most pure, simplest, and most universally compatible solution. So after dedicating an embarrassing number of man-hours to this topic, this is not only our best solution, it's also the ONLY universally compatible approach we've found that also allows you to stick with just a singular code-base. It has been successfully tested on the latest versions of iOS, Windows Phone, Android, laptop Chrome, laptop Safari, PhoneGap, laptop Firefox, IE 9-11, and Windows Edge.
TAGS: mobile app, web app, fixed header, fixed footer, persistent header, persistent footer, scroll issue, iOS scroll bounce, Chrome scroll bounce, Android scroll bounce, webkit scroll issue, webkit touch scrolling, iOS touch scrolling issue
I used iNoBounce https://github.com/lazd/iNoBounce and it works perfectly!
If you need to allow horizontal scrolling as well, there is a pull request by santi6291 at https://github.com/lazd/iNoBounce/pull/36 which fixes that
For 2019 Safari on iOS 13, I was able to use this fix:
html {
overflow: hidden;
height: 100%;
position: fixed;
}
body {
overflow: auto;
height: 100%;
position: relative;
}
At least for me it covers most of the cases.
In my case, I wanted the address bar to disappear on scroll, and I get the bounce effect when a user scrolls, but this solved it for me
html {
position: relative;
overflow-y: scroll;
}
body {
overflow-y: scroll;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
I managed to solve most problems supporting overflow: auto and overflow: scroll on mobile Safari:
without hanging the scroll view after touching at the beginning of list, then moving down and then up (mobile Safari runs its default bouncing behavior for the whole page in that case)
support for fixed header / action bar on top without ugly overlapping of it by a scrollbar
window.addEventListener('DOMContentLoaded', function () {
var atTop = true;
var atBottom = false;
var body = document.getElementById('fixedBody');
body.addEventListener('touchmove', function(event){
event.preventDefault();
});
body.addEventListener('touchstart', function(event){
event.preventDefault();
});
body.addEventListener('touchend', function(event){
event.preventDefault();
});
var scrollingDiv = document.getElementById('scrollDiv');
if (scrollingDiv.scrollHeight <= scrollingDiv.clientHeight) {
atBottom = true;
}
scrollingDiv.addEventListener('scroll', function(event){
if (event.target.scrollTop === 0) {
atTop = true;
} else {
atTop = false;
}
if (event.target.scrollHeight - event.target.scrollTop === event.target.clientHeight) {
atBottom = true;
} else {
atBottom = false;
}
});
var lastY = 0;
var topLock = false;
var bottomLock = false;
scrollingDiv.addEventListener('touchmove', function(event){
event.stopPropagation();
var currentY = event.touches[0].clientY;
if (currentY > lastY) {
// moved down
if (atTop) {
event.preventDefault();
topLock = true;
}
if (bottomLock) {
bottomLock = false;
// TODO: Emulate finger remove and touch again here
}
} else if(currentY < lastY){
// moved top
if (atBottom) {
event.preventDefault();
bottomLock = true;
}
if (topLock) {
topLock = false;
// TODO: Emulate finger remove and touch again here
}
}
lastY = currentY;
});
scrollingDiv.addEventListener('touchstart', function(event){
lastY = event.touches[0].clientY;
event.stopPropagation();
});
scrollingDiv.addEventListener('touchend', function(event){
event.stopPropagation();
});
});
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
</head>
<body id="fixedBody" style="overflow: hidden;">
<div style="position: fixed; height: 64px; width:100%; top:0; left:0; background-color: green; z-index: 1;">
Header
</div>
<div id="scrollDiv" style="position: absolute; width: 100%; top: 64px; bottom: 0px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; background-color: white;">
<div style="height: 150px; background-color: blue;">
First
</div>
<div style="height: 150px; background-color: red;">
Second
</div>
<div style="height: 150px; background-color: green;">
Third
</div>
<div style="height: 150px; background-color: black;">
Another
</div>
</div>
</body>
</html>
The only caveat I have is that when user touches and starts moving down and then up, nothing happens (expected: the list should scroll down). But at least the method prevents "pseudo scrolling down" and not confuses user.
To overcome that last problem, it's necessary to emulate a touch end and then touch start event when direction changed (I've put "TODO" comments).
Update: it's possible to avoid using JavaScript code fix on iOS Cordova with DisallowOverscroll = true and WKWebView.
You just need to add a CSS property overscroll-behavior: none to your body
body {
overscroll-behavior: none
}
More details are in this article
I tried all CSS solutions to no avail. Either iOS still bounced certain divs when it reached the top/bottom or scrolling itself became really unreliable and buggy when working with fixed positioning. And so I came up with a JavaScript solution which is really quite simple that seems to have solved it for me:
function onTouchStart(e) {
// Save position of touch
console.log("touchstart");
const touch = e.touches[0] || e.changedTouches[0];
window.lastY = touch.pageY;
}
function onTouchMove(e) {
console.log("touchmove");
// Check user isn't scrolling past content. If so, cancel move to prevent ios bouncing
const touch = e.touches[0] || e.changedTouches[0];
y = touch.pageY;
if (y < window.lastY && e.srcElement.scrollTop == (e.srcElement.scrollHeight - e.srcElement.clientHeight)) {
console.log("user is trying to scroll down without anywhere to scroll to. Canceling propagation.");
e.preventDefault();
} else if (y > window.lastY && e.srcElement.scrollTop == 0) {
console.log("user is trying to scroll up without anywhere to scroll to. Canceling propagation.");
e.preventDefault();
}
};
document.addEventListener("touchstart", onTouchStart, { passive: false });
document.addEventListener("touchmove", onTouchMove, { passive: false });
For the ionic users -
a simple solution for me was using slot="fixed" on the child div of ion-content.
<ion-content>
<div slot="fixed" />
</ion-content>
css overscroll-behavior is now supported in iOS 16. If you are targeting > iOS 16 devices, to prevent the iOS bounce effect in Safari when a web page content is larger than the viewport, add the following CSS to the html root
html {
overscroll-behavior: none;
}
This is tested in Safari and WKWebview in iOS 16.
I'm developing a mobile website, and a full-screen image will appear as a floating-layer once the website is loaded.
Please see below........
A: My mobile website contains a lot of content which exceeds the windows height
B: After page loaded, a full-screen image appears as a floating-layer on top of the contents. The image exceeds the windows height
C: When user scroll down, he can see the lower part of the image, but not the website content. The bottom of the image should never detached from the screen bottom no matter how the user tries to scroll down
May I know how can I achieve C ??
Also, in situation B, sometimes the image may not exceed the screen height if the user is using a Smartphone with big screen, in this case, the image should be fixed at the top of the screen and not scrollable.
It would be better if all the above can be achieved by NOT using jquery. However, if it is a must, then it is still ok........
Many thanks.
While the general effect is doable with CSS only, you will probably need javascript to toggle the effect on and off.
The general idea is to use position: fixed and overflow: scroll on a layer containing the image, while the body has overflow: hidden. Under these conditions, you're able to scroll the contents of the overlay but not the body.
While this works on desktop, things are a little bit different on mobile where all of the content will be rendered despite the overflow: hidden on the body. A quick work-around is to apply position: fixed to the body as well. I don't know if this is intended behaviour, but it works fine in both Safari and Chrome on iOS.
Markup outlines:
<body class="no-scroll">
<section class="content">
/* content here */
</section>
<aside class="overlay">
<img src="img.jpg">
</aside>
</body>
CSS:
.no-scroll {
overflow: hidden;
position: fixed;
}
.overlay {
overflow-y: scroll;
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
display: none;
}
.overlay img {
width: 100%;
height: auto;
}
.no-scroll .overlay {
display: block;
}
With this you could use javascript to toggle the class no-scroll on the body. When it's there, the overflowing content is hidden and the overlay is visible. When it's not there, the overlay is hidden.
Here's an example of the effect (without the .no-scroll class and javascript, though, just to show that it works):
Full screen
With markup/CSS visible
Edit:
In the example above, I gave the overlay a semi-transparent background and gave the image inside of it a max-width of 100%. If you want the entire screen to be filled with the image, change the max-width to a regular width.
Edit 2:
As requested, here's a jQuery function to toggle the effect.
$(".close").click(function() {
$("body").toggleClass("no-scroll");
});
Just give a <button> or whatever the class name close and it'll toggle the effect on and off.
A client is opening our website into a popup window using JavaScript with window.open. They are turning off scrollbars and making the window a fixed height, causing the pages to not be scrollable. I control the code of the website being loaded this way, but not the calling JavaScript. Is there any way I can force the display of scrollbars?
body { overflow: auto }
should bring back the scroll bars. If it doesn't, and the scrollbar directive in fact turns off the body's scroll bars (I don't know right now whether that is the case), add a wrapper DIV in the body:
html, body { height: 100% }
.wrapper {
width: 100%;
height: 100%;
overflow: auto
}
<div class="wrapper"> ......