iOS – css/js – Overlay scroll but prevent body scroll

Tags: , ,



I know there have been a few questions similar to this but they either don’t work for my use case or the accepted answers have a flaw that doesn’t work for me. So…

I have a page with a list of elements. Clicking on an element in the list will open an overlay with details about that element. I need that overlay to be scrollable but I don’t want the rest of the page under the overlay to scroll so that once the overlay is closed you are in the same position (also the overlay is slightly transparent so it is annoying to the user to see the page scrolling below, also why I can’t save the scrollY and reset on close).

Right now I have the working everywhere except iOS. This is basically what I have:

<html>
   <body>
      <ul id="list">
         <li>something 1</li>
         <li>something 2</li>
         <li>something 3</li>
         <li>something 4</li>
         <li>something 5</li>
      </ul>
      <div id="overlay"></div>
   </body>
</html>

CSS:

body.hidden {
   overflow: hidden;
}
#overlay {
   opacity: 0;
   top: -100vh;
}
#overlay.open {
   opacity: 1;
   overflow-y: scroll;
   overflow-x: hidden;
   top: 0;
}

Then in my click hander I toggle the hidden class on body, the open class on #overlay, and populate the #overlay element with my content. Like I said this works fine everywhere except for iOS.

Solutions I have seen other places say I need to use position:fixed and height:100% on the body and/or html tags. The problem with this solution is that you lose your scroll position and when you close the overlay you’re back at the top of the page. Some of these lists can be really long so that isn’t an option for me.

I can’t prevent scrolling completely with preventDefault on body or something because I need the overlay content to be scrollable.

Any other suggestions?

Answer

There is no way around this right now. As of iOS 9.3 there’s still no good way to prevent the scroll on the body. The best method that I currently implement on all sites that require it is to lock the html and the body’s height and overflow.

html, body {
  height: 100%;
  overflow: hidden;
}

This is the best way to prevent iOS scroll on the content behind the overlay/modal.

Then to preserve the scroll position I shift the content behind up to look like its retaining it then when the modal closes restore the body’s position.

I do this with a lock and unlock function in jQuery

var $docEl = $('html, body'),
  $wrap = $('.content'),
  $.scrollTop;

$.lockBody = function() {
  if(window.pageYOffset) {
    scrollTop = window.pageYOffset;

    $wrap.css({
      top: - (scrollTop)
    });
  }

  $docEl.css({
    height: "100%",
    overflow: "hidden"
  });
}

$.unlockBody = function() {
  $docEl.css({
    height: "",
    overflow: ""
  });

  $wrap.css({
    top: ''
  });

  window.scrollTo(0, scrollTop);
  window.setTimeout(function () {
    scrollTop = null;
  }, 0);
}

When you piece all these together you get http://codepen.io/jerrylow/pen/yJeyoG if you want to test it on your phone here’s just the result: http://jerrylow.com/demo/ios-body-lock/



Source: stackoverflow