我遇到一个非常奇怪的问题...在每个浏览器和移动版本中,我都遇到此问题:


加载页面时,所有浏览器都有一个顶层菜单(显示例如),当您开始滚动页面时会向上滑动。
有时仅在视口的可见部分计算100vh,因此当浏览器栏向上滑动100vh时(以像素为单位)会增加
所有布局由于尺寸已更改,因此需要重新绘画和重新调整
对于用户体验而言,跳动效果很差

如何避免此问题?当我第一次听说viewport-height时,我很兴奋,我认为我可以将其用于固定高度块而不是使用javascript,但是现在我认为唯一的方法实际上是带有一些resize事件的javascript ...

您可以在以下示例站点中看到问题:

有人可以帮助我/建议CSS解决方案吗?


简单的测试代码:




 /* maybe i can track the issue whe it occours... */
$(function(){
  var resized = -1;
  $(window).resize(function(){
    $('#currenth').val( $('.vhbox').eq(1).height() );
    if (++resized) $('#currenth').css('background:#00c');
  })
  .resize();
}) 

 *{ margin:0; padding:0; }

/*
  this is the box which should keep constant the height...
  min-height to allow content to be taller than viewport if too much text
*/
.vhbox{
  min-height:100vh;
  position:relative;
}

.vhbox .t{
  display:table;
  position:relative;
  width:100%;
  height:100vh;
}

.vhbox .c{
  height:100%;
  display:table-cell;
  vertical-align:middle;
  text-align:center;
} 

 <div class="vhbox" style="background-color:#c00">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
    <br>
    <!-- this input highlight if resize event is fired -->
    <input type="text" id="currenth">
  </div></div>
</div>

<div class="vhbox" style="background-color:#0c0">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
  </div></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> 




评论

如果我很好地理解了这个问题,那么您所面临的问题是在移动浏览器中,高度是否超过可见的视口高度。对吗?

有趣的是,以前从未注意到。其主要是明显跳动的背景图片。如何添加过渡(0.5秒左右)以使更改不那么突然?

@GauravAggarwal不,正好相反:实际的视口高度大于浏览器在其地址栏可见时提供的视口高度...

由于我的问题越来越流行,我想给我5美分:保持真正的窗口高度并仅向上滑动菜单栏会更聪明吗?似乎并不那么困难。实际上应该更容易...手指向上->菜单栏向上滑动直到不可见,手指向下->菜单栏向下滑动直到完全可见...全部与身体完全相同,没有任何重新调整和跳跃效果... br />
Google对此提供了一些很好的信息:developers.google.com/web/updates/2016/12/url-bar-resizing如果您已将身高更改为100%,则可以使用100%代替100vh

#1 楼

不幸的是,这是故意的...
这是一个众所周知的问题(至少在safari mobile中),这是故意的,因为它可以防止其他问题。 Benjamin Poulain回复了一个webkit错误:

这完全是故意的。为了达到这种效果,我们付出了很多工作。 :)
基本问题是:滚动时可见区域会动态变化。如果我们相应地更新CSS视口高度,则需要在滚动过程中更新布局。不仅看起来像狗屎一样,而且大多数页面几乎不可能做到60 FPS(iOS上的60 FPS是基准帧速率)。
很难向您展示“看起来像狗屎”部分,但是可以想象当您滚动时,内容在移动,屏幕上您想要的内容也在不断变化。
动态更新高度无法正常工作,我们有几种选择:在iOS上放下视口单位,像在iOS 8之前一样匹配文档大小,使用较小的视图大小,使用较大的视图大小。
根据我们的数据,使用较大的视图大小是最好的折衷方案。大多数使用视口单位的网站在大多数时候看起来都很不错。

Nicolas Hoizey对此进行了大量研究:https://nicolas-hoizey.com/2015/02/viewport-height-is-高于某个移动浏览器中文档的可见部分。html
没有计划中的修复程序
目前,除了避免在移动设备上使用视口高度。 Chrome在2016年也更改为此:

https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/BK0oHURgmJ4
https: //developers.google.com/web/updates/2016/12/url-bar-resizing


评论


是的,正如我所想。唯一可行的解​​决方案是脚本解决方案。据我所知,$(window).height()不受此错误的影响,所以我将采用这种方式。谢谢!

