Simple Solution to Prevent Body Scrolling on iOS

Posted on 30 October 2019 , by Markus Oberlehner , in Development , tagged JavaScript

ads via Carbon

In my last article about building accessible popup overlays with Vue.js we used a simple technique to prevent scrolling in the background. I think that this little trick for preventing scrolling on the <body> element on all devices, including iOS 12 and below (finally, this was fixed in iOS 13 🎉) is worth taking a closer look.

Usually, we can use overflow: hidden on the <body> element to prevent scrolling. But unfortunately, that does not work on older versions of iOS.

In this article, we check out which possibilities we have to prevent scrolling in all browsers, including mobile devices like iPhones and Android-powered smartphones.

The most straightforward way for disabling scrolling on websites is to add overflow: hidden on the <body> tag. Depending on the situation, this might do the job well enough. If you don’t have to support older versions of iOS, you can stop reading here.

Another way of how to deal with this problem is to use the body-scroll-lock package. This is definitely the most bulletproof way how you can do this. But it comes with the downside of being a pretty complicated solution, which adds 1.1 kB to your final bundle.

Next, we take a look at a not very elegant but simple solution to this problem.

The simple solution for preventing scrolling on iOS

The final size this solution adds to our bundle is only 253 bytes, so significantly less than the body-scroll-lock package.

As you can see above, we use position: fixed in combination with storing the scroll position of the user so we can restore the scroll position after the fact.

There are certainly some downsides to this approach. If you change the size of the browser window while the scroll lock is active, for example, the scroll position does not get restored correctly.

Another thing we have to consider is that setting CSS styles on the body triggers painting in the browser. I don’t think this is a big deal in most cases. But if you need to lock and unlock scrolling very frequently (every couple seconds), this might hurt the frame rate of your application.

But the most critical caveat you have to keep in mind is that this approach changes certain styles on the <body> element. If you apply custom styles for overflow , position , top , or width , your styles might break when the scroll lock is enabled.

Furthermore, there might be some edge cases I didn’t think of, and the developers of body-scroll-lock have. But until this point, I got along pretty well using this approach on a couple of sites.

Like What You Read?

Follow me to get my latest articles.

Wrapping it up

It’s unfortunate that for a long time, only using overflow: hidden to prevent scrolling did not work on iOS. But with only 18 lines of JavaScript, we can work around the problem.

In the end, you must decide if it is even necessary in your case to support older versions of iOS. Luckily, iOS users usually do update very quickly.

  • Will Po, Body scroll lock — making it work with everything
  • WebKit Bugzilla,with overflow:hidden CSS is scrollable on iOS

Do you want to learn how to build advanced Vue.js applications?

Register for the Newsletter of my upcoming book: Advanced Vue.js Application Architecture .

Do you enjoy reading my blog?

You can buy me a ☕️ on Ko-fi!

Locking body scroll for modals on iOS

Revisiting old solutions for preventing background scrolling from within modal dialogs & overlays.

I’m a software engineer living and working in East London. I’m currently helping to build a one-stop-shop for the digitisation of alternative assets over at Daphne . Although once strictly front-end, today I work across the whole stack, including dipping my toes into DevOps and writing  Rust & Go.

Stop me if you’ve heard this one before:

  • You have a site with a fixed overlay, such as a modal.
  • You open the modal and scroll it.
  • By the time you hit the end of the modal (or even sooner), the background behind it starts coming along for the ride.

For most browsers, setting overflow: hidden on the body (once the overlay is open) is sufficient. Not so on mobile Safari:

Over the years loads of smart people, such as Ben Frain , have written about various solutions, ranging from CSS -only tricks to JavaScript event interceptors.

Let’s run through a few of them.

The sledgehammer: listening for touch move

Libraries such as Body Scroll Lock selectively block touchmove events using JavaScript. This mostly works, but feels very heavy-handed.

You also need to know up front which (if any) root element you want to retain scrolling on, such as the inner content of the modal itself. If you don’t get it right, you risk the user not being able to scroll the modal, or getting stuck.

Wishful thinking: overscroll-behaviour and touch-action

For a brief moment after iOS 13 was released it seemed like we finally had our answer: -webkit-overflow-scrolling . Ben’s solution sort of works today, but it’s very easy to still get into weird UI states:

After pinching and zooming, you can still scroll the rest of the page. Even worse, by the end of the above video I can’t move at all, rendering the page broken.

Revisiting a classic: position: fixed

The easy, works-everywhere solution is to use position: fixed on the body. Unfortunately, doing so famously resets the scroll position to the top of the page.

This might be fine if your trigger is at the top anyway, since the user will already be there (or close enough). However, if you’re invoking the overlay from further down the page, such as from sticky navigation, the user will lose their spot.

There are a couple of workarounds:

Top positioning

You can apply a margin or negative top value equal to the scroll distance from the top of the page. Then, when you remove position: fixed , you simply scroll the page to that point. It’s pretty seamless.

However things can get a bit weird if the user resizes the page, or changes device orientation. Since you can’t rely on the browser to reposition the scroll, you risk having an offset so large that it actually sends the entire content off-screen unless you dynamically adjust it.

Scrolling to the old position

An easier solution is to just scroll to the position, rather than setting an offset. The content can never be offscreen, since you’re not applying any kind of fake positioning or negative margin to keep it in view; you’re simply saving and restoring the scroll to what you think it ought to be.

It may be wrong after a resize, but should never result in a broken looking page.

Sticking with the classics

I still think it’s hard to beat position: fixed alongside programmatic scrolling:

  • You don’t need to know which elements need to retain scroll. If you want the overlay to be scrollable, it can be using overflow: auto .
  • You don’t need to worry about the contents flying off-screen due to an offset that no longer applies to the current viewport.
  • It works everywhere, including iOS, without any jank .

Here’s a brief example:

Check out the CodePen to get a feel for it:

See the Pen Overlay-scroll position overflow hidden by Jay Freestone ( @jayfreestone ) on CodePen .

The debug view is also available here if you want to try it out on a mobile device.

Let me know if you run into an easier solution!

Preventing body scroll for modals in iOS

Tl;dr solution for 2020/ios 13+.

Thanks to the comments below (and that’s why I love comments so much), we can put a few things together to have a working CSS only solution for iOS 13+:

You can have a play with that here: https://codepen.io/benfrain/live/wvayeWq

I have left the rest of the post un-touched from 2016.

Original post from 2016 follows

Suppose you are building something that pops a modal window from time to time. This probably works well in most places, the problem is, on iOS, even if you toggle:

iOS doesn’t prevent users from scrolling past the modal if there is content that exceeds the viewport height, despite you adding that condition to the CSS. One solution is to write the window.innerHeight into both HTML and body elements in the DOM and then toggle the overflow: hidden style on and off on the body and html :

You can get a similar effect by setting the body and html to position: fixed when you expose the modal and ditch the JavaScript there.However, neither of those solutions prevents users from doing the ‘elastic band’ thing at the bottom; and that subsequently reveals an ugly space. It would be nice if we had something better. My esteemed colleague, Tom suggested making use of touchstart to prevent the default scroll behaviour and while that solved the initial problem (being able to scroll past the modal) it prevented clicks inside the modal. But it wasn’t long before that approach led us to using touchmove instead.

We can use it like this — at the same time that you invoke a function to make the attribute change or class change that shows the modal, you also do this:

And when you want to allow scrolling again, you fire it like this:

Behind the scenes we then need two functions:

The function receives either true or false and subsequently adds or removes an event listener. We then need a simple function reference that disables the default behaviour of the touchmove event.

You can view a basic demo of this technique here: http://benfrain.com/playground/modal-demo.html . You’ll need to do further work if your modal contains a scrolling section but for basic modals this seems to solve the issue quite nicely.

I’d like a simpler CSS solution but this is a pretty light-weight way to get the job done. I welcome a better approach if anyone knows one?

34 comments:

Could you share details about your implementation? How is the dialog positioned in CSS? Is there an overlay? Do you use the standard element and API (with polyfill, ofc)? I’ve found a few demos of dialogs, but neither of them prevent body scroll, so I’d be interested to see a demo that does prevent it.

Hi Šime, I added a demo link at the bottom now. Thanks, Ben

Šime, not using standard API.

This is all well and good until the modal content is taller than the device window height. It prevents the scrolling of content in the modal. I know there is an argument that there shouldn’t be that much content in a modal, but sometimes there just is.

