VirtualKeyboard API: границы viewport и inset
Свяжите geometrychange, visualViewport и отступы клавиатуры, чтобы фиксированные действия оставались доступными в мобильной форме.
BotBrowser Team
Нужна структурированная документация по теме Платформа?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
BotBrowser может повторить разрешённый мобильный fixture с объявленными политикой клавиатуры, профилем и viewport, но не воспроизводит каждую анимацию нативной клавиатуры и не сертифицирует физическое устройство. Практический вопрос уже: когда доверенный редактируемый элемент получает фокус и открывает виртуальную клавиатуру, какая поверхность меняется, какой inset доступен и какое действие остаётся достижимым? Здесь API VirtualKeyboard отделено от общего изменения размера и от выбора профиля в тестах профилей устройств и руководстве по измерению viewport.
Кратко
navigator.virtualKeyboard.overlaysContentявляется политикой страницы: клавиатура может перекрывать контент вместо уменьшения viewport компоновки.geometrychangeсообщаетDOMRectтекущей геометрии клавиатуры; объединяйте его с изменениямиvisualViewportперед записью компоновки.env(keyboard-inset-*)выдаёт CSS-inset только когда браузер и политика их предоставляют. Оставляйте запасной отступ0px.layout viewport,visual viewportи прямоугольник клавиатуры отвечают на разные вопросы. Не заменяйте их однимinnerHeight.- Проверяйте фокус, прокрутку, поворот, масштаб и закрытие клавиатуры как переходы. Снимка открытого состояния недостаточно.
Содержание
- Три координатные поверхности и политика
- Fixture и реализация inset
- Порядок событий и владелец прокрутки
- Матрица границ и возможности BotBrowser
Три координатные поверхности и политика
layout viewport является пространством координат документа. visual viewport является видимой частью после интерфейса браузера, жеста масштабирования или виртуальной клавиатуры. API VirtualKeyboard добавляет прямоугольник клавиатуры, если страница выбрала overlay-режим. Поверхности движутся независимо, поэтому одно значение window.innerHeight не описывает состояние формы с фокусом.
| Поверхность | На какой вопрос отвечает | Пример поля |
|---|---|---|
layout viewport | Каков размер области компоновки документа? | window.innerHeight |
visual viewport | Что пользователь видит сейчас? | visualViewport.height |
| Прямоугольник клавиатуры | Какие пиксели закрыты виртуальной клавиатурой? | virtualKeyboard.boundingRect |
При изменении размера из-за клавиатуры viewport компоновки может уменьшиться, а прямоугольник клавиатуры стать нулевым или исчезнуть из полезной области. В overlay-режиме viewport компоновки может остаться стабильным, пока клавиатура закрывает нижний край. Руководство по изменению viewport описывает порядок событий; здесь добавлен явный контракт политики и CSS-inset.
Не вычисляйте высоту клавиатуры только как innerHeight - visualViewport.height, не записав chrome браузера и масштаб. Это оценка, а не гарантия API. Для overlay предпочитайте virtualKeyboard.boundingRect, а для видимой области сохраняйте visualViewport.height, offsetTop и scale.
navigator.virtualKeyboard.overlaysContent = true сообщает совместимому браузеру, что страница сама обработает закрытую область. Браузер не сдвинет автоматически каждый фиксированный footer. Используйте геометрию или CSS environment variables и оставляйте рабочий путь закрытия и фокуса. Назначайте политику до открытия клавиатуры, а не при каждом фокусе.
Записывайте наличие API, результат присваивания overlaysContent, метку профиля и выбранный CSS-путь. Профиль для компьютера может проигнорировать опцию; это граница возможностей, а не признак пользователя.
Fixture и реализация inset
Fixture фиксирует действие отправки, даёт форме прокручиваемую область и пишет обезличенную запись геометрии. В неё не попадают текст, идентификаторы аккаунтов, события клавиш или содержимое клавиатуры. Не меняйте разметку, пока меняете одну строку матрицы.
<main class="screen">
<form class="card" id="checkout">
<label>Email <input name="email" type="email" autocomplete="email" /></label>
<label>Address <input name="address" autocomplete="street-address" /></label>
<p class="status" role="status" aria-live="polite"></p>
<button class="submit" type="submit">Continue</button>
</form>
</main>
<script>
const vk = navigator.virtualKeyboard;
if (vk) {
vk.overlaysContent = true;
vk.addEventListener('geometrychange', () => {
const r = vk.boundingRect;
document.documentElement.style.setProperty('--vk-bottom', `${r.height}px`);
console.log(JSON.stringify({ type: 'keyboard', height: r.height, top: r.top }));
});
}
</script>
:root {
--vk-bottom: 0px;
}
.screen {
min-block-size: 100dvh;
padding: 1rem;
}
.card {
min-block-size: 140vh;
padding-block-end: calc(5rem + var(--vk-bottom));
}
.submit {
position: fixed;
inset-inline: 1rem;
inset-block-end: calc(1rem + var(--vk-bottom) + env(safe-area-inset-bottom, 0px));
}
@supports (padding: env(keyboard-inset-bottom)) {
.card {
padding-block-end: calc(5rem + env(keyboard-inset-bottom));
}
.submit {
inset-block-end: calc(1rem + env(keyboard-inset-bottom) + env(safe-area-inset-bottom, 0px));
}
}
JavaScript custom property нужна движкам, которые видят геометрию, но не CSS environment variable. Блок @supports проверяет только разбор синтаксиса, а не ненулевое значение keyboard-inset-bottom. Проверяйте вычисленный padding после фокуса и закрытия. 0px корректен, когда клавиатура скрыта, overlay выключен или функция не поддерживается.
Переменные keyboard-inset-top, keyboard-inset-right, keyboard-inset-bottom и keyboard-inset-left, а также варианты ширины и высоты описывают закрытый край. Они помогают отодвинуть фиксированное действие или зарезервировать место в scroll-контейнере, но не заменяют safe-area-inset-*. У устройства могут быть и клавиатура, и safe area индикатора дома. Объединяйте ограничения через max() или calc() и используйте logical properties для RTL или вертикального письма.
Если после закрытия резервный путь оставляет слишком много места, очищайте custom property на следующем geometry event, а не прячьте действие отрицательным смещением. Обычный поток должен работать при старте JavaScript, восстановлении навигации и в профиле без navigator.virtualKeyboard.
Порядок событий и владелец прокрутки
geometrychange может прийти рядом с visualViewport.resize и visualViewport.scroll. Они описывают связанную смену, но их порядок не гарантирован. Используйте один scheduler и читайте значения один раз за animation frame:
let pending = false;
function scheduleLayout() {
if (pending) return;
pending = true;
requestAnimationFrame(() => {
pending = false;
const vv = window.visualViewport;
const k = navigator.virtualKeyboard?.boundingRect;
document.documentElement.dataset.keyboard = k && k.height > 0 ? 'open' : 'closed';
console.log(
JSON.stringify({
layoutHeight: window.innerHeight,
visualHeight: vv?.height ?? null,
visualOffsetTop: vv?.offsetTop ?? null,
keyboardHeight: k?.height ?? 0,
})
);
});
}
window.visualViewport?.addEventListener('resize', scheduleLayout);
window.visualViewport?.addEventListener('scroll', scheduleLayout);
navigator.virtualKeyboard?.addEventListener('geometrychange', scheduleLayout);
Запись отличает переход клавиатуры от browser chrome или pinch zoom. Строка с keyboardHeight: 0 и меньшим visualHeight не доказывает закрытие: это может быть toolbar или масштаб. Сохраняйте scale, offsetTop и прямоугольник сфокусированного элемента. Одна запись за frame не даёт конкурирующим обработчикам применить устаревший padding.
Если фокус попал в поле ниже видимой области, браузер может прокрутить документ или внутренний overflow: auto контейнер. Фиксированная кнопка может быть видна при скрытом поле, либо поле видно, а кнопка закрыта. После прокрутки проверяйте оба прямоугольника. Выберите одного владельца прокрутки: дождитесь настройки фокуса и исправляйте только когда низ поля выше видимой верхней границы клавиатуры; не вызывайте scrollIntoView() на каждый event.
Поворот и масштаб являются разными переходами. Поворот может изменить viewport и safe-area до события клавиатуры. Масштаб жестом меняет визуальные координаты иначе, чем CSS-размеры. Храните scale, offsetTop и safe-area в отдельных строках матрицы.
Запись результата приёмки
В записи указывайте переход, который не прошёл, а не только факт видимой клавиатуры. Сохраняйте идентификатор строки, сфокусированный control, результат политики, window.innerHeight, visualViewport.height, visualViewport.offsetTop, visualViewport.scale, rectangle клавиатуры, вычисленный нижний padding и итоговый rectangle действия.
{
"row": "D",
"focused": "address",
"overlaysContent": true,
"layoutHeight": 844,
"visualHeight": 512,
"visualOffsetTop": 0,
"keyboardTop": 512,
"keyboardHeight": 332,
"actionBottom": 496,
"keyboardTopMinusActionBottom": 16,
"actionReachable": true
}
Если keyboardHeight положителен и actionBottom больше keyboardTop, действие закрыто. Если действие свободно, а поле ниже keyboardTop, выбран неправильный владелец прокрутки. Если оба прямоугольника безопасны, а visualHeight неожиданно мал, проверьте chrome или zoom до изменения CSS. Не сохраняйте значения полей, аккаунты, raw key events или серийные номера.
Найдите первую расходящуюся поверхность
Если нет navigator.virtualKeyboard, пометьте строку как неподдержанную и оставьте обычный путь viewport. Если свойство есть, но overlaysContent остаётся false, браузер сам управляет изменением размера; нулевой прямоугольник не доказывает закрытие клавиатуры. Если geometrychange приходит, но вычисленный keyboard-inset-bottom равен нулю, используйте JavaScript-прямоугольник и отметьте отсутствие CSS-inset. Если прямоугольник верен, а действие закрыто, проверьте containing block и safe-area. Если действие свободно, а поле скрыто, проверьте единственного владельца прокрутки и момент фокуса.
Сделайте резервный путь проверяемым
Рендерьте ту же форму без navigator.virtualKeyboard, при игнорировании присваивания overlaysContent и при нулевых CSS environment variables. Действие должно оставаться доступным в обычном потоке, а пользователь должен иметь возможность закрыть клавиатуру или продолжить другой ввод. Используйте feature flag для сравнения управляемого и явного режима без изменения разметки; храните его в записи fixture, не в записи fingerprinting.
Матрица границ и возможности BotBrowser
Запускайте один и тот же fixture с новым профилем для каждой строки. Сохраняйте неизменными текст, загрузку шрифтов, масштаб и device scale factor, если строка явно не меняет их. Хорошая ошибка называет строку и первую расходящуюся поверхность, например «в строке D rectangle действия оказался на 18px ниже верха клавиатуры после внутренней прокрутки», а не «сломалась мобильная клавиатура».
| Строка | Изменение | Ожидаемое наблюдение | Значение |
|---|---|---|---|
| A | Политика клавиатуры выключена | Обычный или browser-managed resize | Базовая совместимость |
| B | Overlay и фокус поля | geometrychange, положительный height, действие свободно | Явный контракт клавиатуры |
| C | Клавиатура закрыта | Высота возвращается к 0, старого padding нет | Очистка перехода |
| D | Внутренний scroll-контейнер | Поле и действие остаются доступными | Владелец прокрутки |
| E | Поворот или zoom 200% | Viewport и safe-area меняются независимо | Не клавиатурный переход |
| F | Неподдержанный или компьютерный профиль | API отсутствует или опция проигнорирована | Граница возможности |
Сохраняйте browserVersion, метку профиля, назначение политики, сфокусированный элемент, значения компоновки и visual viewport, прямоугольник клавиатуры, safe-area и финальный прямоугольник действия. Не сохраняйте текст формы и данные клавиш. Повторяйте строку C после каждого открытия, чтобы увидеть устаревший --vk-bottom.
Граница возможностей BotBrowser
BotBrowser поддерживает соответствующие мобильные профили с сенсорной поддержкой, заданной профилем, и документированной опцией --bot-mobile-keyboard. В поддерживаемом профиле доверенный фокус редактируемого поля может уменьшить visualViewport.height, пока viewport компоновки стабилен, поэтому fixture повторяет точки видимой клавиатуры и сенсорного ввода. Профили для компьютера могут проигнорировать опцию. BotBrowser не заменяет физические устройства, системные клавиатуры, их анимации или все варианты раскладки приложений и не гарантирует ненулевые keyboard insets. Для выбора профиля см. обзор качества мобильных профилей.
FAQ
Меняет ли overlaysContent layout viewport?
Он предназначен для overlay клавиатуры, поэтому совместимый браузер может оставить viewport компоновки стабильным. visual viewport и прямоугольник клавиатуры всё равно меняются. Проверяйте innerHeight, visualViewport.height и boundingRect.height для каждого браузера.
Одинаковы ли keyboard insets и safe-area insets?
Нет. Keyboard insets описывают временное перекрытие, а safe-area является краем устройства или браузера для постоянного padding. Объединяйте их через calc() или max() и проверяйте закрытие, чтобы старый padding исчезал.
Должен ли каждый geometrychange вызывать scrollIntoView()?
Нет. Дождитесь завершения прокрутки фокуса, проверьте control и действие в одном frame и исправляйте только когда control остаётся за видимой границей клавиатуры. У scroll-контейнера должен быть один владелец.
Источники
- API VirtualKeyboard (W3C)
- MDN: API VirtualKeyboard
- MDN:
geometrychange - MDN: CSS-функция
env() - Документация BotBrowser по эмуляции устройств
Практический итог
Рассматривайте VirtualKeyboard как контракт политики и геометрии. Храните отдельно компоновку, visual viewport, прямоугольник клавиатуры, безопасную область и владельца прокрутки; объединяйте их события и проверяйте открытие, закрытие, поворот, масштаб и неподдержанные профили. BotBrowser может повторить объявленное мобильное условие и снять обезличенную геометрию, но физическое устройство и нативная клавиатура остаются за пределами этого свидетельства.
Эта статья входит в руководство: Поверхности веб-платформы: карта проверки
Похожие статьи
События viewport на мобильных: порядок, объединение, разметка
ПлатформаCSS Anchor Positioning: границы layout и overlay
ПлатформаПроверка качества мобильного профиля браузера
ПлатформаПроверка профилей устройств: телефон, планшет и компьютер
ПлатформаТестирование профилей Android на компьютере и сервере
ОтпечаткиKeyboard Layout API, локаль и конфиденциальность
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.