CSS3 100vh 在移动浏览器中不是常量

CSS3 100vh not constant in mobile browser

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

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

您可以在以下位置查看问题:sample site

任何人都可以帮助我/提出 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>

不幸的是这是故意的……

这是一个众所周知的问题(至少在 safari 移动版中),这是有意为之的,因为它可以防止其他问题。 Benjamin Poulain replied to a webkit bug:

This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)

The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).

It is hard to show you the “looks like shit” part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.

Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.

From the data we had, using the larger view size was the best compromise. Most website using viewport units were looking great most of the time.

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 2016年也改成这样:

我刚刚发现我设计的一个网络应用程序在 iPhone 和 iPad 上存在这个问题,并且发现一篇文章建议使用针对特定 Apple 设备的媒体查询来解决它。

不知道能不能把那篇文章的代码分享到这里,地址是这样的:http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

引用文章:"just match the element height with the device height using media queries that targets the older versions of iPhone and iPad resolution."

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

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

对于我构建的许多网站,客户端会要求 100vh 横幅,正如您所发现的,当您开始滚动时,它会导致糟糕的 "jumpy" 移动体验。这就是我解决问题的方法,以便在所有设备上获得流畅一致的体验:

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

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

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

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

但是,如果用户在桌面上调整其浏览器的大小 window,我的横幅元素将不会调整大小,因为由于上述 jQuery,它现在具有以像素为单位的固定高度设置。为了解决这个问题,我使用 Mobile Detect 将 'mobile' class 添加到我的文档正文中。然后我将上面的 jQuery 包装在一个 if 语句中:

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

因此,如果用户使用移动设备,class 'mobile' 会出现在我的页面正文中,并且会执行上面的 jQuery。因此,我的横幅元素只会在移动设备上应用内联 CSS,而在桌面设备上,原始 100vh CSS 规则仍然存在。

由于我是新人,我无法评论其他答案。

如果有人正在寻找使这项工作有效的答案(并且可以使用 javascript - 因为目前似乎需要它才能使这项工作有效)这种方法对我来说效果很好并且它也考虑了移动方向的变化。我使用 Jquery 作为示例代码,但应该可以使用 vanillaJS。

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

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

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

这会根据稍后可用于高度脚本的设备类型(悬停或触摸)将 class 添加到 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");


}

-设置了超时,以便设备可以在方向更改时正确计算新高度。如果没有超时,根据我的经验,高度将不正确。 500 毫秒可能有点过头了,但对我有用。

如果浏览器覆盖 CSS 100vh,hover-devices 上的

-100vh 是后备。

@nils 解释的很清楚。

接下来呢?

刚回去用相对'classic'%(百分比)在CSS.

实现某些东西通常比使用 vh 更费力,但至少,您有一个相当稳定的解决方案,可以跨不同的设备和浏览器工作,没有奇怪的 UI 故障。

我想到了一个 React 组件 – check it out if you use React or browse the source code 如果你没有,那么你可以根据你的环境调整它。

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

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

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

在你的 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);
    }
}

它至少适用于 chrome 移动设备和 ipad。当您将应用程序添加到 iOS 上的主屏幕并多次更改方向时,不起作用的是 - 不知何故缩放级别与 innerHeight 值混淆,如果我找到一个 post 更新解决方法。

Demo

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

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

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

因为不会被修复,你可以这样做:

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

# css
.content {
  height: 80vh;
}

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

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

以下对我有用:

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% 将使该容器采用可见视口高度。

看看这个答案: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>

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

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

在移动设备上使用 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 获取 window 高度,然后在加载和刷新视口时更新 --devheight (但这确实需要使用 javascript 并且不是纯粹的 css解)

获得正确的视图高度后,您可以创建多个 div,只需更改分配给高度的每个 div 中的倍数,即可按总视口高度的精确百分比创建多个 div。

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

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

这是我用于我的 React 应用程序的解决方法。

iPhone 11 专业版 & iPhone 专业版最大 - 120px

iPhone 8 - 80px

max-height: calc(100vh - 120px);

这是一个妥协但相对简单的修复

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

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

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

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

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

您可以通过添加以下脚本和样式来实现

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);
}

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

VH 100 在移动设备上运行不佳,因为它没有考虑 iOS 栏(或其他平台上的类似功能)。

一种行之有效的解决方案是使用 JavaScript "window.innerHeight".

只需将元素的高度分配给该值,例如 $('.element-name').height(window.innerHeight);

