CSS3 100vhがモバイルブラウザーで一定ではない


285

私は非常に奇妙な問題を抱えています...すべてのブラウザーとモバイルバージョンでこの動作が発生しました:

  • ページをロードすると、すべてのブラウザにトップメニューが表示され(たとえば、アドレスバーが表示されます)、ページのスクロールを開始すると上にスライドします。
  • 100vh はビューポートの表示部分でのみ計算される場合あるため、ブラウザバーを上にスライドすると、100vhが増加します(ピクセル単位)。
  • 寸法が変更されたため、すべてのレイアウトが再ペイントおよび再調整されます
  • ユーザーエクスペリエンスに悪影響を与える

この問題を回避するにはどうすればよいですか?ビューポートの高さを初めて聞いたときは興奮し、JavaScriptを使用する代わりに固定高さのブロックにそれを使用できると思っていましたが、今、それを行う唯一の方法は、実際にはサイズ変更イベントを伴うJavaScriptだと思います...

あなたは問題を見ることができます:サンプルサイト

誰かがCSSソリューションで私を助ける/提案できますか?


簡単なテストコード:


1
私があなたが直面している問題をよく理解している場合、モバイルブラウザの高さは表示されているビューポートの高さより高いです。
Gaurav Aggarwal 2016年

興味深いことに、これまで気づかなかった。その主に背景画像はびくびくしています。transition: 0.5s変更を急激に少なくするために、aを追加するのはどうですか?
C14L 2016年

@GauravAggarwalいいえ、正反対です。実際のビューポートの高さは、アドレスバーが表示されているときにブラウザが提供する高さよりも高くなります...
Nereo Costacurta

1
私の質問が人気になっているので、私は私の5セントを与えたいと思います。実際のウィンドウの高さを維持し、メニューバーを上にスライドするだけの方が賢明ではないでしょうか。それほど難しくはありません。実際にはもっと簡単になるはずです...指を上に移動->メニューバーを上にスライドして非表示にし、指を下に移動->メニューバーを下にスライドして完全に表示します...再調整やジャンプの影響なしに、すべて本体と一緒にスライドします...
ネレオコスタクルタ2017

4
Googleはこれに関していくつかの良い情報を持っています:developer.google.com/web/updates/2016/12/url-bar-resizing体高を100%に変更した場合、100vhではなく100%を使用できます
Benisburgers

回答:


203

残念ながらこれは意図的なものです…

これはよく知られている問題(少なくともSafariモバイルでは)であり、他の問題を防ぐために意図的なものです。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

修正予定なし

この時点では、モバイルデバイスでビューポートの高さを使用しないことを除いて、実行できることはほとんどありません。2016年にChromeもこれに変更されました。


7
うん、思うに。実行可能なソリューションはスクリプトソリューションだけです。私が知っていることから$(window).height()、このバグの影響を受けないので、私はそうします。ありがとうございました!
Nereo Costacurta

3
スクリプトが私にとって唯一の方法であり、完全に機能しました。
captDaylight 2016

456
これまでで最も憂鬱な答え。
Winnemucca 2017年

15
Chromeバージョン56以降、vhは常にURLバーが非表示であるかのように計算され、したがってを除いて、スクロール時にvhは増加しませんposition:fixed。これはSafariでの実装に似ています。続きを読む:developer.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia

4
最初のロード後、最新のChromeは変化しないvhか、<html> 100%高さが変わりません。URLバーが非表示になっているときのレイアウトのスラッシング/リフローは恐ろしく見える可能性があるため、これは実際には素晴らしいです。Firefoxさらに、Edge Mobile動的に更新vhおよび<html>高さを
調整する

142

min-height: -webkit-fill-available;代わりにあなたのCSSで試すことができます100vh。解決する必要があります


20
サポートされていないmin-height: 100vh;フォールバックとして残しておき-webkit-fill-availableます。
Tammy Tee

わぁ、ありがとう!!これにより、「react-div-100vh」はもう必要ありません。
Andres Elizondo

1
最も役立つ答え!
Gauthier

@TammyTeeは正しいです。min-height: 100vh;そうしないとFirefoxなどのブラウザーで機能しなくなるため、そのままにしておくことをお勧めします(少なくともデスクトップでは、iOSはどのブラウザーでもWebkitを使用します)。
JoniVR、

2
これは非常に良い解決策でしたが、iOS 13で変更されたようです—最初のセクションの下部に余分なスペースが表示されていますが、iOS 12.xのSafariで望みどおりに機能しますcodepen.io/RwwL/pen / PowjvPq(これをフォークしてから、iOSのCodePenデバッグモードで表示して、実際の意味を確認する必要があります)。
RwwL

27

