← Назад к вопросам

В чем нюанс vh на мобильных устройствах?

2.0 Middle🔥 131 комментариев
#HTML и CSS

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

В чем нюанс vh на мобильных устройствах?

vh (viewport height) на мобильных устройствах имеет критический баг — высота браузера меняется при появлении и исчезновении адресной строки и других элементов интерфейса. Это вызывает прыганье контента, что портит пользовательский опыт.

Проблема: динамическая высота viewport

На мобильных устройствах браузер имеет две разные высоты:

  1. Initial viewport height — высота браузера с адресной строкой видимой
  2. Dynamic viewport height — высота браузера, когда адресная строка скрыта при прокрутке
/* Проблемный код */
body {
  height: 100vh; /* 100vh = текущая высота viewport */
}

/* На мобиле:
   - При открытии страницы: 100vh = 667px (с адресной строкой)
   - При прокрутке вверх: 100vh = 750px (без адресной строки)
   - Результат: контент прыгает! */

Это происходит потому, что браузер пересчитывает 1vh при каждом изменении видимой части интерфейса.

Решение 1: использовать 100dvh (Dynamic Viewport Height)

dvh (dynamic viewport height) — новый стандарт, который всегда представляет фактическую видимую высоту:

/* ✅ Правильно — адресная строка не влияет */
body {
  height: 100dvh;
  overflow: hidden;
}

/* Других вариантов: 100svh (small), 100lvh (large) */

Другие единицы viewport:

/* dvh — dynamic viewport height (высота меняется при изменении адресной строки) */
height: 100dvh; /* рекомендуется */

/* svh — small viewport height (всегда считает адресную строку как видимую) */
height: 100svh; /* не меняется никогда */

/* lvh — large viewport height (всегда считает адресную строку как скрытую) */
height: 100lvh; /* фиксированная большая высота */

Решение 2: JavaScript переменные

Для браузеров, не поддерживающих dvh, используй JavaScript для расчета реальной высоты:

// Установи переменную CSS с реальной высотой
function setViewportHeight() {
  const vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty("--vh", `${vh}px`);
}

// Вызови при загрузке и при изменении размера
setViewportHeight();
window.addEventListener("resize", setViewportHeight);
window.addEventListener("orientationchange", setViewportHeight);
/* Используй переменную вместо vh */
body {
  height: calc(var(--vh, 1vh) * 100);
  overflow: hidden;
}

Решение 3: позиционирование вместо height

Для полноэкранных блоков используй позиционирование вместо height:

/* ❌ Может прыгать */
.full-screen {
  height: 100vh;
}

/* ✅ Не прыгает */
.full-screen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%; /* или 100vh — тут не прыгает с fixed */
}

Практический пример: мобильное приложение

import { useEffect } from react;

function MobileLayout() {
  useEffect(() => {
    // Установь высоту при загрузке
    const handleResize = () => {
      const vh = window.innerHeight * 0.01;
      document.documentElement.style.setProperty(--vh, `${vh}px`);
    };

    handleResize();
    window.addEventListener(resize, handleResize);
    window.addEventListener(orientationchange, handleResize);

    return () => {
      window.removeEventListener(resize, handleResize);
      window.removeEventListener(orientationchange, handleResize);
    };
  }, []);

  return (
    <div className="min-h-dvh flex flex-col">
      <header>Шапка</header>
      <main className="flex-1 overflow-y-auto">Контент</main>
      <footer>Подвал</footer>
    </div>
  );
}

Tailwind CSS решение

В Tailwind используй dvh или CSS переменную:

/* globals.css */
:root {
  --vh: 1vh;
}

@supports (height: 1dvh) {
  :root {
    --vh: 1dvh; /* поддержка dvh */
  }
}
<!-- Используй в классах -->
<div class="h-[calc(var(--vh)*100)]">Полная высота</div>

Сравнение подходов

ПодходПлюсыМинусыКогда использовать
100dvhСовременный стандарт, простоНе поддерживается старыми браузерамиНовые проекты
JavaScript переменнаяРаботает вездеТребует JS кодПоддержка старых браузеров
position: fixedНадежноНе подходит для всех случаевПолноэкранные компоненты
Поднятие viewport (viewport-fit)Решает проблему notchНе универсальноiPhone с notch

Viewport-fit для iPhone с notch

Для устройств с вырезом (notch) используй meta тег:

<!-- В head -->
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
/* Учти safe area */
body {
  padding-top: max(12px, env(safe-area-inset-top));
  padding-bottom: max(12px, env(safe-area-inset-bottom));
  padding-left: max(12px, env(safe-area-inset-left));
  padding-right: max(12px, env(safe-area-inset-right));
}

Ключевые выводы

  • 100vh на мобиле прыгает при скрытии/показе адресной строки
  • 100dvh — новый стандарт, используй его (с fallback)
  • JavaScript window.innerHeight — надежный способ для старых браузеров
  • position: fixed — не прыгает, но не всегда подходит
  • viewport-fit=cover — для безопасных зон на iPhone
  • Всегда тестируй на реальных мобильных устройствах, не только в DevTools