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

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

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

Как можно избежать этой проблемы? Когда я впервые услышал о viewport-height, я был взволнован и подумал, что смогу использовать его для блоков с фиксированной высотой вместо javascript, но теперь я думаю, что единственный способ сделать это - фактически javascript с некоторым событием resize...

Вы можете увидеть проблему по адресу: образец сайта

Может кто-нибудь помочь мне/предложить решение 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>

Ответы

Ответ 1

К сожалению, это намеренно...

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

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

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

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

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

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

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

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

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

Ответ 2

Вы можете попробовать min-height: -webkit-fill-available; в вашем css вместо 100vh. Должно быть решено

Ответ 3

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

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

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

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

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

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

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

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

Ответ 4

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

демонстрация

Ответ 5

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

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

Ответ 6

Это трюк, который вам понадобится 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');
});

Ответ 7

Посмотрите на этот ответ: 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>

Ответ 8

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

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

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

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

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

Ответ 9

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

Что дальше?

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

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

Ответ 10

Следующий код решил проблему (с помощью jQuery).

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

А другие элементы используют % как единицу для замены vh.

Ответ 11

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

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

-First, я использую скрипт, чтобы определить, является ли устройство сенсорным или зависшим. Пример с обнаженными костями:

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

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

Это добавляет класс к элементу body в соответствии с типом устройства (указатель мыши или касание), который можно использовать позже для сценария высоты.

-Next используйте этот код для установки высоты устройства под нагрузкой и при изменении ориентации:

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 мс может быть переусердствовать, но это сработало для меня.

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

Ответ 12

Следующее работало для меня:

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% заставит этот контейнер принять видимую высоту области просмотра.

Ответ 14

Поскольку это не будет исправлено, вы можете сделать что-то вроде:

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

# css
.content {
  height: 80vh;
}

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

Просто используйте правильное значение vh которое соответствует вашим потребностям.

Ответ 15

Использование 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, чтобы получить высоту окна, а затем обновите --devHeight при загрузке и обновлении области просмотра (однако это требует использования javascript и не является чистым решением css)

Как только вы получите правильную высоту просмотра, вы можете создать несколько элементов div с точным процентом от общей высоты области просмотра, просто изменив множитель в каждом элементе div, которому вы назначаете высоту.

0,10 = 10% высоты обзора 0,57 = 57% высоты обзора

Надеюсь, это может кому-то помочь;)

Ответ 16

Эта проблема беспокоила меня, что верхний полноразмерный баннер оставляет белый промежуток между собой и следующим разделом. Поэтому я использовал следующий чистый CSS-подход, чтобы исправить эту проблему. Хотя на самом деле он не избавляется от пробелов, вместо этого он заполняет его цветом фона следующего раздела, чтобы они выглядели как непрерывные. Это может быть полезно в некоторых условиях.

<div class="full-screen-slider">
......
</div>
<main id="maincontent">
</main>
<style>
#maincontent {
    padding-top: 20vh;
    margin-top: -20vh;
    background-color: #f3f3f3;
}
</style>

Ответ 17

попробуйте это, я надеюсь, это сработает

минимальная высота: 100vh;

Ответ 18

Вы можете попробовать указать position: fixed; top: 0; bottom: 0; position: fixed; top: 0; bottom: 0; свойства вашего контейнера.