私のアプリではそのようにします(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の値に影響を及ぼしているため、解決策が見つかった場合、更新を投稿する可能性があります。

デモ


2
これは実際には非常に迷惑な問題への素晴らしいアプローチです。
Michael Giovanni Pumo

1
「@media not all and(hover:hover){」はモバイルブラウザを検出する完全な方法ではないという警告を追加したいと思います。他のものを使ってもかまいません。
Andreas Herd

私はこのアプローチがとても好きです。私が言えるコメントの1つは、イベントリスナーの使用についてです。これによりページが再描画され、ユーザーが正しいビューポート(ホームページの最初のビュー)を表示することが絶対的に重要である特定のシナリオでのみ使用します
Zach Gollwitzer

22

私がクライアントを構築する多くのサイトでは、100vhバナーを要求します。あなたが発見したように、スクロールを開始すると、モバイルでの「ジャンプ」エクスペリエンスが低下します。これは、すべてのデバイスでスムーズで一貫したエクスペリエンスを実現するために問題を解決する方法です。

まず、バナー要素のCSSを height:100vh

次に、jQueryを使用してバナー要素のピクセル単位の高さを取得し、この高さを使用してインラインスタイルを適用します。

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

これを実行すると、ページが読み込まれるときにバナー要素が100vhに設定され、CSSを使用してバナー要素にインラインCSSが配置されるため、jQueryはこれを上書きし、ユーザーがスクロールを開始したときにサイズ変更を停止します。

ただし、デスクトップでは、ユーザーがブラウザーウィンドウのサイズを変更すると、バナー要素はサイズ変更されません。これは、上記のjQueryにより、ピクセル単位で高さが固定されているためです。これに対処するには、モバイル検出を使用して「モバイル」クラスをドキュメントの本文に追加します。次に、上記のjQueryをifステートメントでラップします。

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

その結果、ユーザーがモバイルデバイスを使用している場合は、ページの本文にクラス「mobile」が存在し、上記のjQueryが実行されます。したがって、私のバナー要素は、モバイルデバイスに適用されるインラインCSSのみを取得しますが、デスクトップでは、元の100vh CSSルールがそのまま残ります。


4
$('.banner').css({ height: window.innerHeight });代わりに使用するように微調整します。これは、ビューポートの「実際の」高さです。 innerHeightのしくみの例
マーティン

8
これはdocument.querySelector('.banner').style.height = window.innerHeight;実際のJavaScriptを書いている人向けです。
Fabian von Ellerts

18

この答えを見てください: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>


2
巧妙な解決策、モバイルデバイスのビューポートの高さに問題がありました。これも解決策として受け入れられるはずです(特にモバイルデバイスの場合)
Julio Vedovatto

非常に素晴らしいソリューション。私の場合、私はポニーフィルを使用しませんでしたが、すべてのデスクトップサイトでは100vhを使用するというロジックを使用していますが、モバイルではvarを使用しています(モバイルの世界にはIE11はありません)。
FrEaKmAn

ネストされた要素に対して機能する唯一のソリューション。他の誰かが数百のインスタンスを置き換える必要がある場合、(少なくともほとんどの場合)正規表現(-)?([\ d。] +)vhを一致させ、それをcalc(var(-vh)*に置き換えることができます。 $ 1 $ 2)
ecc521

10

Reactコンポーネントを思いつきました。React を使用しているかどうかを確認し、使用していない場合はソースコードを参照して、環境に適応できるようにしてください。

フルスクリーンdivの高さをに設定し、window.innerHeightウィンドウのサイズ変更時に更新します。


3
すべてのヒーローが帽子をかぶっているわけではありません。あなたはそれで大丈夫です。どうもありがとうございました。時間を節約しました。
NarayaN 2018

そしてVue愛好家のために... github.com/razumnyak/vue-div-100vh
Tony O'Hagan

6

私は数日ソリューションを探していたので、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;
}


1
これはうまく機能し、Vue / Vuetifyの外部で一般化できます。
レオ

5

私にとってそのようなトリックは仕事をしました:

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

高さとは異なりますか:100%?
FF_Dev

はい、100vhは、ビューポートの高さ(デバイス)の100%です。純粋な100%は、利用可能なすべての親領域のみを塗りつぶします(親ブロックは、たとえば50pxの高さを持つことができ、この親の高さまでのみ塗りつぶされます)。要するに。
Patryk Janik

4

@nilsが明確に説明しました。

次は何ですか?

CSSで相対的な「クラシック」%(パーセンテージ)を使用するように戻りました。

多くの場合、実装するよりも何かを実装する方が手間がかかりvhますが、少なくとも、奇妙なUIの不具合なしにさまざまなデバイスやブラウザで機能するかなり安定したソリューションがあります。


1
高さ:バナーの100%は、モバイルブラウザでもジャンプします。私はこれをAndroidでテストしていますが、これまでのところ、ChromeとOpera Miniは表示されるVPの高さを100%としており、スクロールしても変化しません。EdgeとFirefoxは100%の高さを変更し、ビューを再描画します。Firefoxは特にひどく、引き裂かれ、テキストが再レンダリングされ、非常に明らかに再レイアウトされる
Drenai

1
@Drenaiこれを正しく実装してよろしいですか?jsfiddle.netの小さな例でそれを証明できますか?
klimat

はい、きっと:-)
Drenai

3

iPhoneとiPadでこの問題が発生するように設計したWebアプリを見つけたところ、特定のAppleデバイスを対象としたメディアクエリを使用してそれを解決することを提案する記事を見つけました。

