CSS3 100vh在移动浏览器中不恒定

Ner*_*rta 209 html css css3 viewport-units

我有一个非常奇怪的问题...在每个浏览器和移动版本中我遇到这种行为:

  • 加载页面时所有浏览器都有一个顶级菜单(例如显示地址栏),当您开始滚动页面时,该菜单会向上滑动.
  • 100vh仅在视口的可见部分计算,因此当浏览器栏向上滑动100vh时增加(以像素为单位)
  • 所有布局都重新绘制并重新调整,因为尺寸已更改
  • 对用户体验的糟糕跳跃效果

怎么能避免这个问题?当我第一次听到视口高度时,我很兴奋,我认为我可以将它用于固定高度块而不是使用javascript,但现在我认为唯一的方法是实际上javascript与一些resize事件......

您可以在以下位置查看问题:示例站点

任何人都可以帮我/建议一个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();
})
Run Code Online (Sandbox Code Playgroud)
*{ 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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Man*_*mar 213

我们有新的视口单元lvhsvh救援dvh。最新的 Google I/O 2022 网络作品视频对此进行了演示。

您可能希望坚持让dvh浏览器在滚动时适应移动设备的隐藏选项卡。dvw对于具有,lvw和单位的宽度,其工作方式类似svw

这是视频中的简洁插图:https://youtu.be/Xy9ZXRRgpLk?t=982

Google I/O 2022 上的新视口单元

我可以用吗?

目前,这正在我的 Chrome 金丝雀上运行,并启用了“实验功能”标志。

  • 2022年12月我们终于可以放心使用dvh了。 (9认同)
  • 我简直不敢相信,但这是真的吗?现在是 2023 年 5 月,我刚刚进行了测试,它就像一个魅力:`100svh` 是我们一直在等待的。 (7认同)
  • 截至 2023 年 2 月,您仍应使用后备(例如 height: 100vh;height: 100dvh;) - https://caniuse.com/viewport-unit-variants (5认同)
  • 请注意,这确实会导致一些卡顿。在 iOS 16.3 上,即使动态视口高度变化平滑,但带有 `100dvh` 的元素绝对不会变化;他们仅在动画结束时更新其高度。 (4认同)

nil*_*ils 156

不幸的是,这是有意的......

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

这完全是故意的.我们需要花费大量的工作才能达到这个效果.:)

基本问题是:滚动时可见区域会动态变化.如果我们相应地更新CSS视口高度,我们需要在滚动期间更新布局.不仅如此看起来像狗屎,但在大多数页面中以60 FPS执行此操作几乎是不可能的(60 FPS是iOS上的基线帧速率).

很难向你展示"看起来像狗屎"的部分,但想象当你滚动时,内容会移动,你想要的在屏幕上不断移动.

动态更新高度不起作用,我们有几个选择:在iOS上删除视口单元,匹配iOS 8之前的文档大小,使用小视图大小,使用大视图大小.

根据我们的数据,使用更大的视图大小是最好的折衷方案.大多数使用视口单元的网站在大多数时候看起来很棒.

Nicolas Hoizey对此进行了相当多的研究:https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

没有计划修复