–Nereo Costacurta
16年5月9日在10:52

脚本是我的唯一途径,并且效果很好。

– captDaylight
16年8月23日在17:08

有史以来最令人沮丧的答案。

– Winnemucca
17年11月17日在16:31

从Chrome版本56开始,vh始终像隐藏了URL栏一样进行计算,因此vh在滚动时不会增加,除非position:fixed。这类似于Safari上的实现。了解更多:developers.google.com/web/updates/2016/12/url-bar-resizing

–凯文·法鲁吉亚(Kevin Farrugia)
18年1月10日在23:16



初始加载后,最新版的Chrome不会更改vh或 100%的高度。这实际上很棒-因为URL栏隐藏时布局颠簸/重排看起来很可怕。 Firefox和Edge Mobile仍会动态更新vh和高度,从而在滚动时会导致所有组件的大量撕裂和调整大小,如果将SVG用于背景图像,则尤其糟糕

–德雷奈
18年5月11日在9:50



#2 楼

您可以在CSS中尝试min-height: -webkit-fill-available;而不是100vh。应该解决

评论


我要离开最低高度:100vh;作为不支持-webkit-fill-available的后备。

– Tammy T恤
19年4月3日,下午3:02

哇谢谢你!!有了这个,我不再需要“ react-div-100vh”了!

– Andres Elizondo
19年6月23日19:10

最有帮助的答案!

–Gauthier
19年7月22日在12:48

这是一个非常不错的解决方案,但在iOS 13中似乎有所更改—我在第一部分的底部看到了额外的空间,但是它完全可以在Safari中用于iOS 12.x来工作。codepen.io/RwwL/pen / PowjvPq(您必须对此进行分叉,然后在iOS上的CodePen调试模式下查看它才能真正理解我的意思)。

– RwwL
19/12/26在23:00



从Chrome v84开始,不再支持-webkit-fill-available。 :/

–TheCat
20年7月21日在11:14

#3 楼

在我的应用中,我这样做(打字稿和嵌套的postcss,所以要相应地更改代码):

const appHeight = () => {
    const doc = document.documentElement
    doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()


它至少在chrome mobile和ipad上有效。什么都行不通,是当您将应用程序添加到iOS的主屏幕上并更改方向几次后-缩放级别与innerHeight值混淆了,如果找到解决方案,我可能会发布更新。

演示

评论


这实际上是解决一个非常烦人的问题的绝佳方法。

– Michael Giovanni Pumo
19年3月14日在11:26

我想添加一个警告,“ @ media not all and(hover:hover){”不是检测移动浏览器的防弹方法。随意使用其他东西。

–安德烈亚斯牧群
19年7月11日在15:07

我非常喜欢这种方法。我要发表的一则评论是关于事件监听器的使用。这会导致页面重新绘制,并且我只在某些情况下才使用它,这对于用户查看正确的视口(即首页初始视图)绝对至关重要

– Zach Gollwitzer
20年4月3日,14:55

#4 楼

看这个答案:https://css-tricks.com/the-trick-to-viewport-units-on-mobile/




 // First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
}); 

 body {
  background-color: #333;
}

.module {
  height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
  margin: 0 auto;
  max-width: 30%;
}

.module__item {
  align-items: center;
  display: flex;
  height: 20%;
  justify-content: center;
}

.module__item:nth-child(odd) {
  background-color: #fff;
  color: #F73859;
}

.module__item:nth-child(even) {
  background-color: #F73859;
  color: #F1D08A;
} 

 <div class="module">
  <div class="module__item">20%</div>
  <div class="module__item">40%</div>
  <div class="module__item">60%</div>
  <div class="module__item">80%</div>
  <div class="module__item">100%</div>
</div> 




评论


聪明的解决方案,我正面临着移动设备上的视口高度问题,这也应被视为解决方案(特别是对于移动设备)

–朱利奥·韦多瓦托(Julio Vedovatto)
19年5月1日在21:27

非常好的解决方案。就我而言,我没有使用ponyfill,而是逻辑上,对于所有桌面站点我都使用100vh,而对于移动设备,我使用var(移动世界中没有IE11)。

– Frekaman
20-2-4在14:16