I think this is a fantastic solution, but maybe needs a little more expansion to account for this. I’ll definitely keep an eye on this, it’s extremely useful.

Goddamn it, I completely missed the part AFTER the demo. Sorry Ben, feel free to delete my comment.

Hey, did you find a suitable solution? My modal works fine with a overflow, but it’s not scrolling with -webkit-overflow-scrolling: touch; The scrolling without -webkit… feals really laggy and is a dealbreaker to me.

It still jumps on double tab below the modal :(.

I wish you didn’t point that out, otherwise this would’ve been the ultimate solution to this annoying issue.

I ran into something similar about a year ago when I was prototyping to improve the UX of elements on iOS. You can check out my solution here: https://jsejcksn.github.io/material-number-select.js/

I did a similar approach once but added one more thing to prevent the jump to top (I was using jQuery), right before adding the overflow: hidden, I store the scrollTop, the after the overflow, I add the scrollTop to the body. var scrollAmount = $(‘body’).scrollTop(); $(‘body’).css(‘overflow’,’hidden’).scrollTop(scrollAmount);

I also banged my head on this for quite some time, and I tried a LOT not only to prevent scrolling of the content underneath, but also to enable scrolling on the modal at the same time. My solution involves intercepting the touchmove event and cancel it when the modal is scrolled to the top or bottom. But still I have found no way to prevent it when you tap into those “magic” areas on the very top and bottom of the screen … you can see it in action on http://nordmedia.de when using the mobile navigation (yes, thats a modal as well)

Hi Frank. This is an amazing solution. I am trying to do a similar thing with a modal that has a scrollable text div in it. Could you share the basic code & CSS that solves this issue! I tried using ‘position:fixed;overflow:hidden’ on the parent window, but it still scrolls underneath the modal.

Hey!! Thanks for the tip. Works for me…just a little modification to the original solution posted here. Great Thanks again!!

There is a typo in the link to the webkit.orG bug for Touch-action css property support.

I checked your demo and unfortunately it does not work on Chrome for Android. Yes the post is about Safari iOS , I understand, just thought it might be OK for other people coming here to find out why it does not work there. It does indeed work on FF for Android.

The reason for that is that Chrome treats touch event as passive by default (to increase scrolling speed and scrolling tap response), FF for Android does not do this. https://www.chromestatus.com/feature/5093566007214080

Also https://developers.google.com/web/updates/2017/01/scrolling-intervention points out that, like you write, touch-action: none should be used and where this does not work, also, like you write correctly, the old method of preventing the default action for the event should be used in older browsers and mobile Safari (nice how they write older browsers and mobile Safari in one sentence – hint), though Chrome discourages that and rightfully so.

On another note, when I checked your site on mobile, in particular the navigation menu, (not sure if this is intended) the body does scroll up and down upon touch scrolling. Not a major thing though.

What really caught my attention though is that when I tap the search bar on mobile (FF for Android and Chrome for Android) the soft keyboard slides **over** the search input. This leaves the user with no way of seeing what they type to search for.

A simple solution to that would be to place the search bar at the top of your navigation menu and possibly have the Twitter, GitHub and RSS links show next to each other on one line instead of in a list under each other. Like this all menu items and the search bar could still be seen and interacted with without issues.

Btw, the email given for this comment works 100%, so if you like to get in touch feel free to do so, happy to hear back from you. I also subscribed to this entry so I will receive any replies.

Thank you for your write up! Well done!

Now I am left with the dilemma of not wanting to spend hard cash on Apple hardware or BrowserStack (live view is slow apparently) to test my sites in mobile Safari and desktop Safari, just to be able to debug this type of error or, like again you write so well in your new post, the hostile menu bar of iOS Safari.

To my knowledge there is no way to debug mobile or desktop Safari other than BrowserStack like tools or actually having Apple hardware, right? Yes of course a rouge Mac iso in a VM could do, been down that road but it is tedious and keeping that up to date is even more tedious plus the EULA is not happy with it. WHY does Apple make it so HARD for devs to write clean and 100% working sites for their trouble child called Safari? Bad move trying to even monetize development for their platform(s)!! Grrr!

I say, instead of putting AI chips in their latest phones and pads Apple should really get their act together and clean up their mobile and desktop browsing experience called Safari so us devs and users don’t have a heap of issues to go through to deliver a site that otherwise works great in all other major mobile and desktop browsers. Sigh..

Based on the post and other articles found on the internet, I have created a javascript function to solve this problem. I have tested it on Safari and Chrome on iOS and Chrome on Android. You can find a test case here: https://codepen.io/thuijssoon/pen/prwNjO and a gist here: https://gist.github.com/thuijssoon/fd238517b487a45ce78d8f7ddfa7fee9 . Please let me know your thoughts.

I struggled with a similar problem for 3 days, and the solution I found may help someone. My app has two main divs. The main one is for displaying pages and the other has the navigation links.

The main div allows vertical scrolling for overflowing content: overflow-y: scroll. Some pages have vertically scrollable widgets that don’t well, or are completely unusable, unless scrolling is temporarily disabled on the main content div.

Setting ‘overflow: hidden’ in css via a class didn’t stop the content div from scrolling because it was overridden by ‘overflow: scroll’ applied to the div using an id selector. I could see that this was happening using the element view in the Opera debugger. So I tried putting ‘overflow: hidden’ directly on the content div using the style attribute and it worked. Content area became unscrollable and scrollable widget worked perfectly. So I wrote a bit of javascript that wrote the css into the content div on touchstart and removed it on touchend. Job done. And working on every simulated iOS device I have tried so far.

See this fiddle for a working example. View in mobile browser.

its not working on iOS 12. Everything is scrolling 🙁

Do you mean my fiddle is not working 0lli0?

Confirm. Your demo is no longer working on iOS 12.1 Safari.

Your demo site is not working on iOS 12.1. Any other suggestion?

That’s a real bummer. Can I just check that you are looking at the right thing? (The original link seems to have gone from my message, so I am wondering…)

https://fiddle.jshell.net/daffinm/fwgnm7hs/16/show/

I am referring to modal on iOS. The demo site I tried is http://benfrain.com/playground/modal-demo.html The link you posted in your comment was about a veritcal slider, which was not what this article about, right? or do I misunderstand anything?

In my case I wanted to display a fixed-position modal fullscreen. And solution seems very simple: setting “overflow:hidden;” along with “height:100%;” on both html & body tags. The “height:100%;” is key here. That alone solves the scrolling problem, no javascript needed. Only modal content was scrollable, body was not scrolling at all. Tested on iPhone X Safari/Chrome, iPhone 8 Safari/Chrome, iPhone 7 Safari/Chrome (all via browserstack). Of course on Android Chrome it works flawlessly.

This did NOT solve the problem in my case. What i did was:

body.no-scroll, html.no-scroll { overflow: hidden; height: 100%; touch-action: none; }

The key seems to be “touch-action: none;” on iOS 12.1.x

You sir, are a genius. “touch-action: none;” does also solve it on iOS 13.

You also need to set “-webkit-overflow-scrolling: auto;”. if not set, scrolling to “negative” will invalidate “touch-action: none” and you will be able to scroll the body.

This no longer works on iOS 12

In my case i just had to add

overflow-x: scroll; -webkit-overflow-scrolling: touch;

To the modal only not to the body in order to be scrollable, without affecting the background content

This works, although for `-webkit-overflow-scrolling`, `none` is not a valid value. See https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling . Possible values are “auto” | “touch”

If like me, you spent hours trying to find a scroll solution for Safari 10, I found it and made a simple React hooks version here: https://codesandbox.io/s/react-modal-scrollable-safari-10-friendly-3xzzy

2021 update with iOS 14+ All solutions mentioned are not working as soon as the browser bars in Safari disappear. So if you already scrolled the page and then open a modal, the scroll event will bubble as soon as you reach the top or bottom of your modal scroll. This is also an issue if you have a PWA in standalone mode added to the homescreen where there are no browser bars at all, and therefore no way to block this behaviour.

I made a demo to replicate the issue. It even has a manifest.json so you can add it like a PWA in standalone mode to your homescreen.

https://5n944.csb.app/

This is so dumb, I also noticed that my modal works perfectly when I set the url bar to be at the top, and nothing helps when it’s at the bottom.

This helped me on Dec 22, 2023 thank youuuuuuu

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.

Understanding native JavaScript array methods

The ios safari menu bar is hostile to web apps: discuss.

A rather geeky/technical weblog, est. 2001, by Bramus

Prevent overscroll/bounce in iOS MobileSafari and Chrome (CSS only)

UPDATE 2017.12 : For non-Safari browsers (e.g. Chrome, Firefox) you can use overscroll-behavior to solve exactly this. Simply apply overscroll-behavior-y: none; on html, body and be done with it.

Safari however does not support it …

UPDATE 2021.06 : The workaround below no longer seems to work in recent iOS/MobileSafari versions (iOS 12+) … 😭. Follow Webkit bug #176454 to stay up-to-date on support in Safari.

Know this bouncy overscrolling behaviour that browsers have been doing whenever you reach the “edge” of the page its contents?

Sometimes – in fullscreen apps for example – you’ll want to disable this. Now, there’s no need to resort to JavaScript and hijack touchstart , as the little CSS snippet below can prevent the rubber band scrolling:

Tested with iOS8, iOS9, and iOS10.

However, this snippet disables *all* scrolling on the body . If you want to retain scrolling on your page (but now without the overscroll effect) , you need to make use of a scrollable wrapper that spans the entire window/screen and which wraps around your entire content. Like so:

You’ll most likely want to remove the margin and padding from the body too in that case 😉

Note that your mileage may vary. Other pure CSS solutions do exist (untested though)

🔥 Like what you see? Want to stay in the loop? Here's how:

  • Follow @bramus on Twitter
  • Follow @bramus on Mastodon
  • Follow @bramusblog on Twitter
  • Follow bram.us using RSS

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997) , he fell in love with the web and has been tinkering with it ever since (more …) View more posts

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

