我有一个很奇怪的问题…在每个浏览器和手机版本中,我都遇到了这种行为:

当你加载页面时,所有的浏览器都有一个顶部菜单(例如显示地址栏),当你开始滚动页面时,它会向上滑动。 100vh有时只在视口的可见部分计算,所以当浏览器栏向上滑动时,100vh增加(以像素计算)。 所有布局重新油漆和重新调整,因为尺寸已经改变 对用户体验有不好的跳跃效果

如何避免这个问题?当我第一次听说viewport-height时,我很兴奋,我认为我可以使用它来固定高度块,而不是使用javascript,但现在我认为唯一的方法实际上是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>


不幸的是,这是故意的……

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

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年也变成了这样:

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


我刚刚发现我设计的一个web应用程序在iphone和ipad上有这个问题,并发现一篇文章建议使用针对特定苹果设备的媒体查询来解决这个问题。

我不知道是否可以在这里分享那篇文章中的代码,但是地址是:http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

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

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

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


对于我所创建的许多网站,客户端都会要求100vh的横幅,正如你所发现的那样,当你开始滚动时,它会导致糟糕的“跳跃”体验。这就是我如何在所有设备上获得流畅一致的体验:

我首先设置我的横幅元素CSS的高度:100vh

然后我使用jQuery来获得我的横幅元素的像素高度,并使用这个高度应用内联样式。

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

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

然而,在桌面上,如果用户调整他们的浏览器窗口大小,我的横幅元素不会调整大小,因为它现在有一个固定的高度像素设置由于上述jQuery。为了解决这个问题,我使用Mobile Detect在我的文档正文中添加了一个“Mobile”类。然后我将上面的jQuery封装在if语句中:

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

因此,如果用户在移动设备上,“mobile”类就会出现在我的页面主体上,并且会执行上面的jQuery。所以我的横幅元素只会得到内联CSS应用在移动设备上,同时在桌面上,原来的100vh CSS规则仍然存在。


因为我是新人,我不能对其他答案发表评论。

如果有人正在寻找一个答案,使这个工作(可以使用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");


}

-Timeout设置,以便设备在方向改变时正确计算新的高度。如果没有超时,根据我的经验,高度是不正确的。500毫秒可能有点过分,但对我来说很有效。

如果浏览器覆盖CSS的100vh,则-100vh在hover-devices上是一个备用选项。


@nils解释得很清楚。

接下来呢?

我只是回到使用相对'经典' %(百分比)在CSS。

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


我提出了一个React组件——如果你使用React,请查看它,如果你不使用React,请浏览源代码,这样你就可以使它适应你的环境。

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


在我的应用程序,我这样做(typescript和嵌套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值,我可能会发布一个更新,如果我找到一个解决方案。

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更快、更纯粹的解决方案,因为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-container with height: 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>


你可以试着给出位置:fixed;上图:0;底部:0;属性到您的容器。


你可以试试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为一个工作的高度(但是你需要为你正在编码的每种类型的设备输入这个值)

or

使用javascript获取窗口高度,然后在加载和刷新视口时更新——devheight(然而这需要使用javascript,不是一个纯css解决方案)

一旦你获得正确的视图高度,你可以创建多个div在总视口高度的确切百分比,只需改变乘数在每个div你分配的高度。

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

希望这可以帮助到一些人;)


下面是我在React应用程序中使用的一个方法。

iPhone 11 Pro和iPhone Pro Max - 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 { 身高:100 vh; 高度:-webkit-fill-available; } .v-application——包装{ Min-height: 100vh ! important: -webkit-fill-available; }


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

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栏消失时,元素的高度就不会跳跃。


关于这个问题及其可能的解决方案可以在这篇博客文章中找到:在100vh布局中寻址iOS地址栏

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


Brave浏览器在iOS上的表现不同(有bug ?)它根据显示/隐藏地址栏动态改变视口高度。这有点烦人,因为它改变页面的布局取决于vw/vh单位。

Chrome和Safari都没问题。


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

当你的应用程序被一个带有“下巴”的浏览器加载时,添加一个类。

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中工作,以修复下巴/高度计算问题。然而,它破坏了Android Chrome和Firefox也有bug。

似乎-webkit-fill-available在某种程度上是匆忙加入webkit的,也许是苹果偶然采用的,作为下巴/高度计算的修复?

它依赖于内在的大小,这是不安全的使用。


我通过将最外层的div放在position: absolute,然后设置高度为100%来解决这个问题:

CSS:

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

HTML:

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

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

body { position: fixed; height: 100% }

就这样,然后移动浏览器就会理解你想要什么。

现在,主体将随着浏览器的视图高度而增长或缩小,无论是否有URL栏,或者是否有标签(如在移动safari中)。身体总是能看到完整的画面。


看起来CSS修复是不可靠的,JS一个工作得很好,但元素在用户打开页面时跳跃。

我解决了这个问题使用JS从其他答案+淡出动画隐藏跳跃。

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


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

不要把vh和px混合


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

要展示如何高度:100vh的高度可以导致滚动在移动chrome浏览器:

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

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


解决方案使用位置:固定解决问题和纯CSS:

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

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


找到这个答案的人正在与移动视图上向下/向上滚动的固定位置的元素的连接跳跃问题作斗争,根元素需要在底部而不是顶部指定属性,并且应该在px中给出一个字段值。这就解决了问题

改变: 元素{ 位置:固定; 上图:90年vh; 左:90 vh; 转换:翻译(-95%,-95%); }

:

元素{ 位置:固定; 底部:90 px;/*这是更改*/ 左:90 vh; 转换:翻译(-95%,-95%); }


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

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

例如,当搜索栏在视图中或不在移动中,100%与变化,其中100vh将粘在屏幕高度的第一个值


我们有新的视口单元lvh, svh和dvh来拯救。这在最新的谷歌I/O 2022网络作品视频中得到了证明。

您可能希望继续使用dvh,以便浏览器在滚动时适应移动设备的隐藏选项卡。它的工作方式类似于宽度dvw, lvw和svw单位。

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

我能用吗?

这是目前工作在我的Chrome金丝雀与标志“实验功能”启用。


不幸的是,这个问题至今仍然存在。最大的误导是不可能用浏览器的设备工具栏来表示情况。

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

.your_class {
   height: 100vh,
   max-height: 100%, // <-- add the line
   ...some other props,
}

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


问题:

if ( refDOM.clientHeight != window.innerHeight )

解决方案:

refDOM.style.setProperty('--MYvh', window.innerHeight + 'px' ) ;

完成:

const mobileShit = function ( refDOM ) {
    console.log ( `refDOM.clientHeight : ${refDOM.clientHeight}` ) ;
    console.log ( `window.innerHeight : ${window.innerHeight}` ) ;
    if ( refDOM.clientHeight != window.innerHeight ) { // accounting for mobile
        refDOM.style.setProperty('--MYvh', window.innerHeight + 'px' ) ;
    }
} ;

CSS(通过脚本修改,是的,当任何事情发生变化时,你必须处理事件):

:root {
    --MYvh : 100vh ;
}

React用useEffect和useState挂钩解决方案

    function App() {
      const [vh, setVh] = useState(window.innerHeight);
    
      useEffect(() => {
        const updateVh = () => {
          setVh(window.innerHeight);
        };
    
        window.addEventListener('resize', updateVh);
    
        return () => window.removeEventListener('resize', updateVh);
      }, []);
    
      return (
        <div style={{ height: vh }}>
          {vh} px
        </div>
      );
}

演示:https://jsfiddle.net/poooow/k570nfd9/