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


288

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

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

Як можна уникнути цієї проблеми? Коли я вперше почув про висоту перегляду, я був схвильований і думав, що зможу використовувати його для блоків з фіксованою висотою замість JavaScript, але тепер я думаю, що єдиний спосіб зробити це насправді javascript з деякою подією зміни розміру ...

Ви можете побачити проблему на сайті : зразок

Хтось може мені допомогти з / запропонувати рішення CSS?


простий код тесту:


1
якщо я добре зрозумів питання, з яким ви стикаєтеся в мобільному браузері, висота більше, ніж видима висота огляду .. правильно?
Gaurav Aggarwal

Цікаво, ніколи раніше цього не помічав. В основному це фонова картина, яка помітно стрибка. Як ви можете додати те transition: 0.5sчи інше, щоб зробити зміни менш різкими?
C14L

@GauravAggarwal nope, навпаки: реальна висота вікна перегляду більша за ту, яку надає веб-переглядач, коли видно його адресний рядок ...
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

459
Найбільш гнітюча відповідь.
Winnemucca

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-зображення для фонових зображень
Drenai

143

Ви можете спробувати min-height: -webkit-fill-available;у своєму css замість 100vh. Це слід вирішити


20
Я б залишився min-height: 100vh;як резервний, де -webkit-fill-availableце не підтримується.
Tammy Tee

Нічого, дякую !! З цим мені вже не потрібно "реагувати-дів-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 не все і (hover: hover) {" - це непроникний спосіб виявлення мобільних браузерів. Сміливо використовуйте щось інше.
Андреас Герд

Мені дуже подобається такий підхід. Я хотів би зробити один коментар щодо використання слухачем події. Це спричиняє повторне фарбування сторінки, і я використовую її лише в певних сценаріях, коли користувачеві абсолютно важливо побачити правильний вікно перегляду (тобто початковий перегляд домашньої сторінки)
Зах Голлвіцер

22

Для багатьох сайтів, які я будую, клієнт попросить банер розміром 100 Вт, і так само, як ви знайшли, це призводить до поганого «стрибаючого» досвіду на мобільних пристроях, коли ви починаєте прокручувати. Ось так я вирішую проблему для безперебійної роботи на всіх пристроях:

Я спершу встановив свій елемент банера CSS на height:100vh

Потім я використовую jQuery, щоб отримати висоту в пікселях мого елемента банера, і застосував стиль вбудованої форми, використовуючи цю висоту.

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

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

Однак на робочому столі, якщо користувач змінює розмір вікна свого веб-переглядача, мій елемент банера не змінить розмір, оскільки тепер він має фіксовану висоту, встановлену в пікселях завдяки вищевказаному jQuery. Для вирішення цього питання я використовую програму « Мобільне виявлення», щоб додати клас «мобільний» до тіла свого документа. А потім я загортаю вище jQuery у оператор if:

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

Як результат, якщо користувач перебуває на мобільному пристрої, клас "мобільний" присутній на тілі моєї сторінки, і вищевказаний jQuery виконується. Таким чином, мій елемент банера отримає лише вбудований CSS, застосований на мобільних пристроях, тим часом на робочому столі оригінальне правило CSS у форматі 100vh залишається в силі.