' src=

30 Comments

Not good enough.

Don’t know what you’re talking about this worked fine for me

body, body {} what does it mean, is it a mistake

A mistake indeed. Should’ve been html, body

Worth pointing out that this prevents *any* scrolling when viewed in a browser.

That’s correct. I’ve updated the post to include instructions to using a scrollable wrapper around your content. That way overscroll on the body is prevented, but scrolling of the content is maintained.

Any way to make this work with a site that’s been “Added to Homescreen”?

I can’t seem to find anything :[

This should work fine for a PWA. At least on ios 13 it is working perfect

While this works fine for a web page in a browser, it doesn’t seem to help with a Cordova hybrid app.. When I run my app, it’s almost like the WebView component itself is scrolling as opposed to something within the “html” element. I confirmed this by selecting the “html” element in VS2015’s DOM Inspector and watching the location of the DOM highlight while scrolling.

All the solutions I’ve found end up disabling momentum scrolling completely, which results in another issue where iOS can’t scroll a page if the tap-drag starts on an “input” field.

So, does anybody know of a way to get around this?

Does anyone agree that iOS is absolute garbage for this!!

Now all my absolute elements bounce like hell :))

  • Pingback: Customizing Pull-to-Refresh and Overflow Effects with CSS’ overscroll-behavior | Bram.us

thanks a lot. worked perfectly for me

thanks works perfect

i did following this guide but it still have bouncing effect when scrolling to bottom or top viewport ?

  • Pingback: Scroll Bouncing On Your Websites - ugurak

this destroys the whole website you dumbass

Not if you know what you are doing … or if you’ve read the entire post …

Thanks, It worked perfectly for me on chat page:)

Because you didn’t give position relative to their father’s elements.

Best answer. it worked for me.

What if content is more than 100vh

  • Pingback: 如果使用-webkit-overflow-scrolling,有时div滚动会冻结|ios问答

This works for me, but it creates some scrolling lag (in osx safari at least) sooo…no good 🙁

Thank you so much, it works as expected

This works but since the body is no larger than the screen size, the address bar will not minimise when scrolling down, not important for a lot of people but I’m working on a webapp and I realised this halfway through

It’s a shame we still need to fiddle with non-working workarounds for this in 2021. There is a standard CSS property out there, implemented by actually all other browser vendors but Apple simply refuses to adopt it…

I could not agree more.

Perhaps Apple deliberately keeps Safari in this state so that folks are forced to use Apps on their devices.

Although, Apple is adding support for overscroll-behavior in Safari 16 but this solution works for older Safari versions. Thanks!!!1

I will write my solution, I hope my method will help many people.

Css: html{ overscroll-behavior: none; } body { overflow-y: scroll; }

Leave a comment

Cancel reply.

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.

This site uses Akismet to reduce spam. Learn how your comment data is processed .

Level up your Python game by crafting a polished portfolio with Flask in our workshop.

Join our free community Discord server here !

Learn React with us !

Get job-ready with our Career Toolbox Track's career advice, resume tips, and more 🧰

🤖 Check out all our AI content here 👀

Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here .

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

ios safari body overflow hidden

Sunil Thammineni Mahendra Chinna

Overflow: hidden not working in ios safari..

I have a home page where there will be a popup modal that comes up on the page, and at lower viewports the content on the pop up will have overflow:auto, but at any viewport and device, the background page ( home page) should not be scrollable when the pop up is open, I have added overflow: hidden to the body when there is overlay open, overflow : hidden is working in all devices and browsers except in ipad/iphone safari versions. Researched a lot about this issue, but not able to find the correct solution.

can someone please help me ?

Jennifer Nordell

Jennifer Nordell

I don't know how accurate it is, but it might be something to try. I found on stack overflow a post that specifically says that iOS safari ignores overflow hidden when applied to body or html. They seem to be able to have worked around this by putting everything inside a wrapper div. Might give it a shot. Here's the reference. http://stackoverflow.com/questions/14270084/overflow-xhidden-doesnt-prevent-content-from-overflowing-in-mobile-browsers

Sunil Thammineni Mahendra Chinna

Thanks for the reply! I have already looked into that post, but that did not work out.

Sunil Thammineni Mahendra Chinna if you find the answer, let us know. This is interesting and I'd love to know a solution :)

Well, I have tried a bunch of solutions but in vain, overflow: hidden with position: fixed is doing the job, but position: fixed will distort all the elements on the homepage. I saw few articles suggesting position: relative with overflow: hidden, but that doesn't work.

Still looking for a fix! is there any alternate for overflow:hidden ?

Corey Huddleston

Corey Huddleston

As mentioned by Jennifer, Safari overlooks "overflow: hidden" when applied to HTML and BODY in CSS. You should target the element selector more directly in CSS, specifically the one that has the content that is overflowing, and use "overflow: hidden" on that element. In my testing Safari does not overlook that, and it fixed my issue.

Posting to the forum is only allowed for members with active accounts. Please sign in or sign up to post.

New York, NY

Los Angeles, CA

Design & Development

Disable Body Scrolling For Open Modals on iOS Devices

When overflow: hidden just isn’t enough.

iPhone Rage / © chaffflare / Adobe Stock

Stacey Marks , Engineer

Feb. 18, 2020, the problem.

Are you creating a website that has a full page modal and want to stop pesky body background scrolling when the modal is open? Did you realize the method that pretty much works everywhere doesn’t work for Safari on iPhones?

Did you google how to fix this?

Did all of the answer pages point you to the body-scroll-lock package?

And when you tested the most recent version of it on a phone, it didn’t work?

THE SOLUTION

After scouring the internet for way too many hours, more than I care to admit, I found two solutions that worked for what we needed.

1. Stop everything from scrolling, both body background and everything inside modal. 2. Stop the background from scrolling, while allowing content inside the modal to scroll.

THE FIRST EXAMPLE - Freeze Everything

The first block of code is checking if we are indeed in Safari on an iPhone, otherwise we run what works for literally everything else.

The second block, we are checking if the modal is open, then run what’s needed to stop everything from scrolling.

Easy enough, right?

But wait! What if I actually need to be able to scroll inside my modal?

We got you covered.

ios safari body overflow hidden

THE SECOND EXAMPLE - Allow Scrolling Inside Modal

For context, this is all in the modal component and only gets called once the modal is actually open. In addition,  the _.includes is from the Lodash library, which would have to be imported at the top of your component.

So, because the site is built using Gatsby, we had to take advantage of useEffect in order to access the DOM element to grab the document.

We grab #terms-text-container , which is the container that contains (ha) the modal’s text, not the entire modal itself. We are just targeting the div that we want to be able to scroll.

After checking if we are in mobile iOS Safari, we want to grab all the nodes (the text, in this case) inside #terms-text-container and add them to the insideTextModa l array, which you will see being used later. If we don’t do this and your finger hits a piece of text, it will register as the text <p> but wont register as #terms-text-container and disable scrolling.