唯一适用于嵌套元素的解决方案。在必须替换数百个实例的其他任何人中,您可以(至少在大多数情况下)匹配正则表达式(-)?([\ d。] +)vh并将其替换为calc(var(-vh)* $ 1 $ 2)

– ecc521
20-4-4在18:09



#5 楼

对于我建立的许多网站,客户端都会要求提供100vh的横幅广告,正如您所发现的那样,当您开始滚动浏览时,这会导致在移动设备上出现糟糕的“跳跃”体验。这是我为在所有设备上获得平稳一致的体验而解决问题的方法:

我首先将横幅元素CSS设置为height:100vh

,然后使用jQuery来获取高度横幅元素的像素并使用此高度应用内联样式。

var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });


这样做可以解决移动设备上的问题,因为页面加载时设置了横幅元素到100vh(使用CSS),然后jQuery通过将内联CSS放在我的banner元素上来覆盖它,这会阻止它在用户开始滚动时重新调整大小。元素不会调整大小,因为由于上述jQuery,它现在具有以像素为单位设置的固定高度。为了解决这个问题,我使用“移动检测”在文档正文中添加了“移动”类。然后,我将上述jQuery包装为if语句:

if ($('body').hasClass('mobile')) {
  var viewportHeight = $('.banner').outerHeight();
  $('.banner').css({ height: viewportHeight });
}


结果,如果用户在移动设备上,则在页面的主体和上面的jQuery被执行。因此,我的banner元素只会将内联CSS应用于移动设备,而在台式机上,原始的100vh CSS规则仍然存在。

评论


我将它调整为使用$('。banner')。css({height:window.innerHeight});而是视口的“实际”高度。 innerHeight如何工作的示例

–马丁
17年7月13日在9:17



这是document.querySelector('。banner')。style.height = window.innerHeight;适用于编写实际JavaScript的人。

–法比安·冯·埃勒兹(Fabian von Ellerts)
19年3月12日在18:14

#6 楼

对我来说,这样的把戏很成功:

height: calc(100vh - calc(100vh - 100%))


评论


它与高度不同吗:100%?

–FF_Dev
20年5月30日12:00

是的,当纯100%仅填充所有可用的父区域时,100vh是视口高度(您的设备)的100%(父块可以具有例如50px的高度,并且只会填充到该父高度)。简而言之。

–Patryk Janik
20年6月4日在12:05

尽管地址栏没有隐藏,但这种简单的技巧确实确实有效,但是比其他解决方案要好。不赞成我。

– Eli
20年7月3日在8:34

#7 楼

我想出了一个React组件–检查您是否使用React或不使用它来浏览源代码,以便可以使其适应环境。

它将全屏div的高度设置为window.innerHeight,然后在窗口调整大小时对其进行更新。

评论


并非所有英雄都戴帽子。例如,你很好。非常感谢你。节省了数小时的时间。

– NarayaN
18年8月21日在9:16

对于Vue爱好者... github.com/razumnyak/vue-div-100vh

–托尼·奥哈根(Tony O'Hagan)
19/12/26在4:40

好的代码。谢谢你,先生。

–帕特里克·费舍尔(Patrick Fisher)
20 Dec 9'在21:08

#8 楼

几天来我一直在寻找解决方案,这是将VueJS与Vuetify一起使用的每个人(我的解决方案使用v-app-bar,v-navigation-drawer和v-footer):在App.vue中使用)具有以下内容:




 .v-application {
    height: 100vh;
    height: -webkit-fill-available;
}

.v-application--wrap {
    min-height: 100vh !important;
    min-height: -webkit-fill-available !important;
} 




评论


这很好用,并且可以在Vue / Vuetify世界之外推广。

–leo
20-05-30在14:49

起初,这似乎对我有用,但是后来我注意到,它不会根据移动设备上方向的改变来调整容器的大小。

–mediafreakch
20-6-26在12:32

