От автора: во всех мобильных браузерах есть два вьюпорта. Вьюпорт макета ограничивается вашими стилями (width: 100% означает 100% вьюпорта макета), а визуальный вьюпорт описывает только видимую область страницы. В качестве напоминалки можно использовать эту визуализацию. В сегодняшней статье мы узнаем, что случается, когда эти вьюпорты меняют размер. Также мы изучим событие resize.
Некоторые изменения вьюпорта приветствуются. Например, изменение визуального вьюпорта при смене ориентации или зуме. Другие известны лишь посвященным, например, перезапись значения мета тега вьюпорта. Остальное лишь раздражает, особенно появление и исчезновение панелей инструментов в браузере и программной клавиатуры.
Событие resize
Событие resize должно срабатывать при любом изменении размеров любого из двух вьюпортов. На десктопе все так и происходит, в основном потому, что оба вьюпорта равны окну браузера, а изменение размера окна браузера запускает это событие со времен Netscape 3.
На мобильных устройствах все сложнее. Возьмите пирожок с полки, если слышали такой термин как «браузерная зависимость»: ваша паранойя достигла таких высот, что вы можете заниматься фундаментальными исследованиями браузеров. Добро пожаловать в мой мир.
Событие resize точно срабатывает при смене ориентации, а также когда появляется или скрывается панель инструментов браузера. В других ситуациях событие может не сработать.
Что общего у смены ориентации и панели инструментов? Я понятия не имею. Мне непонятно, почему именно эти два примера точно вызывают событие resize, а другие, более важные нет.
Вы поняли, к чему все идет? Ну так вот: таблица совместимости. Не задавайте слишком много логики. Различия в браузерах не совсем логичны. Несколько важных различий мы рассмотрим чуть ниже в этой статье.
Самая большая проблема – отсутствие события zoom. У изменения ориентации уже давно есть свое личное событие, а у масштабирования, что, возможно, более важно, такого события нет.
Я на протяжении шести лет добиваюсь ввода события zoom. Было бы очень полезно знать, увеличил ли пользователь страницу или нет. Тем не менее, со мной не согласен почти ни один браузер. А те, кто согласен (только Edge), запускают событие resize. Это помогает немного, но данное событие срабатывает и в других обстоятельствах.
Общие изменения визуальных вьюпортов
Забавный факт: зум и изменение ориентации меняют размеры визуального вьюпорта. Вы могли бы сказать, что это одно и то же, но это не так.
Все браузеры запускают событие resize при смене ориентации пользователем. (Кто любит мозговой штурм, подумайте, что будет, если пользователь изменит ориентацию на 180 градусов. Будет ли это изменение ориентации или размера?)
Тем не менее, только Edge, BlackBerry 10 и Android WebKit WebViews (но не стандартные браузеры) запускают событие resize при изменении масштаба. Поэтому в большинстве браузеров невозможно понять, масштабирована ли страница пользователем или нет. Ну, можно постоянно запускать скрипт на проверку размера визуального вьюпорта, но тогда удар по производительности будет… давайте не будем этого делать, ладно?
В некоторых ситуациях очень важно знать, масштабировал ли пользователь страницу. Особенно когда используется position: fixed, и нужно знать, видит ли пользователь фиксированный элемент полностью. (В теории, можно решить эту задачу с помощью position: device-fixed, однако на данный момент значение работает только в Edge.)
С тех пор как Apple включили зум везде, его определение стало еще более важной задачей. Дни немасштабируемых страниц сочтены, а это значит, что определенные дизайны должны знать, когда пользователь увеличил или уменьшил страницу. Мы же до сих пор не можем определить этого. Уважаемые разработчики браузеров, пожалуйста, добавьте событие zoom. Спасибо.
Смена мета вьюпорта
Мета тег вьюпорта можно переписывать на лету, принудительно заставляя браузеры изменять размер вьюпорта макета. Однако полностью удалить тег невозможно. Вот так (код ниже работает во всех браузерах):
1 2 |
var metaViewport = document.querySelector('meta[name=viewport]'); metaViewport.setAttribute('width','380'); |
Конечно, эффект довольно мутный. Я узнал про него очень давно, но за эти годы так и не смог найти ему хорошего, практического применения. Кроме того, сам эффект довольно неказист. Веб-страница изменяется резко, а это может дезориентировать пользователей.
Изменение размеров вьюпорта макета запускает событие resize. Исключение составляет Safari/iOS. Поэтому браузеры правильно обрабатывают этот неясный пограничный случай, на всякий случай.
Панели инструментов и клавиатуры
Вот мы и дошли до самого главного: выезжающие и исчезающие панели инструментов и клавиатуры. Стандартная браузерная панель инструментов содержит адресную строку и, возможно, кнопки назад и перезагрузить, что занимает примерно 60px вертикального пространства. Это пространство не является частью окна браузера, что наводит на мысль, что высота визуального вьюпорта на 60px меньше размера экрана.
Большинство мобильных браузеров скрывают адресную строку, когда пользователь прокручивает страницу вниз, и показывают, когда страницу прокручивают вверх. Хорошее интерфейсное решение, однако оно вызывает изменение визуального вьюпорта примерно на 60px, а это может вызвать проблемы в некоторых случаях.
Например, Jeremy недавно заметил, что единицы измерения vh, которые очень удобно использовать в адаптивных дизайнах, не срабатывают из-за изменения визуального вьюпорта. Если задать элементу высоту height: 100vh (т.е. 100% высоты визуального вьюпорта), то по началу все работает нормально. Однако как только пользователь начинает прокручивать страницу, высота визуального вьюпорта увеличивается на 60px, и элемент становится выше на 60px. Это может сломать макет страницы.
Jeremy также обратил внимание, что эту проблему невозможно решить только с помощью CSS. Можно задействовать JS, что мы чуть ниже увидим, но это может замедлить страницы. Поэтому решение должно поступить от самих разработчиков браузеров.
Похоже, команда Chrome уже работает над решением. Начиная с Chromium 56, 100vh будет высчитываться относительно максимальной высоты визуального вьюпорта, т.е. без панелей инструментов и клавиатур, несмотря на то, видимы они или нет. А window.innerHeight так и будет реагировать на выезжающие или существующие панели инструментов, давая правильную высоту визуального вьюпорта.
Круто, но прямо сейчас это может не сработать. Подход работает только в последней версии Google Chrome. Это лучшее решение, и боюсь, остается только ждать.
Клавиатуры в Safari/iOS
В iOS (удивительно!) есть свои проблемы. Во всех остальных браузерах программная клавиатура уменьшает окно браузера, а значит, и визуальный вьюпорт. В iOS программная клавиатура является независимым слоем, отображаемым поверх окна браузера. Окно браузера никак не реагирует на появление или исчезновение клавиатуры. Визуальный вьюпорт не меняется, событие resize не срабатывает.
Я копался больше дня, но признал свое поражение: клавиатуру нельзя обнаружить. Изменения вьюпорта, медиа запросы, соотношение сторон, другие события типа размытия: ничто не указывает на появление программной клавиатуры в Safari.
Заключение
В итоге мы остаемся с неполной картиной. Изменение размеров вьюпортов работает почти всегда, за исключением клавиатуры в Safari. На данный момент JS события не позволяют точно отследить все изменения. В частности, нам нужно событие zoom.
Если ваш проект требует детального знания высоты визуального вьюпорта, вам не повезло. Тем не менее, разработчики браузеров все больше прислушиваются к требованиям обычных разработчиков, поэтому вполне возможно, что скоро будут представлены решения этих проблем.
Автор: Peter-Paul Koch
Источник: //medium.com/
Редакция: Команда webformyself.