Then we add the event listener. Because it's a phone scroll the action is touchmove and we call our method handleTouchMove below, which is defined just below.

Within handleTouchMove , if the event.target ( what our finger touches and moves) is either NOT the #terms-text-containe r, or anything inside of it, which is represented with the array insideTextModal , we preventDefault , aka stop it from scrolling

And once the modal is closed, we remove the touchmove event listener, and that’s it!

OTHER (PERSONAL) TAKEAWAYS

Removing Event Listeners In order to properly remove an event listener, what you pass to the removeEventListener must be the same as what you passed into the addEventListener .

QA’ing your iPhone Specific Work When testing iPhone specific situations like this, the issue may not be able to be replicated in the browser, even when using responsive mode. We were only able to see this issue when testing on an iPhone itself, or later, using the Xcode simulator, which was a life saver!!

Have a different way to solve this issue? Please let us know!

Ahmad Shadeed

Prevent scroll chaining with overscroll behavior.

Let’s suppose that we have an element with position: fixed . If it has scrolling, you will notice that when you reach the bottom boundary of the element, the browser will continue scrolling on the body element. It’s an unwanted and confusing effect. Thankfully, we can fix that with CSS.

In this article, we’ll go through the overscroll-behavior CSS property, what’s the problem it solves, how it works, and where we can use it.

Introduction

Let’s take a very common problem. We have a modal dialog that is positioned in the center of the viewport. Underneath that modal, there is the actual web page content.

ios safari body overflow hidden

When we reach the end of the modal content, the browser will continue scrolling on the main page’s content instead. That is called scroll chaining . It’s a default behavior that can be overridden now with a new CSS property called overscroll-behavior .

ios safari body overflow hidden

This behavior is often not needed and can distract the user from focusing on the modal content. In the figure above, notice how when we reached the end of the modal, the page continued to scroll.

The old, hacky solution

We used to fix that by applying overflow: hidden on the body element via Javascript. When a modal is opened, we add a class to the body that is responsible for applying the overflow.

This solution used to work perfectly across desktop browsers, but Safari on iOS didn’t like that. To make it work, we also need to add position: fixed to the body element.

This works, but it will cause the browser to scroll to the top, distracting the user from the task at hand. That is a fix that introduces other problems. I’m not aware of a solution for that except for the pinned one in this article by Ben Frain.

According to the snippet in Ben’s article, the following should be added into the <body> element once the modal is active.

I remember working on a client project in 2018 where I told him that we can’t prevent the body scrolling for the mobile menu on iOS. He replied:

Nothing is impossible in programming. I’m sure there is a solution.

“Programming” refers to CSS in that case, in case you’re wondering. I kept trying and researching a lot, but without a perfect solution. I wish to go back in time and introduce the overscroll-behavior to the solution.

Introducing overscroll-behavior

The overscroll-behavior property sets what a browser should do when we reach the boundary of a scrolling area. It’s supported in all major browsers except Safari. The property is a shorthand for overscroll-behavior-x and overscroll-behavior-y .

Note that setting overscroll-behavior will set both axes. Let’s get into the possible values.

overscroll-behavior: auto

The default value is auto , which allows scroll chaining. When we have a scrolling element and we reach the boundary of it, the browser will continue scrolling on the <body> content.

ios safari body overflow hidden

And here is a video that shows it better:

overscroll-behavior: contain

From its name, the value contain will contain the scroll within the element boundary. In the figure below, the scroll is contained within the blue outlined wrapper.

ios safari body overflow hidden

overscroll-behavior: none

When none is set, it has a similar effect to contain , ** in addition** to prevent the bounce effect when the top or bottom of the page is reached.

Use cases and examples

Now that you have an idea of how overscroll-behavior works, let’s get into some useful use cases.

Mobile navigation

When the mobile navigation is too long, scrolling too fast can cause the issue of scroll chaining on the body content. In the following figure, we have a long, scrollable navigation list.

ios safari body overflow hidden

If the scroll isn’t contained, it will cause the body content to scroll too. We can avoid that

Let’s see it in a video. I added a semi-transparent background for the navigation to see underneath it. Notice how it behaves when I toggle off the overscroll-behavior-y property.

When toggled off, a body scroll will happen.

An interesting thing to keep in mind though is that when the navigation is short (and thus isn’t scrollable), and a user tried to scroll for no reason, the body will scroll, even if overscroll-behavior-y: contain is set.

Unfortunately, I’m not aware of a fix for that other than the “hacks” introduced at the beginning of the article.

Side navigation

Another useful usage for overscroll-behavior is when you have a sidebar and main. In that case, the sidebar is fixed and it might be scrollable if its content is too long.

ios safari body overflow hidden

To avoid scrolling the main section when reaching the end boundary of the sidebar, we need to add the following:

Chat component

ios safari body overflow hidden

Inspired by Facebook, the chat component is a perfect use case for overscroll-behavior . We can use it to avoid the body scrolling when we reach the end of it.

Modal content

The first example I went within the article is modal, so I won’t repeat it. However, there are cases where we might need a list within a modal, and when that list is scrollable, we don’t want scroll chaining to happen.

ios safari body overflow hidden

Notice the list of options in the modal. This is a scrollable list. If we reach the boundary of it, the modal content will scroll. To avoid this, we can use overscroll-behavior .

Horizontal list

I spotted this use-case on Facebook’s home page. There is a section that has a list of users, and it scrolls horizontally as you see in the figure below.

ios safari body overflow hidden

This is a good usage for overscroll-behavior-x .

CSS overscroll-behavior is a useful property that solves an issue we used to hack around years ago. I hope that you learned something new from this article.

Do you know other useful use cases for it? If yes, I would love to hear from you on Twitter @shadeed9.

Enjoyed the read? If you'd like to support my work, consider buying me a coffee. Each article takes about 10 cups to create. Thanks a latte!

Support me on Ko-fi

Subscribe to my newsletter

A place where I share all the awesome CSS articles, demos, and updates that I like.

  • | New Account
  • | Log In Remember [x]
  • | Forgot Password Login: [x]
  • Format For Printing
  •  -  XML
  •  -  Clone This Bug
  •  -  Top of page

Momentum Scrolling on iOS Overflow Elements

Avatar of Chris Coyier

Web pages on iOS by default have a “momentum” style scrolling where a flick of the finger sends the web page scrolling and it keeps going until eventually slowing down and stopping as if friction is slowing it down. Like if you were to push a hockey puck across the ice or something. You might think that any element with scrolling would have this behavior as well, but it doesn’t. You can add it back with a special property.

Hey Chris, Is there a way to prevent the momentum scrolling on desktop (using a magic mouse or trackpad) as well as on iOS? I have been searching for a long time, but without luck.

Hey David, you can implement a scroll debouncer that can limit to only 1 scroll event being run every few milliseconds. Thing is, it will be far from perfect and in several cases may feel pretty unnatural.

Perhaps I’ll write a simple tutorial for this.

Why would you ever want to do that? That’d be the most annoying thing.

You could check for touch events with modernizr or something like it, and then only apply the rule in certain cases.

Checking for touch events is unreliable. Too many false positives and even at Modernizr they don’t recommend it.

Hey, you could try to use @media feature to see the size of your screen and if it is so small then the code works, but if it is bigger than x value it will not work, for example:

That will only apply to a screen size max of 768px. That’s the simplest way, and it is how many websites do it

if i use -webkit-overflow-scrolling:touch, when page are scrolling, i tap a button to reset the container of scrollTop = 0 , in ios or android, it can stop scrolling but it can’t go to the top of container

Hi this seemed to work really well until Feb 2019 – when there seems to be some rendering issues – anyone else encountered this?

Chris, momentum scrolling doesn’t seem work inside an iframe. I’ve tried several settings, I’ve also tried to iframe in the demo,

http://playground.johanbrook.com/css/touchtest.html

But it doesn’t produce scrollbars 99% of the time. Looking into a hack.

Chandra, did you find a solution to touch scrolling on iframes? I’m getting mad after the same problem…

Yeah, it doesn’t work on iframes. If you wrap the iframe in a container and put the -webkit-overflow-scrolling: touch and overflow-y: scroll on that, it should work. See this Codepen .

Kristie, thanks for the codepen example – it totally solved our problem!

Thanks this works perfectly! Do you have anything to resolve the milisecond lag when clicking on a link on a touch device?

I’m currently using webkit-touch-scrolling to increase performance the performance of scrolling in iPad and iPhone web applications. It has significantly increased performance, but just like most applications that use a timeline like environment with images, the iPad runs out of memory when a lot of ’tiles’ have loaded.

