iOS 7 iPad Safari横屏innerHeight/outerHeight布局问题

73
我们发现在iOS7的Safari浏览器上,一个高度为100%的网页应用存在问题。看起来窗口的内部高度(window.innerHeight)(672像素)与外部高度(window.outerHeight) (692像素)不匹配,但这种情况仅在横向模式下出现。结果就是,在body高度设为100%的应用中,会多出20像素的空间。这意味着当用户在我们的应用上向上滑动时,导航元素会被浏览器框架覆盖。另外,所有绝对定位在屏幕底部的元素都会偏移20像素。
这个问题也在这个问题中有所提及: IOS 7 - css - html height - 100% = 692px 并且可以在这张模糊的截图中看到: iOS 7 Safari outerHeight issue 我们正在尝试绕过这个问题,以便在Apple修复这个错误之前,我们不必担心它。
一种解决方法是只在iOS 7中对body进行绝对定位,但这基本上会将多出的20像素放在页面顶部而不是底部:
body {
    position: absolute;
    bottom: 0;
    height: 672px !important;
}
任何有助于使outerHeight与innerHeight匹配,或者绕过此问题以使我们的用户看不到此问题的帮助,将不胜感激。

2
请参考以下网址以获取更多详细信息:http://krpano.com/ios/bugs/ios7-ipad-landscape/ - Vikash Pandey
1
顺便提一下:这个 bug 在 iOS8 上似乎已经修复了。解决方法应该只针对 iOS7。 - Patrick Rudolph
13个回答

60

在我的情况下,解决方案是将位置更改为fixed:

@media (orientation:landscape) {
    html.ipad.ios7 > body {
        position: fixed;
        bottom: 0;
        width:100%;
        height: 672px !important;
    }
}

我还使用了一个脚本来检测带有iOS 7的iPad:

if (navigator.userAgent.match(/iPad;.*CPU.*OS 7_\d/i)) {
    $('html').addClass('ipad ios7');
}

5
我将window.scrollTo(0,0);与此结合,在屏幕方向改变时使用,这似乎解决了我们99%的问题。给我几天时间在生产环境中测试一下,我认为我们可能已经找到了答案。 - hisnameisjimmy
我刚刚发现一个使用iPad mini OS 7的朋友没有这个问题,有什么办法可以确定是否是iPad mini引起的? - Gino
1
我有一台配备Retina显示屏的iPad mini,并且遇到了相同的问题。 - chrishawn
哇...我简直不敢相信我花了这么长时间才找到它。我整晚都在寻找奇怪的JavaScript黑客技巧,几乎都没用。这太神奇了。 - Christopher Reid
真是浪費了兩天時間在 iPad 模擬器上,我以為我的 postion:absolute; 是引起這個問題的原因。謝謝! - Chris Gunawardena

16

简单、清晰的纯CSS解决方案:

html {
     height: 100%;
     position: fixed;
     width: 100%;
   }

iOS 7似乎能够正确设置高度。此外,不需要调整大小的JavaScript事件等。由于您正在使用全高度应用程序,因此是否始终采用固定位置并不重要。


这在普通的带视网膜的iPad上可以运行,但在iPad Mini上却无法运行,很奇怪。 672px的修复似乎在两者上都有效。 - jscharf

14

塞缪尔的答案,正如Terry Thorsen所述,非常有效,但在网页已添加到iOS主屏幕的情况下会失败。

更直观的修复方法是检查window.navigator.standalone变量。

if (navigator.userAgent.match(/iPad;.*CPU.*OS 7_\d/i) && !window.navigator.standalone) {
    $('html').addClass('ipad ios7');
}

这样只有在Safari内打开时才适用,如果从主屏幕启动,则不适用。


另一种情况是当收藏栏正在显示时,这种方法不起作用。因此,通过这个答案和我下面的回答,所有问题都得到了解决(就我所知)。 - Nick Hingston

2

尽管 Samuel 的答案很好,但如果用户将页面添加到主屏幕上,则会出现错误(主屏幕页面不会出现该错误)。在添加类之前,请检查 innerHeight:

