web-dev-qa-db-ja.com

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

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

  • ページをロードすると(たとえばアドレスバーを表示すると)、すべてのブラウザにトップメニューが表示されます。これは、ページのスクロールを開始するとスライドします。
  • 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>
158

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

これはよく知られている問題であり(少なくともSafariモバイルでは)、他の問題を防ぐため意図的なものです。 Benjamin Poulainがウェブキットのバグに回答しました

これは完全に意図的なものです。この効果を達成するには、私たちの側でかなりの作業が必要でした。 :)

基本的な問題はこれです:あなたがスクロールするにつれて可視領域は動的に変化します。それに応じてCSSビューポートの高さを更新する場合は、スクロール中にレイアウトを更新する必要があります。それがたわごとのように見えるだけでなく、60 FPSでそれをすることはほとんどのページで事実上不可能です(60 FPSはiOSのベースラインフレームレートです)。

「たわごとのように見える」部分を見せるのは難しいですが、スクロールするとコンテンツが移動し、画面上で欲しいものが絶えずシフトしていると想像してください。

動的に高さを更新するのはうまくいきませんでした、我々はいくつかの選択肢を持っていました:iOSの上でビューポート単位を落とす、iOS 8の前のようにドキュメントサイズを合わせる、小さいビューサイズを使う、大きいビューサイズを使う。

私たちが持っていたデータから、より大きいビューサイズを使うことが最も良い妥協案でした。ビューポートユニットを使用しているほとんどのWebサイトは、ほとんどの場合見栄えが良かったです。

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もこれに変わりました。

117
nils

私がクライアントを構築しているサイトの多くは100vhのバナーを要求しますが、あなたが見つけたように、あなたがスクロールし始めたとき、それはモバイルで悪い「飛び跳ねる」経験をもたらします。これは私がすべてのデバイスにわたってスムーズで一貫した経験のために問題を解決する方法です:

最初にバナー要素CSSをheight:100vhに設定します

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

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

これを行うと、モバイルデバイスでページがロードされたときにCSSを使用してbanner要素が100vhに設定され、jQueryがこれをオーバーライドしてユーザーのスクロール時にサイズ変更されないようになります。

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

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

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

19
user6261119

私のアプリでは、私はそうしています(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);
    }
}

それは少なくともクロムモバイルとiPadで動作します。 iOSのホームスクリーンにアプリを追加して向きを数回変更するとうまくいかないのですが、どういうわけかズームレベルがinnerHeightの値と混同します。解決策が見つかった場合は更新を投稿することもあります。

デモ

14
Andreas Herd

あなたはmin-height: -webkit-fill-available;の代わりにあなたのcssで100vhを試すことができます。それは解決されるべきです

5
Saurabh Jain

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

これが必要なトリックです 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 */
}

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`);
});
3
Stijn Slats

私はReactコンポーネントを思いついた - それをチェックアウトする あなたがReactを使うなら/ ソースコードを閲覧するなら あなたがそうしないなら - あなたはあなたの環境にそれを適応できる。

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

3
Mike

私がデザインしたWebアプリがiPhoneとiPadでこの問題を抱えているのを見つけた。そして、特定のAppleデバイスを対象としたメディアクエリを使ってそれを解決することを提案する記事を見つけた。

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

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

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

保留中の編集:今すぐテストすることはできませんが、戻ってきて結果を報告します。

3
Jahaziel

@ nilsはそれを明確に説明しました。

次は何ですか?

私はCSSで相対的な 'classic'%(percentage) を使い始めました。

多くの場合、vhを使用するよりも何かを実装する方が労力がかかりますが、少なくとも、奇妙なUIグリッチを発生させることなく、さまざまなデバイスやブラウザで機能する、かなり安定したソリューションがあります。

3
klimat

以下のコードは(jQueryを使って)問題を解決しました。

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

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

2
Ray1422

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

誰かがこの仕事をするための答えを探しているならば(そして今のところこの仕事をするのに必要と思われるので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");


}

- デバイスが向きの変更時に新しい高さを正しく計算するようにタイムアウトを設定します。タイムアウトがない場合、私の経験では高さは正しくありません。 500ミリ秒は無理かもしれませんが私のために働いてきました。

ブラウザがCSS 100vhを上書きする場合、hover-devicesの-100vhはフォールバックです。

1
t.j.goodman

以下は私のために働いた:

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を付けたheight: 100%はそのコンテナに可視のビューポートの高さを取ります。

1
Rico Kahler

モバイルデバイスで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%

これが誰かに役立つことを願っています;)

0
Ray Andison

あなたはあなたのコンテナにposition: fixed; top: 0; bottom: 0;プロパティを与えることを試みることができます。

0
Hasan upinion

it は修正されないので、次のようにすることができます。

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

# css
.content {
  height: 80vh;
}

私にとっては、JavaScriptを使用するよりも速くて純粋な解決策であり、多くのデバイスやブラウザでは機能しませんでした。

ニーズに合った適切な値のvhを使用するだけです。

0
turkus

うまくいけば、これはここで示唆されるようにUA定義のCSS環境変数になるでしょう: https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-397536046

0
Malvoz