Is there any way to track the scroll position when momentum scrolling? Much like the ‘touchmove’ and ‘scroll’ event? So far I have seen lot of no’s.

My current solution would be to set a variable when the user starts scrolling and set a setTimeout() to an X amount of milliseconds to ‘simulate’ the scroll function.

This is just a little side note, the reason you run out of memory is because apple will only allow you to have 10 mb of ram with web applications. Why would they let you create native like applications on the web? If you could, there would be no reason to pay to be in their app store. We need to hold them accountable.

Had the same problem as you, iOS safari crashing. I debugged this for over a day, because I was sure the memory crash was due to hundreds of Angular hg-repeat elements(empty ones, which loads images upon scroll position).

The funny thing is, when debugging this in iOS Simulator, it doesn’t occur! Another funny fact is that I went from a menu with position:fixed, because the menu pops down when a input field is activated(rendering halts upon window resize), but at the same time changing the input field to a select field which doesn’t pull up the keyboard. So all i all, I just implemented a bug which wasn’t even needed, argh!

For others that might search for this, I will include that the browser crashed upon invoking $location.hash(id) and $anchoScroll().

I tested your demo on Windows Phone (IE10) and both divs. scrolled kinetically.

This is only an issue with iOS. Both Android and WP behave properly without the use of ridiculous proprietary styles.

Exactly what I needed, Chris!

And I think you might be from the future , because you always seem to foresee what I’m going to need next. Very appreciated, man!

So incredibly helpful. Thank you! I was wondering how I managed to accidentally disable the momentum scroll.

Thanks! So relieved CSS fixes this!

Every time I try this style out, some of the content on some of my pages just disappears –– has anyone else had this problem? The elements that disappear still take up space (so it’s not like they’re display:none; ) I can see the content when I look in Chrome’s inspector window, and the yellow indicator points to where my content is supposed be, and it basically points to these invisible divs that have height:0px;

It’s definitely these “momentum scrolling” styles causing this. I’ve tried only deleting those styles, and my content comes back, but then goes poof when I add the them again.

Because I wanted the whole page to have the momentum scrolling, this is how I did my styles:

Any ideas what I might be doing wrong? Any thoughts much appreciated.

Oops – nevermind! Tried adding the style to html instead, worked like a charm. Duh

Yup, you got it right Annie: thanks! It works like a charm. :)

Hey! I sat next to you at the jQuery conference in San Diego! lol

Thank you Annie! I’ve fixed my issue! Change the -webkit-overflow-scrolling to none before to change overflow to hidden so I can prevent the disappearing :)

Thanks buddy!

I noticed that using overflow-y: auto; (from Chris’s example) also works. Tested in the native browser and Chrome of a Samsung tablet with Android 4.0.4 and also on iPad with iOS 6, Safari + Chrome.

overflow-y: auto; is working on all browser for me but not working on iPad. I am using following Style:

.m-scrollable-filter { overflow-x: hidden !important; overflow-y: auto !important; -webkit-overflow-scrolling: touch !important; }

pls give me any response.

Chris said in the article that overflow-y must be set to scroll for -webkit-overflow-scrolling: touch to work. So use this code instead (there’s no need for !important ):

Anyone have this associated issue and know how to prevent it (or what I’m doing wrong)?…

I have a div within a wrapper div which specifically allows horizontal scrolling but prevents vertical scrolling. Everything is fine except that on ipad it remains possible to vertically lift the entire div, which then bounces back on release. What is more annoying is that this then allows the same div to ‘drag’ diagonally with a diagonal swipe. Here’s the page in progress…

http://raewilkinson.scratchthesky.com/

I fixed this issue with javascript that prevents the default scroll-bounce behaviour in the vertical axis. It works but it’s not ideal. I still have no insight into how the entire div could become ‘dragable’ in every direction.

I had the other case where the content was scrollable in vertical direction. I had this style applied:

Even when there were no need for horisontal scroll, it behaved like if you drag horizontally it moves the content sideways and bounce back. I solved this by doing:

This cause momentum to be used only in y-direction, since momentum is disabled when setting overflow to auto.

You can set it the other way: overflow-y:auto to only use momentum in x-direction.

If I add this: overflow-y: scroll; /* has to be scroll, not auto */ -webkit-overflow-scrolling: touch; to the html, or body, or any major block of my website I get no scroll improvements and I cannot tap on the top bar to scroll to the top. Any idea why?

You don’t need to add it to html or body on your webpages; Apple uses momentum scrolling on all pages automatically. -webkit-overflow-scrolling: touch is just for scrolling other elements on the page (only being able to scroll, say, a p ). Also, touch scrolling gives no “scroll improvements” — it just lets you flick your finger to quickly get through something long.

Thank you very much for this, exactly what i needed for fixing the scrolling issue on ios.

Hey — Just thought id take a second to say THANK YOU for this … as much as it seems i should have already known about the -webkit property, had i not seen this i may very well have gone down a dark road trying to implement a javascript replacement for overflow-y … THANK YOU SINCERELY

Just a warning for folks out there finding this great feature – if you are using it in a rule that also includes overflow-x: hidden – beware. Because the -webkit-overflow-scrolling: touch property will allow the user to scroll vertically and horizontally , which may reveal hidden divs that are meant to be hidden to the left or right.

I am facing this exact problem. I loose the vertical momentum scrolling as soon as I add overflow-x: hidden to the html and body tag. Adding -webkit-overflow-scrolling: touch restores the vertical momentum scrolling but breaks my overflow-x: hidden . Is there a way to get away with both? I tried to place the two styles at different places, in the html tag only, in the body tag only, in a wrapper div, always with no luck.

I believe that now (Aug 2018) this is not an issue. I have a website that acts like a full-height app, using position: fixed for full height content blocks that transition into one another, and one of them has overflow-y: auto for when it overflows. I added the -webkit-overflow-scrolling: touch property, as well as the overflow-x: hidden to this block, and it seems to be fine. It definitely helped the scrolling issue we were experiencing in older IOS phones (iPhone 6), and we don’t notice any other side effects (although I’m not sure if I have any content that overflows the left and right). I’d also like to point out that this is working even with overflow-y: auto , despite the code example in this post mentioning it only works on scroll (perhaps now that it’s 2018, IOS has resolved these issues).

Does any one know how to add a auto scrolling function to a horizontal scroll bar???I have some images in a table row and i want that row to auto scroll horizontally but not manually.. Please help me

I have customize scroll using CSS3, its working fine in all browser but the overflow-x is not visible in iphone. Here is the code what I have done. http://codepen.io/anon/pen/aLwhq

works great on ipad, thanks for the tip.

Thank you, this fixed a problem I was having and now the iPhone page looks like it should!

Okay, so when I have overflow-y: scroll; and -webkit-overflow-scrolling: touch; it works fine in most cases… except on the DIV I actually need it.

It’s a fixed DIV. When I load the page in landscape, and there is enough content to need overflow it works, but if I then change the device orientation to portrait (where the content doesn’t need overflow) then back to landscape the DIV no longer scrolls at all!

Please reply on this comment to above comment so I get notifications :P (I wish there were a way to edit such preferences on here!)

Hi Charles, I am also having this same issue, have you got any solution for this?

This was a great find — worked as promised. The only side effect is that child elements with position: fixed now move with the parent’s scrolling, until the momentum stops, and then they snap back into their fixed positions.

Did you ever find a solution to this? I’m having the same issue with a position:fixed navigation header div positioned on the top.

