This is normal behaviour on iOS (and iOS only). You can work around it by declaring overflow: hidden
on both html
and body
element. In addition, you should set the body to position: relative
.
Overflow behaviour
There are several things at play here. To understand why the fix works, we first need to have a look at how the overflow of the viewport is set.
- The overflow of the viewport is determined by the overflow setting of the
html
element.
- But as long as you leave the overflow of the
html
element at its default (visible
), the overflow setting of the body gets applied to the viewport, too. Ie, you can set either html
or body
to overflow: hidden
when you target the viewport. The overflow behaviour of the body element itself is unaffected - so far.
- Now, if you set the overflow of the
html
element to anything other than visible
, the transfer from body
to viewport does no longer happen. In your particular case, if you set both overflows to hidden
, the setting of the html
element gets applied to the viewport, and the body
element hides its overflow as well.
That's actually the case in every reasonably modern browser out there, and not specific to iOS.
iOS quirks
Now, iOS ignores overflow: hidden
on the viewport. The browser reserves the right to show the content as a whole, no matter what you declare in the CSS. This is intentional and not a bug, and continues to be the case in iOS 7 and 8. There is nothing anyone can do about it, either - it can't be turned off.
But you can work around it by making the body element itself, not the viewport, hide its overflow. 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
.
With that in place, most content is hidden. But there still is an exception: elements which are positioned absolutely. Their ultimate offset parent is the viewport, not the body. If they are positioned somewhere off screen, to the right or to the bottom, you can still scroll to them. To guard against that, you can simply set the body element to position: relative
, which makes it the offset parent of positioned content and prevents those elements from breaking out of the body box.
Answering in code
There is one final gotcha to watch out for: the body itself must not be larger than the viewport.
So the body needs to be set to 100% of the viewport width and height. (The credit for a CSS-only way to achieve it goes to this SO answer.) Margins on the html
and body
elements have to be 0, and the html
must not have padding or a border, either.
Finally, in order to deal with body padding, and in case you ever want to set a border on the body, make the math work with box-sizing: border-box
for the body.
So here goes.
html {
overflow: hidden;
height: 100%;
margin: 0;
padding: 0;
border: none;
}
body {
overflow: hidden;
position: relative;
box-sizing: border-box;
margin: 0;
height: 100%;
}
NB You can set body padding and border as you please.