その記事のコードをここで共有できるかどうかはわかりませんが、アドレスは次のとおりです:http : //webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

記事の引用:「iPhoneとiPadの古いバージョンの解像度を対象とするメディアクエリを使用して、要素の高さをデバイスの高さに合わせるだけです。」

彼らは、フルハイトの要素を適応させるためにわずか6つのメディアクエリを追加しました。CSSが完全に実装されているため、機能するはずです。

編集保留中:現在はテストできませんが、戻って結果を報告します。


23
まだ😉それらの結果を待っている
gman

2
約束通り戻ってこなかったのは残念です。しかし、HTMLとCSSを思いがけないほど長くいじった後、レイアウトのデザインを変更し、自分のニーズに「問題なく」機能する方法を見つけましたが、万能なソリューションではありませんでした。
Jahaziel

2

私は新しいので、他の答えについてコメントすることはできません。

誰かがこの作業を行うための答えを探している場合(そして、現時点でこの作業を行うために必要と思われるため、javascriptを使用できます)、このアプローチは私にとってかなりうまくいき、モバイルの向きの変化も説明します。サンプルコードにはJqueryを使用していますが、vanillaJSで実行できるはずです。

-まず、スクリプトを使用して、デバイスがタッチしているかホバーしているかを検出します。ベアボーンの例:

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

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

これにより、後で高さスクリプトに使用できるデバイスの種類(ホバーまたはタッチ)に応じて、ボディ要素にクラスが追加されます。

-次に、このコードを使用して、ロード時および方向変更時のデバイスの高さを設定します。

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はフォールバックです。


2
タッチスタートはすべてのブラウザーでサポートされているわけではなく、一部の非モバイルデバイスではdeveloper.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai

@Drenai Touchstartは、ほとんどのモバイルブラウザーでサポートされています。デスクトップでのサポートはやや少なく、IE、Opera、Safariはサポートしていません。ただし、元の質問はデスクトップではなくモバイルを対象としています。これは有効な答えになります。
ポール

コメントありがとうございます!私は主にこの戦術を使用して、一部のモバイルブラウザーでの画面の再調整とジャンプを克服しました。ブラウザがタッチスタートをサポートしていない場合、フォールバックはcss 100vhになります-100vhがサポートされていない場合は別のトピックになります。デスクトップデバイスにタッチサポートがある場合、高さはJavaScriptで計算されます。その後、100vhが提供するサイズ変更時に再計算が失われますが、デスクトップでは向きが変更されないため、これは一般に致命的な問題ではありません。
tjgoodman 2018

また、高さ計算をサイズ変更イベントに添付することもできますが、モバイルデバイスでの再調整とジャンプの問題とまったく同じ問題に遭遇する可能性があります。したがって、この戦術は実用的であることがわかりました。
tjgoodman 2018

1
@Paul ChromeおよびFirefoxデスクトップブラウザーにタッチスタート機能がある場合、モバイルブラウザーの検出に使用するのは間違いなく間違った機能ですか。ChromeとWindowsのFirefoxでテストしたところ、どちらもこのテストに合格しました
Drenai

2

次のコードは問題を解決しました(jQueryを使用)。

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

そして、他の要素は%、置き換えるための単位として使用しvhます。


2

これが、Reactアプリで使用した回避策です。

iPhone 11 ProおよびiPhone Pro Max-120px

iPhone 8-80px

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

それは妥協ですが、比較的簡単な修正です


1

以下は私のために働きました:

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%、コンテナが見えるビューポートの高さを取ることになります。



0

ので、それが固定されることはありません、あなたのような何かを行うことができます:

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

# css
.content {
  height: 80vh;
}

私にとっては、多くのデバイスやブラウザで動作しないJavaScriptを使用するよりも速くて純粋なソリューションでした。

vhニーズに合った適切な値を使用してください。


0

モバイルデバイスでvhを使用しても100vhでは機能しません。デバイスの高さ全体を使用する設計上の選択のため、アドレスバーなどは含まれません。

実際のビューの高さに比例したdivの高さを含むレイアウトを探している場合は、次の純粋なcssソリューションを使用します。

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

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

ビューポートの高さを設定するには2つのオプションがあります。手動で--devHeightを機能する高さに設定します(ただし、コーディングするデバイスのタイプごとにこの値を入力する必要があります)

または

JavaScriptを使用してウィンドウの高さを取得し、ビューポートの読み込みと更新時に--devheightを更新します(ただし、これにはJavaScriptを使用する必要があり、純粋なCSSソリューションではありません)。

正しいビューの高さを取得したら、高さを割り当てる各divの乗数を変更するだけで、ビューポート全体の高さの正確なパーセンテージで複数のdivを作成できます。

0.10 =ビューの高さの10%0.57 =ビューの高さの57%

これが誰かを助けるかもしれないことを願っています;)


1
それは素晴らしい解決策です。この詳細については、Css-tricksのこの記事を参照してください
Amaury Hanser

0

これを行うには、次のスクリプトとスタイルを追加します

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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.