html {高度:100vh;高度:-webkit-fill-available; } body,body> div,.v-application,.v-application--wrap {height:100vh;高度:-webkit-fill-available;最低高度:100vh;最小高度:-webkit-fill-available;为我节省了两个问题。最后

–mediafreakch
20 Jun 26'13:04



#9 楼

您可以通过添加以下脚本和样式来做到这一点
  function appHeight() {
    const doc = document.documentElement
    doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
  }

  window.addEventListener('resize', appHeight);
  appHeight();

.module {
  height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}


评论


谢谢你,先生。这有帮助

–rufatZZ
20-10-20在21:13

此处对此进行了全面描述:css-tricks.com/the-trick-to-viewport-units-on-mobile / ...

– stauross
20 Nov 26 '14:27

这就像一个魅力。对我来说,我想要的完美解决方案。非常感谢你。

– Amar Shakya
20-12-28 at 11:33

#10 楼

@nils清楚地解释了这一点。

接下来是什么?

我只是回过头来在CSS中使用相对的“经典” %(百分比)。

通常要花更多的精力实现的功能比使用vh的功能要好,但是至少,您有一个相当稳定的解决方案,它可以在不同的设备和浏览器上运行,而不会出现奇怪的UI故障。

评论


高度:横幅广告的100%仍然在移动浏览器上跳跃。我已经在Android上对其进行了测试,到目前为止,Chrome和Opera Mini的可见VP高度为100%,并且在滚动时不会改变。 Edge和Firefox更改100%的高度,然后重新绘制视图。 Firefox尤其糟糕,它会撕裂,重新渲染文本,并且显然会重新布局

–德雷奈
18年5月2日,9:16

@Drenai您确定正确实现了吗?您能在jsfiddle.net上的一个小例子中证明这一点吗?

– klimat
18年11月28日在8:52

是,我确定:-)

–德雷奈
18年11月28日在16:21

#11 楼

以下代码(使用jQuery)解决了该问题。

var vhHeight = $("body").height();
var chromeNavbarHeight = vhHeight - window.innerHeight;
$('body').css({ height: window.innerHeight, marginTop: chromeNavbarHeight });


其他元素以%为单位代替vh

#12 楼

我刚刚发现我设计的Web应用程序在iPhone和iPad上存在此问题,并且发现了一篇文章,建议使用针对特定Apple设备的媒体查询来解决此问题。该文章中的代码在这里,但地址是这样的:http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

引用该文章:“使用针对旧版iPhone和iPad分辨率的媒体查询将元素高度与设备高度匹配。“

他们仅添加了6个媒体查询来适应全高度元素,它应该可以正常工作完全实现了CSS。

编辑中:我现在无法测试,但是我会回来报告结果。

评论


还在等待那些结果results

– gman
18年1月8日在6:58

对不起,我没有按承诺回来。但是在摆弄HTML和CSS的时间长于我想要的时间之后,我更改了布局的设计,发现了一种可以满足我的需求的方法,但不是通用解决方案。

–贾哈齐尔
18年8月28日在18:01

#13 楼

刚接触我时,我无法对其他答案发表评论。

如果有人正在寻找答案来完成这项工作(并且可以使用javascript-似乎需要使用它来完成此工作,目前),这种方法对我来说效果很好,并且也说明了移动方向的变化。我将Jquery用于示例代码,但应可与vanillaJS一起使用。

-首先,我使用脚本来检测设备是触摸还是悬停。裸露的示例:

if ("ontouchstart" in document.documentElement) {
    document.body.classList.add('touch-device');

} else {
    document.body.classList.add('hover-device');
}


这根据设备类型(悬停或触摸)将类添加到body元素,以后可用于高度脚本。

-接下来使用此代码设置设备在负载和方向变化时的高度:

if (jQuery('body').hasClass("touch-device")) {
//Loading height on touch-device
    function calcFullHeight() {
        jQuery('.hero-section').css("height", $(window).height());
    }

    (function($) {
        calcFullHeight();

        jQuery(window).on('orientationchange', function() {
            // 500ms timeout for getting the correct height after orientation change
            setTimeout(function() {
                calcFullHeight();
            }, 500);

        });
    })(jQuery);

} else {
    jQuery('.hero-section').css("height", "100vh");


}


-设置超时以便设备会在方向更改时正确计算新高度。如果没有超时,以我的经验,高度将不正确。 500ms可能过大,但对我有用。

如果浏览器覆盖CSS 100vh,则悬停设备上的100vh属于后备状态。

评论


并非所有浏览器都支持Touchstart,某些非移动设备也支持它。developer.mozilla.org/en-US/docs/Web/Events/touchstart

–德雷奈
18年5月2日在9:19