此时,除了避免在移动设备上使用视口高度之外,您无能为力.移动Chrome似乎也想要适应这一点,尽管它不确定它们是否会贯彻执行.

  • 大多数抑郁症都回答过. (315认同)
  • 从Chrome版本56开始,vh总是被计算为好像URL栏被隐藏,因此除了"position:fixed"之外,vh在滚动时不会增加.这类似于Safari上的实现.了解详情:https://developers.google.com/web/updates/2016/12/url-bar-resizing (11认同)
  • 对于 2019 年仍在关注此问题的人来说,这个问题仍然存在。我将总结[this](https://developers.google.com/web/updates/2016/12/url-bar-resizing)帖子中给出的最新情况 - **使用 `vh` 将调整元素的大小就好像 URL 栏始终隐藏一样,而使用 % 将调整元素的大小,就像 URL 栏始终显示一样。** 虽然对此没有“完美”的解决方案,但我可以建议的唯一解决方法是第一个使用 100%当用户打开页面时,通常会显示 URL 栏的部分,并对所有其他部分使用 100vh。 (8认同)
  • 是的,我想.只有可行的解决方案才是脚本解决方案.据我所知,`$(window).height()`不受此bug的影响,所以我会这样做.谢谢! (5认同)
  • 初始加载后,最新的Chrome不会改变"vh"或"<html> 100%"高度.这实际上很棒 - 当URL Bar隐藏时,布局颠簸/重排可能看起来很糟糕.`Firefox`和`Edge``Mobile`仍然动态更新`vh`和`<html>`高度,在滚动时导致所有组件的大量撕裂和调整大小,如果使用SVG作为背景图像则特别糟糕 (3认同)
  • 是的,safari 是新的 IE。和 chrome 模仿它。无论如何,今天(6 月 18 日)在 chrome 上的行为似乎有些随机:有时,它保持较低的值,没有地址栏,有时它会更新……废话,真的,是吗。 (3认同)
  • 脚本对我来说是唯一的方式,并且工作得很好. (2认同)
  • 让我重新表述一下:“我们破坏了浏览器,但是我们要做很多工作,所以我们要保留它。” ...仅仅是我还是苹果越来越成为十年前的微软? (2认同)
  • 我对这个公认的答案感到困惑。我(和 OP)的问题似乎是“为什么 100vh 的值在我滚动并导致重绘时会重新计算?” 接受的答案似乎是说:“这是故意的,引用:如果我们相应地更新 CSS 视口高度,我们需要在滚动期间更新布局。不仅看起来很糟糕,而且在 60 FPS 中这样做实际上是不可能的大多数页面”,这正是我们看到并想要修复的行为。引用来自错误报告,称 webkit 不会重新计算 100vh,但 iOS Chrome 会(而且看起来很糟糕)。 (2认同)

小智 60

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

  • 从 Chrome v84 开始,不再支持 -webkit-fill-available。:/ (19认同)
  • 我将保留“ min-height:100vh;”作为不支持`-webkit-fill-available`的后备选项。 (6认同)
  • 谢谢,这太干净了!顺便说一句,我结束了使用这个版本,因为我的应用程序首先依赖于非零高度 `height: 100vh; 最大高度:-webkit-fill-available;` (6认同)
  • 你救了我的命谢谢你:) (5认同)
  • 这太棒了。我很确定顺序在这里很重要。我们非常习惯做这样的事情:`-webkit-bleedingedgethingy: 1; bleededgethingy: 1;` 因此,当常规版本进入规范时,它优先。但在这里您希望供应商特定的设置覆盖正常设置,因此 `height: 100vh; 高度:-webkit-fill-available;`而不是相反。 (4认同)
  • 这是一个非常好的解决方案,但似乎在 iOS 13 中发生了变化 - 我在第一部分的底部看到了额外的空间,但它在 iOS 12.x 的 Safari 中完全按照预期工作 https://codepen.io /RwwL/pen/PowjvPq (您必须分叉此文件,然后在 iOS 上的 CodePen 调试模式下查看它才能真正明白我的意思)。 (2认同)

Pat*_*nik 40

对我来说,这样的伎俩成功了:

height: calc(100vh - calc(100vh - 100%))
Run Code Online (Sandbox Code Playgroud)

  • 这实际上与写入“height: 100%”相同,因为它转换为“100vh - 100vh + 100%”。所以这种做法可以称为“根本不使用vh”。重要提示:为了能够使用“height: 100%”,您必须将该高度从根元素传播到所有子元素,直到目标元素。有时可能太难了 (6认同)
  • 它与 height: 100% 不同吗? (5认同)
  • @SleepWalker说的是正确的,结果与Hight完全相同:100%。 (3认同)

小智 39

将身体位置设置为固定,将高度设置为 100%

body { position: fixed; height: 100% }
Run Code Online (Sandbox Code Playgroud)

就是这样,然后手机浏览器就会明白你想要什么。

现在,无论是否有 URL 栏,或者是否有选项卡(如移动 Safari 中),主体都会随着浏览器的视图高度而增大或缩小。身体总是会得到完整的视野。

  • 这实际上最终对我有用。我在 &lt;main&gt; 元素上使用了上述样式,而不是 body 元素 body,该元素是 body 的直接子元素,它为移动设备上的 Safari、Chrome 和 DuckDuckGo 解决了这个问题。我不确定它是否被认为是“最佳实践”,但如果我在进一步构建网站时发现任何问题,请务必发表评论。 (4认同)
  • 对我来说神奇地有效。不确定这是否可以安全使用。但无论如何都要使用它。如果出现问题将会更新。 (2认同)

