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

288

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

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

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

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

Может кто-нибудь помочь мне / предложить решение CSS?


простой тестовый код:

Nereo Costacurta
источник
1
если я правильно понял вопрос, проблема, с которой вы сталкиваетесь, заключается в том, что в мобильном браузере высота больше, чем видимая высота области просмотра. верно?
Гаурав Аггарвал
Интересно, никогда не замечал этого раньше. Это в основном фоновая картинка, которая заметно нервная. Как насчет того, чтобы добавить transition: 0.5sили сделать так, чтобы изменение было менее резким?
C14L
@GauravAggarwal Нет, прямо противоположное: реальная высота области просмотра больше, чем та, которую предоставляет браузер, когда видна его адресная строка ...
Nereo Costacurta
1
Поскольку мой вопрос становится популярным, я хотел бы дать свои 5 центов: не будет ли разумнее поддерживать реальную высоту окна и только скользить вверх по строке меню? это не кажется таким сложным. На самом деле все должно быть проще ... пальцем вверх -> строка меню скользит вверх до невидимости, пальцем вниз -> строка меню скользит вниз до полной видимости ... все вместе с телом без каких-либо настроек и скачкообразного эффекта ...
Nereo Costacurta
4
У Google есть хорошая информация по этому вопросу: developers.google.com/web/updates/2016/12/url-bar-resizing. Вы можете использовать 100% вместо 100vh, если вы изменили рост на 100%
Benisburgers

Ответы:

203

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

Это хорошо известная проблема (по крайней мере, в 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 году:

Нильс
источник
7
Да, как я думаю. Единственное жизнеспособное решение - это решение на основе сценариев. Из того, что я знаю, $(window).height()эта ошибка не затрагивается, поэтому я пойду таким образом. Спасибо!
Nereo Costacurta
3
Скрипт был для меня единственным способом и работал отлично.
captDaylight
461
Самый удручающий ответ.
Виннемукка
15
Начиная с версии Chrome 56 значение vh всегда рассчитывается так, как будто строка URL скрыта, и поэтому значение vh не увеличивается при прокрутке, кроме position:fixed. Это похоже на реализацию в Safari. Подробнее: developers.google.com/web/updates/2016/12/url-bar-resizing
Кевин Фарругия,
4
Последний Chrome не меняет vhили <html> 100%высоту после начальной загрузки. Это на самом деле замечательно - поскольку раскрутка / перекомпоновка макета, когда скрывается панель URL, может выглядеть ужасно. Firefoxи Edge Mobileвсе еще динамически обновляет vhи <html>высоту, вызывая много разрывов и изменения размера всех компонентов при прокрутке, особенно плохо при использовании SVG для фоновых изображений
Дренай
143

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

Саурабх Джайн
источник
20
Я бы оставил min-height: 100vh;в качестве запасного варианта, где -webkit-fill-availableне поддерживается.
Тэмми Ти
Вау, спасибо !! При этом мне больше не нужен «response-div-100vh»!
Андрес Элизондо
1
самый полезный ответ!
Готье
@TammyTee прав, лучше придерживаться min-height: 100vh;этого, так как в противном случае он будет работать в браузерах, таких как Firefox (по крайней мере, на настольных компьютерах iOS использует webkit независимо от того, какой браузер).
JoniVR
2
Это было очень хорошее решение, но, похоже, оно изменилось в iOS 13 - я вижу дополнительное место в нижней части первого раздела, но оно работает точно так, как нужно в Safari для iOS 12.x codepen.io/RwwL/pen / PowjvPq (вам придется раскошелиться, а затем просмотреть его в режиме отладки CodePen на iOS, чтобы действительно понять, что я имею в виду)
RwwL
27

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

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

Андреас Херд
источник
2
Это на самом деле фантастический подход к очень раздражающей проблеме.
Майкл Джованни Пумо
1
Я хотел бы добавить предупреждение, что «@media not all и (hover: hover) {» не является пуленепробиваемым способом обнаружения мобильных браузеров. Не стесняйтесь использовать что-то еще.
Андреас Херд
Мне очень нравится этот подход. Один комментарий, который я хотел бы сделать, касается использования прослушивателя событий. Это вызывает перерисовку страницы, и я использую ее только в определенных сценариях, когда абсолютно необходимо, чтобы пользователь видел правильный видовой экран (т. Е. Начальный вид домашней страницы)
Зак Гольвитцер,
22

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

Сначала я установил свой элемент баннера 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 });
}

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


источник
4
Я бы настроил его, чтобы использовать $('.banner').css({ height: window.innerHeight });вместо этого, что является «реальной» высотой области просмотра. Пример того, как работает innerHeight
Мартин
8
Что document.querySelector('.banner').style.height = window.innerHeight;для людей, пишущих настоящий JavaScript.
Фабиан фон Эллертс
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>