I’m having a similar issue. Fixed child element in container that that has “-webkit-overflow-scroll” moves while scrolling, and in addition, it’s hidden (I think the latter issue is related to z-index being changed when “webkit-overflow-scroll” is applied.

I can confirm that overflow: auto; -webkit-overflow-scrolling: touch; Works if you don’t want to show scroll bars all the time. Thanks for the tip, Chris!

Thanks for the tip Chris; overflow: auto; -webkit-overflow-scrolling: touch; works fine for iPhone media query; but when I used it within media query for iPad (1,2), it was causing the iOS app that was loading the page through iframe, to crash.

It works great for me. But I have another problem. While creating custom scroll, I’m unable to get smooth scrolling. Did I miss something?

Saved my day! However why I would need to add this in the first place is beyond me

Saved my week !!!

Thanks Chris, just made my day too!

Bundle of thanks!

Dear your technique works for me. i was facing problem in scrolling on touch devices, then i give your code to“`

body {overflow-y:scroll}

“` now my website scrolling smooth now.

Wow, that was a life saver.

Why in the world would you disable inertial scrolling by default? Who thought “This developer clearly wants this thing to be scrollable, but he probably wants it to be janky, so let’s make that the default.”?

why overscroll scroll?

Thanks for this, just saved me a few hours. Thanks also to the keywords that got me here.

Crazy how specific some of the controls can be.

Love it! Worked Thanks! :D

You just saved me hours of futzing around. Again. Thanks!

Thank you verry much! Keep on going with this good work, great article!

Words cannot express how grateful I am for this. Thank you!

I have a strange problem. I’ve tried looking everywhere and I can’t find any answers.

I’m using this CSS property ( -webkit-overflow-scrolling ) in an iOS app loaded inside a web view.

As soon as I open the app, it crashes and closes again. The crash report lead me to this page: http://bit.ly/1GobVK2 which suggests that it is caused by this CSS property. Is that legit? Should I continue to try and fix this based on this theory?

Has anyone else had similar issues with this CSS property?

just wanted to say, it actually does work with overflow: auto; , I have it set up and working on a few sites. Thank you for this snippet – don’t know why, but I keep forgetting this exact declaration and come back every time I need it again. ;)

This is really helpful. I was struggling for this issue and after applying -webkit-overflow-scrolling: touch; all the things stated moving smoothly. Thanks man for this :)

Hi, I’m a real tech dummy, so can you please help me and put it really simply like go to settings, go to x, then x, etc. my iPhone 5s screen keeps scrolling and it’s driving me nuts! The screen is clean, it’s a year old. I’ve tried turning it off and on again, no joy. How do I fix it? If it’s a memory thing should I delete some aps? Thanks! :-)

So yeah, didn’t read that it doesn’t work on “auto” and then tried it… Works on auto for me using Cordova on iOS so it might have changed, just a heads up.

https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-overflow-scrolling

There seems to be a warning on the page to not use ‘-webkit-overflow-scrolling’ on production facing sites.

Thank you! This worked perfectly!!

What a nice solution, it worked perfectly! Thanks!

Hi – when i use -webkit-overflow-scrolling on a div on ios 9+, cordova app it only activates scroll on 2 finger drag. any ideas on a work around?

I know that this is an old post, but…

“Web pages on iOS by default have a “momentum” style scrolling where a flick of the finger sends the web page scrolling and it keeps going until eventually slowing down and stopping as if friction is slowing it down. Like if you were to push a hockey puck across the ice or something”

…I believe this statement is actually false. Take any web page, including this very website, and scroll it in mobile Safari. It will not have inertia scrolling. Scrolling stops almost immediately when you lift your finger. Inertia scrolling at the page level isn’t a default at all.

Unless of course I am crazy and am the only person getting these results. Tested in iOS 7, 8 and 9, on 3 devices.

My finding is that you actually do have to use webkit-overflow at the document level. And because most sites don’t, they are seriously missing out.

Helped me a lot. I went from scrolling divs to scrolling page, so now it works on iOS. Has worked perfect on android from the beginning.

I love you!! just what I needed. you saved my app.

It seriously makes a world of difference to enable it, since it really isn’t a default. More info here:

https://ferdychristant.com/the-world-s-most-overlooked-web-optimization-for-ios-d88c7517d520#.r6pijsei7

This fix no longer seems to be working. – Is anybody experiencing the same issue? – Does anybody know how it could be fixed now?

Seems to be working on my iPhone SE, just not on my desktop Mac.

is there a way to make momentum scrolling work and hide the scrollbar permanently ? I tried using ::-webkit-scrollbar et all – looks like they are not compatible with -webkit-overflow-scrolling: touch;

Does anyone know how to get topbar and bottom bar for iphone to hide? In iOS 9 it is standard, but with this scrolling that standard disappears..

You saved me again. Thanks!

Try this it’s working for me