man*_*-84 34

看看这个答案: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`);
});
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

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

小智 20

对于我构建的许多网站,客户端将要求100vh横幅,正如您所发现的那样,当您开始滚动时,它会在移动设备上产生糟糕的"跳跃"体验.这就是我如何解决问题,以便在所有设备上获得平稳一致的体验:

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

然后我使用jQuery获取我的banner元素的高度(以像素为单位)并使用此高度应用内联样式.

var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });
Run Code Online (Sandbox Code Playgroud)

这样做解决了移动设备上的问题,就像页面加载时一样,使用CSS将banner元素设置为100vh然后jQuery通过在我的banner元素上放置内联CSS来覆盖它,这会阻止用户开始滚动时调整大小.

但是,在桌面上如果用户调整其浏览器窗口大小,我的banner元素将不会调整大小,因为它现在具有由于上述jQuery而设置的固定高度(以像素为单位).为了解决这个问题,我使用Mobile Detect将"移动"类添加到文档正文中.然后我将上面的jQuery包装在if语句中:

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

因此,如果用户在移动设备上,则"我的页面"主体上会出现"移动"类,并执行上述jQuery.所以我的banner元素只会在移动设备上应用内联CSS,同时在桌面上原始的100vh CSS规则仍然存在.

  • 对于编写实际JavaScript的人来说,这是`document.querySelector('。banner')。style.height = window.innerHeight;`。 (5认同)
  • 我会调整它以使用`$('.banner').css({height:window.innerHeight});`相反,这是视口的"真实"高度.[innerHeight的工作原理示例](https://codepen.io/martinkrulltott/pen/KmJmVm) (4认同)

And*_*erd 18

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

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

在你的CSS中:

:root {
   --app-height: 100%;
}

html,
body {
    padding: 0;
    margin: 0;
    overflow: hidden;
    width: 100vw;
    height: 100vh;

    @media not all and (hover:hover) {
        height: var(--app-height);
    }
}
Run Code Online (Sandbox Code Playgroud)

它至少适用于chrome mobile和ipad.什么不起作用是当您将应用程序添加到iOS上的主屏幕并更改方向几次 - 不知何故缩放级别与innerHeight值混淆,如果我找到解决方案,我可能会发布更新.

演示

  • 我想添加一个警告,即“@media not all and (hover:hover) {”并不是检测移动浏览器的万无一失的方法。随意使用其他东西。 (3认同)

Sar*_* Ak 17

您可以通过添加以下脚本和样式来做到这一点

  function appHeight() {
    const doc = document.documentElement
    doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
  }

  window.addEventListener('resize', appHeight);
  appHeight();
Run Code Online (Sandbox Code Playgroud)

风格

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

  • 这里有完整的描述:https://css-tricks.com/the-trick-to-viewport-units-on-mobile/#css-custom-properties-the-trick-to- Correct-sizing (3认同)

Sti*_*ats 9

这是您需要的技巧https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

的CSS

.my-element {
    height: 100vh;                      /* Fallback for browsers that do not support Custom Properties */
    height: calc(var(--vh, 1vh) * 100);
    transition: 1s;                     /* To avoid a jumpy result */
}
Run Code Online (Sandbox Code Playgroud)

JS

// 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`);
});
Run Code Online (Sandbox Code Playgroud)


Jür*_* P. 8

当我几天在寻找解决方案时,这里是我的每个人使用 VueJS 和 Vuetify(我的解决方案使用 v-app-bar、v-navigation-drawer 和 v-footer):我创建了 App.scss(在 App.scss 中使用)。 vue) 具有以下内容:

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

.v-application--wrap {
    min-height: 100vh !important;
    min-height: -webkit-fill-available !important;
}
Run Code Online (Sandbox Code Playgroud)


小智 7

您可以尝试position: fixed; top: 0; bottom: 0;为您的容器提供属性。

  • 添加更多信息来说明为什么这是问题的答案。 (3认同)

Mik*_*sin 6

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

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

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

小智 5

不幸的是,这个问题至今仍然存在。最大的误导是不可能通过使用浏览器的devices toolbar.

我刚刚解决了这个问题(在 PC、iOS 和 Android 浏览器上测试过):

.your_class {
   height: 100vh,
   max-height: 100%, // <-- add the line
   ...some other props,
}
Run Code Online (Sandbox Code Playgroud)

我希望它能节省您的时间。