注意:在JS中创建一个函数可能会很有用,这样当屏幕尺寸改变时高度可以改变。但是,我建议只在屏幕宽度发生变化时调用该函数,这样当用户向下滚动页面时 iOS 栏消失时元素不会跳跃高度。

可以在这个博客 post:Addressing the iOS Address Bar in 100vh Layouts

中找到有关该问题及其可能解决方案的精彩读物

我在 React 应用程序中最终得到的解决方案是利用上面 post 中描述的 react-div-100vh 库。

iOS 上的 Brave 浏览器表现不同(错误?)。它会根据 showing/hiding 地址栏动态更改视口高度。这有点烦人,因为它会根据 vw/vh 个单位更改页面布局。

Chrome Safari 没问题。

不要使用推荐的方法,例如 -webkit-fill-available
我只是花了一整天的时间来修复这个“错误”。

当您的应用程序使用带有“下巴”的浏览器加载时添加 class。

JavaScript

// Angular example but applicable for any JS solution
@HostBinding('class.browser-has-chin') browserHasChin: boolean = false;

public ngOnInit(): void {
    this.browserHasChin = this._isMobileSafari();
}

private _isMobileSafari() {
    return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/) ? true : false;
}

CSS

.browser-has-chin {
  @media screen and (max-device-width: 767px){
    // offset with padding or something
  }
}

备注:
-webkit-fill-available 道具在跨浏览器兼容性方面存在重大问题。

我能够在 Chrome 和 iOS Safari 中使用它来修复 chin/height 计算问题。但是它损坏了 Android Chrome 并且 Firefox 也有错误。

似乎 -webkit-fill-available 在某些时候被匆忙加入了 webkit 并且可能被 Apple 随意采用作为 chin/height 计算的修复?

它依赖于 intrinsic sizing,但尚不能安全使用。

我通过将最外面的 div 放在 position: absolute 处然后将 height 设置为 100% 来解决它:

CSS:

.outer {
    position: absolute;
    height: 100%;
}

HTML:

<div class="outer">
    <!-- content -->
</div>

将您的 body 位置设置为固定,将高度设置为 100%

body { position: fixed; height: 100% }

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

现在 body 会随着浏览器的视图高度增大或缩小,无论是否有 URL 栏,或者是否有标签(如在移动 safari 中) . body 将始终获得完整视图。

似乎 CSS 修复不可靠,JS 一个工作正常但是当用户打开页面时元素跳转。

我使用 JS from other answers + fadeIn animation 隐藏跳跃解决了这个问题。

这并不适合所有用例,但对于其中一些用例,例如必须位于底部的按钮,可能是一个很好的解决方案。

在我的例子中 - 我将一些元素设置为 90vh,将另一个元素设置为 70px 将 70px 更改为 10vh 解决了它

Never mix vh with px units

我在下面创建了两个示例:

  1. 展示 height: 100vh 作为高度如何导致在移动 chrome 浏览器中滚动:

代码 : https://codesandbox.io/embed/mobile-viewport-100vh-issue-nxx8z?fontsize=14&hidenavigation=1&theme=dark

演示https://nxx8z.csb.app/


  1. 解决方案使用 position: fixed 来解决问题,并且纯粹使用 CSS:

代码 : https://codesandbox.io/s/mobile-viewport-100vh-issue-fix-forked-ypx5x?file=/index.html

演示 : https://ypx5x.csb.app/

找到这个答案的人正在努力解决滚动时移动视图上元素的奇怪跳跃问题downward/upward其位置是固定的w.r.t根元素需要指定属性 在底部而不是顶部,应该给出一个以 px 为单位的字段值。这解决了问题

改变这个:

element{
position: fixed;
top: 90vh;
left: 90vh;
transform: translate(-95%, -95%);


}

对此:

    element{
    position: fixed;
   bottom: 90px; /* this is the change */
    left: 90vh;
    transform: translate(-95%, -95%);


    }

body {
   width: 100%;
   height: 100%
}

100% 会根据屏幕尺寸自行调整,但出于某种原因 100vh 在屏幕变化时调整有问题所以使用底部 0 将受到影响因此如果你使用 100vh 或 100%

例如,当搜索栏在移动设备中可见或不可见时,100% 会发生变化,其中 100vh 将贴在屏幕高度的第一个值上

我们有新的视口单元 lvhsvhdvh 来拯救。这在最新的 Google I/O 2022 网络作品视频中得到了证明。

您可能希望坚持使用 dvh,以便浏览器在滚动时适应移动设备的隐藏标签。对于 dvwlvwsvw 单位的宽度,它的工作方式类似。

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

Can I use?

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