4
Я хотів би налаштувати її $('.banner').css({ height: window.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>


2
розумне рішення, я зіткнувся з проблемою з висотою вікна перегляду на мобільних пристроях, це слід сприймати і як рішення (спеціально для мобільних пристроїв)
Julio Vedovatto

дуже приємне рішення. У моєму випадку я не використовував ponyfill, а скоріше логічно, що для всіх настільних сайтів я використовую 100vh, тоді як для мобільних я використовую var (у нас немає мобільного IE11).
FrEaKmAn

Єдине рішення, яке працювало для вкладених елементів. У кого б то не було замінити пару сотень екземплярів, ви можете (принаймні більшу частину часу) співставити з regexp (-)? ([\ D.] +) Vh і замінити його на calc (var (- vh) * $ 1 $ 2)
ecc521

10

Я придумав компонент React - перевірте це, якщо ви використовуєте React чи переглядаєте вихідний код, якщо цього не зробите, щоб ви могли адаптувати його до свого середовища.

Він встановлює висоту повноекранного дзвінка до, window.innerHeightа потім оновлює його на розмірах вікна.


3
Не всі герої носять шапки. Ви все добре, наприклад. Велике спасибі. заощаджені години часу.
NarayaN

А для любителів Vue ... github.com/razumnyak/vue-div-100vh
Тоні

6

Коли я шукав рішення кілька днів, ось мій для всіх, хто використовує VueJS з Vuetify (моє рішення використовує v-панель додатків, v-навігаційний ящик та v-footer): я створив App.scss (використовується в додатку. 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, і він заповнюватиметься лише до цієї батьківської висоти). У великому короткому.
Патрик Яник

4

@nils це чітко пояснив.

Що далі?

Я щойно повернувся до використання відносного "класичного" %(відсотка) у CSS.

Часто більше докладати зусиль, щоб реалізувати щось, ніж було б за допомогою vh, але принаймні, у вас є досить стабільне рішення, яке працює на різних пристроях та браузерах без дивних збоїв у користувальницькому інтерфейсі.


1
Висота: 100% для банерів все ще стрибає на мобільних браузерах. Я тестував його на Android, і поки Chrome і Opera Mini дають 100% як видиму висоту VP, і не змінюються при прокручуванні. Edge та Firefox змінюють висоту на 100% та перефарбовують вигляд. Firefox особливо поганий, зі сльозами, перегляд тексту, дуже очевидно, переформатування
Дренай

1
@Drenai Ви впевнені, що правильно це реалізуєте? Чи можете ви довести це на невеликому прикладі на 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 набагато довше, ніж хотілося б, я змінив дизайн свого макета і знайшов спосіб, який працював «нормально» для моїх потреб, але не був універсальним рішенням.
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");


}

-Timeout встановлюється так, щоб пристрій правильно обчислював нову висоту при зміні орієнтації. Якщо тайм-ауту немає, на мій досвід висота не буде правильною. 500 мс може бути надмірним, але спрацювало для мене.

-100vh на ховер-пристроях - це резервний запас, якщо браузер замінює CSS 100vh.


2
Touchstart підтримується не в усіх браузерах, а деякі не мобільні пристрої мають його developer.mozilla.org/en-US/docs/Web/Events/touchstart
Дренай

@Drenai Touchstart підтримується у більшості мобільних браузерів. На робочому столі підтримка трохи менше, IE, Opera і Safari не підтримують її. Однак оригінальне питання спрямоване на мобільний, а не на робочий стіл. Що робить це правильною відповіддю.
Пол,

Дякуємо за коментарі! Я в основному використовував цю тактику для подолання перенастроювання та стрибкості екрана в деяких мобільних браузерах. Якщо браузер не підтримує сенсорний запуск, резервним кодом буде css 100vh - якщо 100vh не підтримується, це вже інша тема. Якщо настільний пристрій має сенсорну підтримку, то висота обчислюється за допомогою JavaScript. Потім ми втрачаємо повторний розрахунок розміру, який забезпечує 100Вт, але це взагалі не фатальна проблема, оскільки на робочому столі немає змін орієнтації.
tjgoodman

Також розрахунок висоти також може бути доданий для зміни події, але тоді ми можемо зіткнутися з тією самою точною проблемою перестроювання та стрибкості на мобільних пристроях. Тому я визнав цю тактику практичною.
tjgoodman

1
@Paul Якщо у браузерів настільних пристроїв Chrome та Firefox є сенсорний запуск, то, напевно, це неправильна функція для виявлення мобільного браузера? Я щойно перевірив це в Chrome і Firefox у Windows, і обидва пройшли цей тест
Дренай

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 - 120 пікселів

iPhone 8 - 80 пікселів

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яке відповідає вашим потребам.


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)

Отримавши правильну висоту перегляду, ви можете створити декілька знаків з точним відсотком від загальної висоти вікна перегляду, просто змінивши множник у кожному ділі, якому ви призначите висоту.

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


-1

Ви можете спробувати надати position: fixed; top: 0; bottom: 0;властивості контейнеру.


2
Додайте більше інформації про те, чому це відповідь на питання.
Заражений Дрейк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.