if (window.location == window.parent.location && navigator.userAgent.match(/(iPod|iPhone|iPad)/)) { $(‘#orderpop’).attr(‘style’, ‘-webkit-overflow-scrolling: touch !important; overflow-y: scroll !important;’); }

What about other prefixes, -moz-, -khtml-, -o-, -ie-, or is this specifically for safari only? Does this need to be done for android too? What other prefixes would be needed for those?

Thid demo and in my project don’t work on iPad(MD329LL/a) with 7.0.4(11B554a)

Thank you for this. I had such a hard time even finding the right thing to call this that I almost gave up on it. Amazing how little it’s actually used in things like responsive menus. I’m even finding paid plugins and whatnot that don’t even use this.

It works fine for scrolling but lazy loading stopped working. After touchmove event the is still scrolling and unable to load the images. How to fix this?

This no longer requires overflow-y to be scroll – it can be set to auto and have the intended effect only once the content goes over the element’s bounds causing a scrollbar.

overflow-y: scroll; /* has to be scroll, not auto */

I could be mistaken, but I don’t think this is true? I have overflow-y: auto set and momentum scrolling seems to be working fine in iOS.

Some things are just that easy. After hours of debugging and searching i found this simple solution… and it even works in cordova/phonegap. Next time i visit this site first. Thank you. CSS-Tricks 4TW!

Works perfectly, thanks so much!

This worked and is much more pleasant BUT the container that was scrolling horizontally had elements with hover states that would activate when touched on iOS, now they do not respond to touch, so I will probably have to scrap the inertia scroll since the information displayed on hover is more important :(

I want Same scrolling Feature in android devices

Here postcss plugin https://npmjs.com/package/postcss-momentum-scrolling

Just to reiterate the sprinkled comments in here:

THIS BREAKS SCROLL NOTIFICATION

I appreciate the article as it does give inertial scrolling back, but a few developers in here (Jessie, Nishant, Charles) have noted that they no longer receive scroll events when this is turned on. This is imported for our application. We do get one event when inertial scrolling finally stops to exactly 0 but this does not help our use cases. We need events WHILE the scrolling is occuring, as we get when this is not in or as we get on other products with inertial scrolling. Jessie, thanks for the setTimeout and track solution. I will probably simulate it this way until we find another answer.

This fix no longer seems to be working. – Is anybody experiencing the same issue? – Does anybody know how it could be fixed now?

According to this it is no longer needed: https://ferdychristant.com/why-ios-11-is-great-news-for-the-web-345c43cc55db

Thanks for that tips it’s working really well ! But I have a problem, the scrollbar appear, while I put ‘ ::-webkit-scrollbar { display: none; }.

Can you help me to make it transparent, or customize it ?

Hi! Nice post, thx! There is a problem when using the animation on the page. Content when scrolling begins to “dance”. Has anyone encountered, tried to fix?

Great. This was useful to see. We use this in the credits within our game.

webkit-overflow-scrolling: auto not work on my iPad now.

“webkit-overflow-scrolling” no more works in ios 13 as ios 13 has by default enabled the momentum scrolling in any scrollable container but I do not want that behavior. Anyone have any idea how can I disable momentum scrolling on my webpage?

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Save my name, email, and website in this browser for the next time I comment.

Copy and paste this code: micuno *

Leave this field empty

ios safari body overflow hidden

Overflow hidden + round corners not working on Safari

Apparently, it’s a bug on safari. Any workaround/code to fix this?

http://stefanias-first-project-e6faf1.webflow.io/issue-2

42

Here is my site Read-Only: LINK ( how to share your site Read-Only link )

Hi Stefania,

round corners don’t need overflow hidden most of the time (they don’t need it for bg images for example)

your link plays well in my Safari, look:

http://vincent.polenordstudio.fr/snap/issue_2_2018-09-21_15-01-03.png

Hey @stefania4

In Safari, everything also looks find for me until I hover over the element.

Are you able to share a read-only link?

Hi, @stefania4 !

This Safari bug is well known (don’t know why they didn’t fix it). Workaround you can find here: Safari not hiding overflow on rounded corner divs

thanks @sabanna , Now it’s working.

I used this as reference:

The issue is the combination of overflow , border-radius , and transition

This is the solution: On the element with overflow:

.transitionfix() { -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0) }
added minus z-index value for image and higher value for the parent

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.

Instantly share code, notes, and snippets.

@nicolaskopp

nicolaskopp / ios-fixed-scrolling-fix.css

  • Star 24 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Embed Embed this gist in your website.
  • Share Copy sharable link for this gist.
  • Clone via HTTPS Clone using the web URL.
  • Learn more about clone URLs

@steida

steida commented Jan 1, 2023

Isn't https://www.bram.us/2021/07/08/the-large-small-and-dynamic-viewports/ a fix?

Sorry, something went wrong.

@Offirmo

Offirmo commented Nov 1, 2023

Hi from 2023!

@steida Yes, using lvw, lvh is the fix at the moment

HOWEVER there is another bug:

  • when pinned on iPhone (PWA / "pin to home screen")
  • when requesting fullscreen (meta viewport-fit=cover, webmanifest "display": "fullscreen")

body's position must NOT be "fixed", or it strangely crops to the small viewport! (seen 2023/11 iOs 16.6.1 iPhone 14) (strangely it works with position: absolute but that's not the same)

The solution is

  • ensure root element (:root, html) has no padding, no margin, no border
  • ensure body has height 100 lvh

@zigavidmar

zigavidmar commented Jan 17, 2024

If you are still experiencing this issue and the above hasn't helped check if you have a "position: relative" on the body or html tag and remove it . This was the issue scrolling was broken on safari mobile for me.

@luishdez

luishdez commented Feb 7, 2024 • edited

@zigavidmar yes! it's happening to me as well. And apparently it gets worse with multiple relative parents or horizontal scrolling.

I tried with an empty page and it was fine ( not feeling native.. ) , once you add more depth it gets worse. And the elastic control that allows you to move to another tabs etc makes a lot of conflicts.

BTW: little bit of topic but related. Framer motion has same problem with child animations that mix px and % or relative units. Animations doesn't work , but they work fine after the dragStart event it's triggered. The webkit renders properly.

I forgot. There is another actor in the issue. The touch algo. That analyze and predicts your intent. A huge scroll area usually works fine...

How To Prevent Scrolling The Page On iOS Safari 15

ios safari body overflow hidden

If we show a modal on iOS we need to prevent events inside the modal from interacting with the page behind the modal. On a previous episode of “Fun with Safari” we could use preventDefault() on the "touchmove" event but on iOS 15 that no longer works. Here we go.

To make this work we need iOS to think that there is nothing to scroll.

But how do we do this?

We set the height of both the html and the body element to the window height and then set overflow on these elements to hidden so the content gets cut off.

For now it’s set to 100px .

A height of 100vh

A vh is a viewport height unit, 1vh means 1% of the viewport height.

Great. Let’s set the measure to 100vh and observe what happens.

Nope. This is not going to work. On iOS 100vh is always the full height of the viewport, even if the footer shows.

If you’re browsing this page on iOS Safari, scroll up and down a bit to see the height stays fixed no matter of the footer is active or not.

Using -webkit-fill-available

Let’s try the -webkit-fill-available property instead.

This is a -webkit- thing, so if you’re on Firefox the measure will now snap to 0

This will do the trick. But it results in a modal that is not filling up all available space.

-webkit-fill-available will fill the “safe” space, so it excludes the room the footer will take up if it’s active.

We want all the space.

It turns out that window.innerHeight reflects the actual available space. So what we want is this.

But you and I both know that this won’t fly because window.innerHeight is a JavaScript property.

Syncing window.innerHeight

Luckily we can use CSS Custom Properties to make this tick.

Now we switch to JavaScript land where we update the custom property when the window is resized.

The 'resize' event is trigged when the iOS Safari footer changes in height, so this nicely aligns our measure with the page height.

I know we could also “just” set measure.style.height but as we’re going to use Custom Properties for our modal in a minute so we might as well go the extra mile here.

If you’re on iOS, see if the measure scales correctly by scrolling up and down a bit.

We’re almost out of the woods. Our measure is now be the right size no matter if the iOS Safari footer is visible or not.

Now we’re going to apply this height to the html and body elements when our modal is activated. This prevents the page from scrolling.

We’ll add an is-locked class to our html element when we show the modal.

When the is-locked class is assigned we use it to set the required styles so the page cannot be scrolled.

Wait a minute, what is that calc() doing there?!

Let me try to explain. If the iOS Safari footer is hidden when the modal opens, iOS will reveal the footer if the user makes a vertical pan gesture, the 1px prevents this from happening.

I don’t know why, I just know that I’m so tired. 🤷‍♂️

Let’s just move on.

Now we listen for the 'resize' event to keep the --window-inner-height CSS Custom Property synced with the window.innerHeight in JavaScript land.

To prevent iOS from hiding the footer when the modal is visible and the footer is visible we need to prevent the default action of the 'pointermove' event.

If we want to support iOS 14 we need to add 'touchmove' as well.

We’re nearly done. 🫠

Because we’re resizing the document the user scroll position is lost when the modal opens. So we need to remember that position before it opens and restore it when the modal closes.

There we go! 😅

Try it out on iOS Safari 15 by clicking the “Open modal” button below. It’s been tested with the address bar at the bottom of the viewport (which is the default) and at the top. We can move it to the top by tapping “aA” and then “Show Top Address Bar”.

The modal is slightly transparent on purpose so we can see that the page is scrolled to the top. We can assign the backdrop-filter: blur(20px) style to the modal root element to make this less obvious.

I sincerely hope the Apple Safari dev team will fix this mess in the next upate, but I’m not holding my breath.

I share web dev tips on Twitter, if you found this interesting and want to learn more, follow me there

Or join my newsletter

Busy doing the newsletter subscribing.

Something went wrong, can you give it another go?

At PQINA I design and build highly polished web components.

Make sure to check out FilePond a free file upload component, and Pintura an image editor that works on every device.

Related articles

Typescript interface merging and extending modules, blocking navigation gestures on ios safari, styling password fields to get better dots.

iOS でページ全体はスクロールを無効にし、個別の要素(textarea など)では有効にする方法

ios safari body overflow hidden

iOS 以外での実現方法

PC や Android など、iOS 以外のデバイスで、ページ全体をスクロール無効にし、それ以外の要素は有効にしたい場合は、以下の CSS を追加すれば良いです。

しかし、iOS では、この方法だとうまくいきません。

iOS でページ全体のスクロールを無効にする方法

ググってみるとたくさん記事が出てきます。一番シンプルでオーソドックスな方法は、以下の JavaScript を使用する方法です。

touchmove イベントハンドラを設置し、タップした状態から指を動かしたときに、 preventDefault() を呼ぶことでイベントをキャンセルします。

これならたしかにスクロールを無効にすることができます。しかし、これだと、たとえばテキストエリアがあった場合にはそのテキストエリア内のスクロールもできなくなってしまいます。

ページ全体(body)はスクロール無効にしたいけど、それ以外のテキストエリアなどの要素に対してはスクロールを有効にしたい、そういうときの実現方法を紹介します。

特定のエリアのみスクロールを無効にする方法 についての記事はありますが、今回実現したいのは、これの逆です。

以下の JavaScript で実現することができます。例としてテキストエリア内をスクロール有効にしています。

touchmove イベントが発火したときに、コールバック関数は引数 event を受け取ることができます。そしてこのイベントがどの要素で発火したのかを event.target で取得することができます。これがテキストエリアだった場合は、イベントの伝搬を中止する、というのがざっくりとして処理です。

ところがちょっと厄介なこととして、テキストエリア内で、一番上にスクロールされた状態でさらに上にスクロールしようとすると、body(ページ全体)がスクロールされてしまいます。一番下でスクロールした場合も同じです。

これだと完全にページ全体のスクロールを無効化したとは言い難いので、もう少し工夫します。テキストエリア内はスクロールできるけど、一番上(下)にスクロールされた状態では、イベントキャンセルの適用外、つまりスクロールができないようにします。

しかし、これには問題があり、ページがロードされた段階ではテキストエリアは一番上にスクロールされた状態なので、テキストエリア内でスクロールができなくなってしまいます。そのため textarea.scrollTop = 1; を最初に実行することで回避します。

まだ問題があって、一度でも一番上や下までスクロールしてしまうと、そこからスクロールできなくなってしまいます。なので、 scroll イベントハンドラを設置し、一番上(下)までスクロールしたら、その位置から+1(-1)だけ位置を移動させて、ほんの僅かな量だけ一番上(下)までスクロールできないようにします。こうすることで、常に一番上(下)にはスクロールされた状態にはならないので、テキストエリア内のスクロールを通してページ全体がスクロールされてしまうことを防ぐことができます。

少し回りくどい実装方法ではありますが、iOS でも body に overflow: hidden; をかけたのとほぼ同等の挙動になりました。

テキストエリアではなくほかの要素に対して同じことをしたい場合は1行目を書き換えてください。また複数スクロール可能にしたい場合は要素と処理を追加してください。

ちなみに最初は、イベントの伝搬をバブリングからキャプチャリングに変更し、body までイベントが伝搬したタイミングでイベントの伝搬を止める、という処理をしようとしてましたが、body まで伝搬したタイミングで止める方法が思いつかなかった(わからなかった)のであきらめました x(

ios safari body overflow hidden

IMAGES

  1. Safari position fixed overflow hidden

    ios safari body overflow hidden

  2. Safari position fixed overflow hidden

    ios safari body overflow hidden

  3. Mobile Safari and overflow:hidden problem

    ios safari body overflow hidden

  4. Mobile Safari Overflow Hidden Bug

    ios safari body overflow hidden

  5. Overflow:Hidden on Body Is Broken in iOS6

    ios safari body overflow hidden

  6. How to Enable Incognito Mode in Safari for iOS 15, iPadOS

    ios safari body overflow hidden

VIDEO

  1. NEW fragrances I'm completely OBSESSED WITH...(these are all 10/10's)

  2. iOS17.4 正式版更新釋出!全新 emoji 表情符號終於來啦~🙂‍↕️🙂‍↔️|塔科女子

  3. Safari Privacy Settings #tech #iphonetricks #techtips #ios #safari

  4. iOS SAFARI BROWSER FOR ANY ANDROID ❗ #ios #ytshorts #safari

  5. iOS 🍎 Safari For Android 🔥 [Working 100%] Android 13

  6. Bloqueando o scroll horizontal no Elementor com CSS

COMMENTS

  1. How to disable scrolling on mobile Safari?

    Here is what I did: I check the body y position , then make the body fixed and adjust the top to the negative of that position. On reverse, I make the body static and set the scroll to the value I recorded before. var body_x_position = 0; function disable_bk_scrl(){. var elb = document.querySelector('body');

  2. Simple Solution to Prevent Body Scrolling on iOS

    As you can see above, we use position: fixed in combination with storing the scroll position of the user so we can restore the scroll position after the fact.. Caveats. There are certainly some downsides to this approach. If you change the size of the browser window while the scroll lock is active, for example, the scroll position does not get restored correctly.

  3. Locking `body` scroll for modals on iOS

    For most browsers, setting overflow: hidden on the body (once the overlay is open) is sufficient. Not so on mobile Safari: The TLDR is: use position: fixed and scroll the container to manage the shifting offset. Check out the CodePen for an example.

  4. Preventing body scroll for modals in iOS

    iOS doesn't prevent users from scrolling past the modal if there is content that exceeds the viewport height, despite you adding that condition to the CSS. One solution is to write the window.innerHeight into both HTML and body elements in the DOM and then toggle the overflow: hidden style on and off on the body and html: var vpH = window ...

  5. How to fight the <body> scroll

    Step to prevent scroll on <body/> element: Add overflow:hidden on body element. Handle touch events, for Safari. Keep the scroll bar gap. Plus bonus point: make full screen locks work without ...

  6. Prevent overscroll/bounce in iOS MobileSafari and Chrome (CSS only)

    UPDATE 2017.12: For non-Safari browsers (e.g. Chrome, Firefox) you can use overscroll-behavior to solve exactly this. Simply apply overscroll-behavior-y: none; on html, body and be done with it. Safari however does not support it …. UPDATE 2021.06: The workaround below no longer seems to work in recent iOS/MobileSafari versions (iOS 12 ...

  7. finally ios <body> overflow:hidden is happening!!!

    You should be able to apply "overflow:hidden" to the body tag to prevent the page from scrolling. This is useful for modal dialogs because you really shouldn't be able to scroll the page behind it by design. IOS didn't care, though, and just let you scroll anyhow, which was dumb. That seems to be adjusted now. 26.

  8. Overflow: hidden not working in iOS safari.

    As mentioned by Jennifer, Safari overlooks "overflow: hidden" when applied to HTML and BODY in CSS. You should target the element selector more directly in CSS, specifically the one that has the content that is overflowing, and use "overflow: hidden" on that element. In my testing Safari does not overlook that, and it fixed my issue.

  9. Disable Body Scrolling For Open Modals on iOS Devices

    1. Stop everything from scrolling, both body background and everything inside modal. 2. Stop the background from scrolling, while allowing content inside the modal to scroll. THE FIRST EXAMPLE - Freeze Everything . The first block of code is checking if we are indeed in Safari on an iPhone, otherwise we run what works for literally everything else.

  10. Overflow-x value ignored in mobile safari

    Tested with Mobile Safari on iOS 7.1/8.2 Following code didn't work for me neither. html { overflow: hidden; } I believe it's a bug/feature of Mobile Safari, other browsers, including Safari on OSX works well. But the overflow:hidden works on iPhone/iPad if you also set position:fixed to HTML element. Like this:

  11. Prevent Scroll Chaining With Overscroll Behavior

    We used to fix that by applying overflow: hidden on the body element via Javascript. When a modal is opened, we add a class to the body that is responsible for applying the overflow. body.modal-open {overflow: hidden;}.modal.is-open {display: block;} This solution used to work perfectly across desktop browsers, but Safari on iOS didn't like that.

  12. 153852

    2) Prevent body scrolling at all time when the body is set to `overflow: hidden` and, *as it is the case when the body is set to `position: fixed`*, display the browser UI fully as soon as the body is set to `overflow: hidden`, and leave it that way til it is set to something else. This is what Chrome on iOS does, and it works very well.

  13. Momentum Scrolling on iOS Overflow Elements

    Tested in the native browser and Chrome of a Samsung tablet with Android 4.0.4 and also on iPad with iOS 6, Safari + Chrome. Reply. Balbir. ... I loose the vertical momentum scrolling as soon as I add overflow-x: hidden to the html and body ... { 'overflow': 'hidden'}); // For iOS, change it to fixed positioning and make it in the same place as ...

  14. Scroll doesn't work on IOS · Issue #102 · willmcpo/body-scroll-lock

    Overflow-y: auto and height: 100%+1px fixes two iOS scroll bugs. I believe this library uses overflow: hidden. iOS doesn't respect overflow hidden for scroll lock containers but overflow auto does work. Height: 100% + 1px is a hack to allow scroll lock for dynamic content that goes beyond the height of the view port (again, iOS).

  15. Overflow hidden + round corners not working on Safari

    The issue is the combination of overflow, border-radius, and transition. This is the solution: On the element with overflow:.transitionfix() {-webkit-backface-visibility: hidden;-moz-backface-visibility: hidden;

  16. css

    Funny enough, if instead of overflow-y: hidden, overflow: hidden will be set on body, Safari prevents vertical scrolling :) - Limon Monte Oct 18, 2018 at 5:34

  17. Fix scrolling bug on iOS Safari with fixed elements and bottom bar

    body's position must NOT be "fixed", or it strangely crops to the small viewport! (seen 2023/11 iOs 16.6.1 iPhone 14) (strangely it works with position: absolute but that's not the same) The solution is. ensure root element (:root, html) has no padding, no margin, no border; ensure body has height 100 lvh

  18. How To Prevent Scrolling The Page On iOS Safari 15

    window.scrollTo(0, scrollY); } There we go! 😅. Try it out on iOS Safari 15 by clicking the "Open modal" button below. It's been tested with the address bar at the bottom of the viewport (which is the default) and at the top. We can move it to the top by tapping "aA" and then "Show Top Address Bar".

  19. css

    To make it happen, you must first set the overflow of the html element to anything other than visible, e.g. to auto or hidden (in iOS, there is no difference between the two). That way, the body overflow setting doesn't get transferred to the viewport and actually sticks to the body element when you set it to overflow: hidden.

  20. iOS でページ全体はスクロールを無効にし、個別 ...

    PC や Android など、iOS 以外のデバイスで、ページ全体をスクロール無効にし、それ以外の要素は有効にしたい場合は、以下の CSS を追加すれば良いです。. html, body { overflow: hidden; } 実に簡単です。. しかし、iOS では、この方法だとうまくいきません。.

  21. html

    It treats the menu like overflow is hidden and does not scroll. ... Adding margin and padding code seem to help it work fine in Safari for OS X, but I am having a strange issue where Safari on iOS ignores the overflow:hidden property on another element. But this appears to fix the problem at hand. .right-off-canvas-menu { height: 100%; max ...