大多数移动浏览器都支持@Drenai Touchstart。在台式机上支持较少,IE,Opera和Safari不支持。但是,最初的问题是针对移动设备而非台式机的。这使之成为有效答案。

– Paul
18年8月11日在19:18

感谢您的评论!我主要使用这种策略来克服某些移动浏览器上屏幕的调整和跳动。如果浏览器不支持touchstart,则回退将是CSS 100vh-如果不支持100vh,则是另一个主题。如果桌面设备碰巧具有触摸支持,则将使用javascript计算高度。然后,我们将失去对100vh提供的调整大小的重新计算,但这通常不是致命的问题,因为台式机上的方向没有变化。

–t.j.goodman
18年8月13日在6:35

同样,高度计算也可以附加到调整大小事件中,但随后我们可能会遇到相同的精确问题,即移动设备上的重新调整和跳跃性。因此,我发现这种策略是可行的。

–t.j.goodman
18年8月13日在6:38

@Paul如果Chrome和Firefox桌面浏览器具有touchstart,那么确定检测移动浏览器使用的功能肯定错误吗?我刚刚在Windows的Chrome和Firefox中对其进行了测试,并且都通过了此测试

–德雷奈
18年8月13日在21:16



#14 楼

以下对我有用:

html { height: 100vh; }

body {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
}

/* this is the container you want to take the visible viewport  */
/* make sure this is top-level in body */
#your-app-container {
  height: 100%;
}


body将占据可见视口高度,而#your-app-containerheight: 100%将使该容器占据可见视口高度。

#15 楼

这是我用于我的React应用程序的一种解决方法。 br />
这是一个折衷方案,但修复相对简单

#16 楼

VH 100在移动设备上无法正常运行,因为它没有考虑iOS栏(或其他平台上的类似功能)。
一种有效的解决方案是使用JavaScript“ window.innerHeight”。只需将元素的高度分配给该值,例如
$('。element-name')。height(window.innerHeight);
注意:在JS中创建函数可能很有用,因此调整屏幕大小时可以改变高度。但是,我建议仅在更改屏幕宽度时才调用该函数,这样当用户向下滚动页面时,iOS栏消失时,元素的高度将不会跳跃。

#17 楼

希望这将是一个UA定义的CSS环境变量,如下所示:https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-397536046

#18 楼

因为它不会被修复,所以您可以执行以下操作:

# html
<body>
  <div class="content">
    <!-- Your stuff here -->
  </div>
</body>

# css
.content {
  height: 80vh;
}


对我来说,这是比使用JavaScript更快,更纯粹的解决方案,但无法正常工作在许多设备和浏览器上。

只使用适合您需要的vh值即可。

#19 楼

在移动设备上使用vh不能与100vh一起使用,因为它们的设计选择是使用设备的整个高度,不包括任何地址栏等。

如果要查找包含div高度的布局与真实视图高度成比例,我使用以下纯css解决方案:

:root {
  --devHeight: 86vh; //*This value changes
}

.div{
    height: calc(var(--devHeight)*0.10); //change multiplier to suit required height
}


您有两个选项来设置视口高度,手动将--devHeight设置为高度可以正常工作(但您需要为要编码的每种设备类型输入此值)



使用javascript获取窗口高度,然后更新-在加载和刷新视口时需要devheight(但是这确实需要使用javascript而不是纯CSS解决方案)

一旦您获得了正确的视口高度,就可以以总视口高度的确切百分比创建多个div只需在每个div中更改乘数,即可将高度分配给。

0.10 =视图高度的10%
0.57 =视图高度的57% ght

希望这可能对某人有所帮助;)

评论


那是一个很好的解决方案。您可以在这篇有关Css-tricks的文章上阅读更多有关此的内容。

–阿莫里·汉瑟(Amaury Hanser)
19-4-3在13:39



#20 楼

可以在此博客文章中找到有关该问题及其解决方案的不错的阅读:在100vh布局中寻址iOS地址栏
我最终在React应用程序中使用的解决方案是利用上面的帖子。

#21 楼

您可以尝试将position: fixed; top: 0; bottom: 0;属性赋予您的容器。

评论


添加有关为什么这是问题的答案的更多信息。

– Pinaki Mondal
19年2月14日在12:35

#22 楼

尝试在移动设备上使用html, body { height: 100% }达到100vh的效果。