if (navigator.userAgent.match(/iPad;.*CPU.*OS 7_\d/i)) {
    if(window.innerHeight==672){
        $('html').addClass('ipad ios7');
    }
}

请注意,该错误在Webview下也不会出现。

2
我的iPad报告window.innerHeight为671(使用console.log),因此672并不总是准确的。 - Esger

2
我使用了这个JavaScript解决方案来解决这个问题:
    if (navigator.userAgent.match(/iPad;.*CPU.*OS 7_\d/i) && window.innerHeight != document.documentElement.clientHeight) {
  var fixViewportHeight = function() {
    document.documentElement.style.height = window.innerHeight + "px";
    if (document.body.scrollTop !== 0) {
      window.scrollTo(0, 0);
    }
  }.bind(this);

  window.addEventListener("scroll", fixViewportHeight, false);
  window.addEventListener("orientationchange", fixViewportHeight, false);
  fixViewportHeight();

  document.body.style.webkitTransform = "translate3d(0,0,0)";
}

1

Samuel的方法的一个变体,但是在html上设置了position: -webkit-sticky对我最有效。

@media (orientation:landscape) {
    html.ipad.ios7 {
        position: -webkit-sticky;
        top: 0;
        width: 100%;
        height: 672px !important;
    }
}

请注意 'top: 0',而不是 'bottom: 0',目标元素是 'html',而不是 'body'。

0

接受的答案无法处理收藏栏处于显示状态时的情况。以下是改进后的通用解决方案:

@media (orientation:landscape) {
  html.ipad.ios7 > body {
    position: fixed;
    height: calc(100vh - 20px);
    width:100%;
  }
}

0

关于已接受的答案,我也尝试了以下规则并取得了成功:

html.ipad.ios7 {
    position: fixed;
    width: 100%;
    height: 100%;
}

这样做的额外好处是,它似乎可以阻止HTML元素在固定的body元素下面滚动。


0

基本上有两个问题 - 横向模式下窗口的高度和用户从纵向模式返回时的滚动位置。我们通过以下方式解决了这个问题:

窗口的高度由以下控制:

// window.innerHeight is not supported by IE
var winH = window.innerHeight ? window.innerHeight : $(window).height();

// set the hight of you app
$('#yourAppID').css('height', winH);

// scroll to top
window.scrollTo(0,0);

现在,以上内容可以放入一个函数中,并绑定到窗口调整大小和/或方向更改事件。就是这样... 请参考以下示例:

http://www.ajax-zoom.com/examples/example22.php


你并没有真正解决问题,因为页面底部有一个奇怪的白色空间(请看这里),用户仍然可以滚动页面。 - agudulin

0
我遇到了与此相同的问题,然后发现了这个页面。这里有很多有用的答案,但也有一些对我来说不太适用的答案。
然而,我找到了一个解决方案,在我的情况下可以完全独立于操作系统版本和当前、过去或未来的任何错误。
解释:开发一个 Web 应用程序(非本地应用程序),其中包含几个固定大小的全屏模块,类名为“module”。
.module {position:absolute; top:0; right:0; bottom:0; left:0;}

其中包含一个类名为“footer”的页脚

.module > .footer {position:absolute; right:0; bottom:0; left:0; height:90px;}

没关系,如果我以后将页脚的高度设置为另一个值,甚至是通过内容来确定其高度,我可以使用以下代码进行校正:

function res_mod(){
    $('.module').css('bottom', 0); // <-- need to be reset before calculation
    var h = $('.module > .footer').height();
    var w = window.innerHeight;
    var o = $('.module > .footer').offset();
    var d = Math.floor(( w - o.top - h )*( -1 ));
    $('.module').css('bottom',d+'px'); // <--- this makes the correction
}

$(window).on('resize orientationchange', res_mod);

$(document).ready(function(){
    res_mod();
});

感谢Matteo Spinelli的技能,我仍然可以毫无问题地使用iScroll,因为它的更改事件幸运地在之后被触发。如果没有这样,就需要在更正后重新调用iScroll-init。

希望这能帮助到某些人


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接