CSS3 100vh не является постоянным в мобильном браузере

у меня очень странный вопрос... в каждом браузере и мобильной версии я столкнулся с этим поведением:

  • все браузер имеет верхнее меню при загрузке страницы (показывая адресную строку, например), которые скользят вверх при запуске прокрутки страницы.
  • 100vh рассчитываются только на видимой части окна просмотра, поэтому при скольжении панели браузера вверх 100vh увеличивается (в пикселях)
  • весь макет перекрасить и повторно настроить, так как размеры имеют изменено
  • bad jumpy эффект для пользовательского опыта

как можно избежать этой проблемы? Когда я впервые услышал о viewport-height, я был взволнован, и я думал, что могу использовать его для фиксированных блоков высоты, но теперь я думаю, что единственный способ сделать это на самом деле 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 sould 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;
}
html lang-html prettyprint-override"><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>

8 ответов


к сожалению, это умышленное...

Это хорошо известная проблема (по крайней мере, в safari mobile), которая является преднамеренной, поскольку она предотвращает другие проблемы. Бенджамин Пулен ответил на ошибку webkit:

Это совершенно намеренно. Для достижения этого эффекта потребовалось немало усилий с нашей стороны. :)

основная проблема заключается в следующем:видимая область изменяется динамически при прокрутке. Если мы обновим CSS высота окна просмотра соответственно, нам нужно обновить макет во время прокрутки. Мало того, что это выглядит как дерьмо, но делать это при 60 FPS практически невозможно на большинстве страниц (60 FPS-базовая частота кадров на iOS).

трудно показать вам часть "выглядит как дерьмо", но представьте, что при прокрутке содержимое перемещается, и то, что вы хотите на экране, постоянно меняется.

динамическое обновление высоты не работало, у нас было несколько вариантов: drop viewport единиц на iOS, соответствует размеру документа, как до iOS 8, использовать небольшой размер, большой размер.

из данных, которые у нас были, использование большего размера представления было лучшим компромиссом. Большинство веб-сайтов, использующих viewport, большую часть времени выглядели великолепно.

Николас Хойзи исследовал это совсем немного: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html

исправление запланировано

на данный момент Вы не можете ничего сделать, кроме как воздержаться от использования высоты видового экрана на мобильных устройствах. Mobile Chrome, кажется, хочет адаптировать это тоже, хотя он не уверен, что они будут следовать через.


для многих сайтов, которые я создаю, клиент попросит баннер 100vh, и так же, как вы нашли, это приводит к плохому "нервному" опыту на мобильном телефоне, когда вы начинаете прокручивать. Вот как я решаю проблему для плавного последовательного опыта на всех устройствах:

сначала я установил свой элемент баннера CSS в height:100vh

затем я использую jQuery, чтобы получить высоту в пикселях моего элемента баннера и применить встроенный стиль, используя эту высоту.

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

этим решает проблему на мобильных устройствах, как при загрузке страницы, элемент баннера установлен в 100vh с помощью CSS, а затем jQuery переопределяет это, помещая встроенный CSS на мой элемент баннера, который останавливает его от изменения размера, когда пользователь начинает прокручивать.

однако на рабочем столе, если пользователь изменяет размер окна браузера, мой элемент баннера не будет изменяться, потому что теперь он имеет фиксированную высоту, установленную в пикселях из-за вышеуказанного jQuery. Для решения этой проблемы я использую Мобильный Обнаружить добавить класс' mobile ' в тело моего документа. И затем я обертываю вышеуказанный jQuery в Оператор if:

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

в результате, если пользователь находится на мобильном устройстве, класс "mobile" присутствует в теле моей страницы и выполняется вышеуказанный jQuery. Таким образом, мой элемент баннера получит только встроенный CSS, примененный на мобильных устройствах, а на рабочем столе оригинальное правило 100VH CSS остается на месте.


в моем приложении я делаю это так (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 mobile и ipad. Что не работает, так это когда вы добавляете свое приложение на рабочий стол на iOS и меняете ориентацию несколько раз - каким-то образом уровни масштабирования мешают значению innerHeight, я могу опубликовать обновление, если найду решение для него.

демо


Я только что нашел веб-приложение, которое я разработал, имеет эту проблему с iPhones и iPads и нашел статью, предлагающую решить ее с помощью медиа-запросов, ориентированных на конкретные устройства Apple.

Я не знаю, Могу ли я поделиться кодом из этой статьи здесь, но адрес такой:http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

выход из статьи: "просто сопоставьте высоту элемента с высотой устройства, используя медиа-запросы, которые нацелены более старые версии разрешения iPhone и iPad."

Они добавили только 6 медиа-запросов для адаптации элементов полной высоты, и он должен работать, поскольку он полностью реализован CSS.

Edit pending: я не могу проверить его прямо сейчас, но я вернусь и сообщу о своих результатах.


Я придумал компонент React -зацените Если вы используете React или просмотр исходного кода Если вы этого не сделаете, то вы можете приспособить его к вашей окружающей среде.

Он устанавливает высоту полноэкранного div в window.innerHeight а затем обновляет его при изменении размера окна.


@nils объяснил это ясно.

что дальше?

Я просто вернулся, чтобы использовать относительную "классику"% (процент) в CSS.

это часто больше усилий, чтобы реализовать то, чем он будет использовать vh, но, по крайней мере, у вас есть довольно стабильное решение, которое работает на разных устройствах и браузерах без странных сбоев пользовательского интерфейса.


Как я новичок, я не могу комментировать другие ответы.

Если кто - то ищет ответ, чтобы сделать эту работу (и может использовать 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");


}

- тайм-аут установлен так, что прибор высчитал бы новую высоту правильно на изменении ориентации. Если нет тайм-аута, по моему опыту высота будет не правильно. 500ms может быть переусердствовать, но имеет работать на меня.

- 100vh на hover-устройствах является резервным, если браузер переопределяет CSS 100vh.


надеюсь, это будет UA-определенная переменная среды CSS, как предложено здесь:https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-397536046