Мануэл-84
источник
2
умное решение, я столкнулся с проблемой с высотой области просмотра на мобильных устройствах, это также должно быть принято как решение (особенно для мобильных устройств)
Хулио Ведоватто
очень хорошее решение. В моем случае я использовал не ponyfill, а скорее логику, что для всех настольных сайтов я использую 100vh, а для мобильных я использую var (у нас нет IE11 в мобильном мире).
FrEaKmAn
Единственное решение, которое работает для вложенных элементов. Если кому-то еще нужно заменить пару сотен экземпляров, вы можете (по крайней мере, большую часть времени) сопоставить регулярное выражение (-)? ([\ D.] +) Vh и заменить его на calc (var (- vh) * $ 1 $ 2)
ecc521
10

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

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

Михаил Васин
источник
3
Не все герои носят кепки. Вы в порядке, например, этого. Большое спасибо. сэкономили часы времени.
НараяN
И для любителей Vue ... github.com/razumnyak/vue-div-100vh
Тони О'Хаган
6

Поскольку я искал решение несколько дней, вот мое решение для всех, кто использует VueJS с Vuetify (мое решение использует v-app-bar, v-navigation-box и 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% заполняют только всю доступную родительскую область (родительский блок может иметь, например, высоту 50 пикселей, и он будет заполняться только до этой родительской высоты). Короче говоря.
Патрик Яник
4

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

Что дальше?

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

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

Klimat
источник
1
Высота: 100% для баннеров по-прежнему прыгает в мобильных браузерах. Я тестировал его на Android, и до сих пор Chrome и Opera Mini выдают 100% видимой высоты ПО и не изменяются при прокрутке. Edge и Firefox изменяют высоту на 100% и перерисовывают вид. Firefox особенно плох, со слезами, текст перерисован, очень явно
изменен
1
@ Дренай, ты уверен, что правильно это реализовал? Можете ли вы доказать это на небольшом примере на jsfiddle.net или около того?
Климат
Да, я уверен :-)
Дренаи
3

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

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

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

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

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

Иозиил
источник
23
все еще жду тех результатов 😉
gman
2
Извините, я не вернулся, как обещал. Но после того, как я поработал с HTML и CSS гораздо дольше, чем мне бы хотелось, я изменил дизайн своего макета и нашел способ, который работал «хорошо» для моих нужд, но не был универсальным решением.
Джахазиил
2

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

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

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

tjgoodman
источник
2
Touchstart поддерживается не во всех браузерах, и на некоторых не мобильных устройствах он есть developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai
@Drenai Touchstart поддерживается в большинстве мобильных браузеров. На десктопе поддержка чуть меньше, IE, Opera и Safari его не поддерживают. Однако оригинальный вопрос нацелен на мобильные, а не на настольные компьютеры. Что делает это правильным ответом.
Пол
Спасибо за комментарии! Я использовал эту тактику главным образом для преодоления перенастройки и перепада экрана на некоторых мобильных браузерах. Если браузер не поддерживает сенсорный запуск, резервным вариантом будет css 100vh - если 100vh не поддерживается, это другая тема. Если у настольного устройства есть поддержка касания, тогда высота будет рассчитана с помощью JavaScript. Затем мы теряем пересчет при изменении размера, который обеспечивает 100vh, но это не является фатальной проблемой в целом, так как на рабочем столе нет изменений ориентации.
tjgoodman
Кроме того, вычисление высоты также может быть добавлено к событию изменения размера, но тогда мы можем столкнуться с той же самой проблемой перенастройки и перескоков на мобильных устройствах. Поэтому я считаю эту тактику практичной.
tjgoodman
1
@Paul Если в настольных браузерах Chrome и Firefox есть сенсорный запуск, то, безусловно, это неправильная функция для обнаружения мобильного браузера? Я только что проверил его в Chrome и Firefox на Windows, и оба прошли этот тест
Drenai
2

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

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

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

Ray1422
источник
2

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

iPhone 11 Pro и iPhone Pro Max - 120 пикселей

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

Рико Калер
источник
0

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

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

# css
.content {
  height: 80vh;
}

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

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

Turkus
источник
0

Использование 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% высоты обзора

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

Рэй Андисон
источник
1
Это хорошее решение. Вы можете прочитать больше об этом в этой статье о трюках Css .
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);
}
Сарат Ак
источник
0

Попробуйте html, body { height: 100% }что-нибудь с эффектом 100vh на мобильных устройствах.

Сэм Бокай
источник
-1

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

Хасан восстание
источник
2
Добавьте больше информации о том, почему это ответ на вопрос.
Зараженный Дрейк