前端知识库(lizh)
  • README
  • Bugs
    • 前端调试随笔
    • 浏览器常见问题概览
    • 浏览器兼容问题概览
    • HTML常见问题概览
    • CSS常见问题概览
    • JS常见问题概览
    • 移动端兼容性问题概览
    • 微信小程序开发
    • NodeJs常见问题概览
    • Mac常见问题概览
    • 微信开发遇坑指南
    • Npm包常见问题概览
    • 其他问题汇总
  • Css探索系列
    • CSS基础知识
    • CSS常见问答
    • CSS常见问答02
    • CSS应用示例
    • CSS应用示例02
    • 由Z Index引发的层叠上下文思考
    • 由浮动塌陷引发的块级格式上下文思考
    • CSS探索系列 Flex布局
    • CSS探索系列 Margin
    • CSS探索系列 Auto关键字
    • CSS探索系列 Gradient
    • CSS探索系列 Line Height
    • CSS探索系列 元素居中
    • CSS探索系列 动画
    • 为什么使用PostCSS处理CSS?
    • 重新认识伪类与伪元素
    • 自定义表单伪元素样式
    • 如何理解Css中的Display属性
    • 视口和软键盘对视口的影响
    • 关于Css
  • Frontend
    • 00 关于Web前端
    • 01 前端知识概览
    • 02 常用前端库概览
    • 基础 00 前端常见问题01
    • 基础 01 浏览器缓存
    • 基础 02 浏览器工作原理
    • 基础 03 谈谈前端跨源问题及解决方法
    • 进阶 01 Web性能优化
    • 进阶 02 搜索引擎优化(SEO)
    • 进阶 03 前端模块化编程
    • 进阶 04 规范代码:Linter、Prettier、EditorConfig
    • 进阶 11 前端自动化测试
    • 高级 01 前端安全
    • Vue2.X原理篇
    • Vue3初步了解及迁移指南
    • 重读Vue教程
    • React17.X原理篇
    • 你必须知道的React问题
    • 重读React教程
    • 聊一聊Cookie的一些问题
    • 如何理解HTTP响应的状态码
    • HTTP的历史演变及概述
    • Webpack4.X原理篇
    • Webpack基础入门篇
    • Webpack构建优化篇
    • TypeScript使用指南
    • 代码规范
      • 前端规范
      • HTML
      • CSS
      • JS
  • Html探索系列
    • HTML基础知识
    • HTML基础知识02
    • HTML常见问答
    • HTML经典实践用例
    • HTML元素的宽高及位置详解
    • Video元素的使用和常见问题总结
    • Html探索系列 Meta标签
    • DOCTYPE:文档类型与浏览器模式
    • DHTML(动态网页)简介
    • HTML标签详解
    • HTML布局的几种方式
    • HTML全局属性
    • 关于Html
  • Js探索系列
    • 基础知识
    • 常见问答
    • 应用示例
    • 趣味示例
    • 基础篇 05 AJAX
    • 基础篇 06 Window对象
    • 基础篇 07 Error、JSON、Math、Console对象
    • 基础篇 08 History、URL、Screen、Navigator、Location对象
    • 基础篇 09 文档对象模型(DOM)
    • 基础篇 10 Document对象
    • 基础篇 11 Element对象
    • 基础篇 12 Event对象
    • 基础篇 13 键盘、鼠标、触摸事件
    • 基础篇 15 CSS对象模型(CSSOM)
    • 进阶篇 01 Prototype对象和继承
    • 进阶篇 02 Promise对象
    • 进阶篇 07 迭代器(Iterator)
    • 进阶篇 08 Generator和Async函数
    • 进阶篇 09 JavaScript异步编程
    • Date对象和日期时间字符串格式
    • Canvas基础入门篇
    • Canvas进阶篇
    • SVG基础入门篇
    • 四种判断数据类型方法的优缺点
    • 深入理解JavaScript的浅拷贝和深拷贝
    • 谈谈JavaScript的作用域和上下文
    • 复制内容到剪贴板
    • 关于Javascript
  • NodeJs
    • 关于Node.Js
    • Node.Js:三种调试方法
    • Npm包管理器简介及一些机制
    • NPM:Package.Json详解(中文)
    • NPM:从零开始,开发一个软件包
    • NPM:常用命令
    • Node.Js:Fs(文件系统)
    • Node.Js:Global(全局变量)
    • Node.Js:HTTP
    • Node.Js:Module(模块)
    • Node.Js:Path(路径)
    • Node.Js:Readline(逐行读取)
  • Research
    • 极细边框(1px边框)实现方式
    • 如何监控前端异常?
    • H5页面跳转和刷新
    • Web主题切换和个性化定制方法总结
    • Vue SSR(服务端渲染)的简单实现
    • 基于Create React App打造代码规范化的React+Ts项目
    • H5可视化编辑
    • Web常用功能
    • Javascript加密混淆
    • Vue如何导入TypeScript
    • 移动端PDF预览
    • 纯CSS绘制箭头
    • 网站性能测量和优化方法
  • Tech
    • GOOGLE浏览器的搜索技巧
    • Curl的用法指南
    • Sublime3插件篇
    • Charles安装及使用
    • Nginx基础使用
    • 排序算法(Javascript)
    • 代码整洁之道(摘录笔记)
    • Java的24种设计模式与7大原则
    • 观察者和发布订阅模式
  • Tools
    • Git
      • Git基础教程
      • Git常见问题
    • Gitbook
      • Gitbook入门篇
      • Gitbook插件篇
      • Gitbook进阶篇
由 GitBook 提供支持
在本页
  • 布局视口、视觉视口、理想视口的区别
  • 软键盘显示模式
  • iOS 的软键盘模式
  • Android 的软键盘模式
  • 视口尺寸变更
  • 检测软键盘弹出和收起
  • 相关问题
  • 视口单位(Viewport units)
  • 参考资料

这有帮助吗?

  1. Css探索系列

视口和软键盘对视口的影响

上一页如何理解Css中的Display属性下一页关于Css

最后更新于1年前

这有帮助吗?

视口(Viewport)是移动 Web 开发中一个非常重要的概念,最早是由苹果公司在推出 iPhone 手机时发明的,其目的是为了让 iPhone 的小屏幕尽可能完整显示整个网页。通过设置视口,不管网页原始的分辨率尺寸有多大,都能将其缩小显示在手机浏览器上,这样保证网页在手机上看起来更像在桌面浏览器中的样子。在苹果公司引入视口的概念后,大多数的移动开发者也都认同了这个做法。

视口(Viewport)简单来说就是浏览器显示页面内容的屏幕区域。但在移动端浏览器涉及三个视口(Viewport)概念 :布局视口(Layout Viewport)、视觉视口(Visual Viewport)和理想视口(Ideal Viewport)。

三种视口

布局视口、视觉视口、理想视口的区别

布局视口(Layout Viewport)

布局视口(Layout Viewport)是指浏览器在其中绘制网页的视口,它涵盖页面上的所有元素。布局视口的宽度取决于文档内容,不会随用户在屏幕上缩放页面而变化。

获取布局视口:

document.documentElement.clientWidth | document.body.clientWidth

注意: 一般移动端浏览器都默认设置了布局视口的宽度。根据设备的不同,布局视口的默认宽度有可能是 768px、980px 或 1024px等,这个宽度并不适合在手机屏幕中展示。移动端浏览器之所以采用这样的默认设置,是为了解决早期的 PC 端页面在手机上显示的问题。

视觉视口(Visual Viewport)

视觉视口(Visual Viewport)是指是屏幕上实际可见的内容,不包括屏幕键盘、缩放区域之外的区域或任何其他不随页面尺寸缩放的屏幕控件。视觉视口区域的宽度是指浏览器窗口的宽度,而移动设备的浏览器窗口宽度一般也是整个屏幕的宽度。

获取视觉视口:

window.innerWidth

注意: 当用户缩放页面,或者屏幕键盘等用户界面功能开启/关闭时,视觉视口会变化,但布局视口不会改变。

理想视口(Ideal Viewport)

理想视口(Ideal Viewport)是指对设备来讲最理想的视口尺寸,即布局视口和屏幕宽度一致。采用理想视口的方式,可以使网页在移动端浏览器上获得最理想的浏览和阅读的宽度。

设置理想视口:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, minimum-scale=1, user-scalable=no">

获取理想视口:

window.screen.width

软键盘显示模式

软键盘的本质是什么?软键盘其实是一个 Dialog。

InputMethodService 为输入法创建了一个 Dialog,并且将该 Dialog 的 某些参数进行了设置,使之能够在底部或者全屏显示。当点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该 Dialog 显示在底部,或者全屏显示。

Android 定义了一个属性,名字为 windowSoftInputMode,这个属性用于设置 Activity 主窗口与软键盘的交互模式,用于避免软键盘遮挡内容的问题。可以在 AndroidManifet.xml 中对 Activity 进行设置。如:android:windowSoftInputMode=”stateUnchanged|adjustPan”。该属性可选的值有两部分:一部分为软键盘的状态控制,控制软键盘是隐藏还是显示;另一部分是 Activity 窗口的调整,以便腾出空间展示软键盘。

android:windowSoftInputMode 的属性设置必须是下面中的一个值,或一个 ”state” 值加一个 ”adjust” 值的组合,各个值之间用 | 分开:

  • stateUnspecified:未指定状态。 软件默认采用的就是这种交互方式,系统会根据界面采取相应的软键盘的显示模式。

  • stateUnchanged:不改变状态。 当前界面的软键盘状态,取决于上一个界面的软键盘状态,无论是隐藏还是显示。

  • stateHidden:隐藏状态。 软键盘总是被隐藏,不管是否有输入的需求。

  • stateAlwaysHidden:总是隐藏状态。 和 stateHidden 不同的是,当跳转到下个界面,如果下个页面的软键盘是显示的,而我们再次回来的时候,软键盘就会隐藏起来。

  • stateVisible:可见状态。 软键盘总是可见的,即使在界面上没有输入框的情况下也可以强制弹出来。

  • stateAlwaysVisible:总是显示状态。 和 stateVisible 不同的是,当我们跳转到下个界面,如果下个页面软键盘是隐藏的,而我们再次回来的时候,软键盘就会显示出来。

  • adjustUnspecified:未指定模式。 设置软键盘与软件的显示内容之间的显示关系。这个选项也是默认的设置模式。在这中情况下,系统会根据界面选择不同的模式。

  • adjustResize:调整模式。 该模式下,窗口总是调整屏幕的大小用以保证软键盘的显示空间。

  • adjustPan:默认模式。 该模式下,不会调整屏幕大小来保证软键盘的空间,而是采取了另外一种策略,系统会通过布局的移动,来保证用户要进行输入的输入框肯定在用户的视野范围里面,从而让用户可以看到自己输入的内容。

iOS 的软键盘模式

iOS 默认使用 adjustPan 模式。键盘弹出时,visual viewport 尺寸有变化,但 layout viewport 尺寸不变,部分顶部的元素可能被遮挡或挤出可视区域。

在这个自动调整中,layout viewport 尺寸未变,但可能引发 doucment、layout viewport、visual viewport 两两间相对位置的变化。如果 viewport meta 中未指定 user-scalable=no,还会引发 zoom。另外实测显式,软键盘收起时,(由于软键盘弹出)引发的 zoom、滚动变化并不会还原,而仅仅是 visual viewport 尺寸还原。

Android 的软键盘模式

Andriod 默认使用 adjustResize 模式(受版本或其他因素影响,也有可能未指定模式)。键盘弹出时,layout viewport 与 visual viewport 同步缩小。

该模式仍可能引发 document 滚动以便使 focus input 出现在「可见且显眼」的地方。

视口尺寸变更

visual viewport 在如下场景中会变更尺寸:

  • 用户缩放及移动(pan)视图。

  • 设备 orientation 变更。

  • 浏览器工具栏、状态栏等自动隐藏/出现。

  • 设备软键盘弹出/隐藏。

注意: visual viewport 尺寸变更时会触发 visualViewport resize 事件。

layout viewport 在页面初始渲染确定大小后,仅在如下场景下可能改变:

  • 设备 orientation 变更。

  • 浏览器工具栏、状态栏等自动隐藏/出现。

  • 设备软键盘弹出/隐藏。

其中前两个场景,可以视作 visual viewport 尺寸变更后,浏览器为了适配「在缩放比1.0,左上角完全贴合时,layout viewport 能与 visual viewport 完全重合」而做的「让展现符合布局预期」的工作。而软键盘弹出场景,调整策略却依不同浏览器 / WebView 有不同行为。

注意: iOS WebView 中只有 layout viewport 尺寸变化才触发 window resize 事件。

检测软键盘弹出和收起

Web 并无标准 API 检测软键盘是否弹出。除非原生 WebView 增加 bridge API。

目前可靠且兼容性较好的方式是 iOS 中监听 visualViewport resize 事件,而(不支持 visualViewport API 的)Android 中监听 window 的 resize 事件(必需指定相应的软键盘显示模式,否则 viewport 尺寸不会变化)。

注意: 在苹果手机上,当软键盘收起时输入框会失去焦点。因此,也可以在苹果手机上可以通过输入框失去和得到焦点事件来判断。

注意: Android 软键盘快速工具栏右侧的「收起」按钮,点击后会收起软键盘,但 input 标签却并不失焦,此时 blur 事件未触发(不能以此为据获知软键盘收起)。

const ua = window.navigator.userAgent;
const isIOS = /iPhone|iPod|iPad/i.test(ua)
const isAndroid = /Android/i.test(ua)

// iOS端:需要排查 input、textarea 之外的标签(如:radio、checkbox)触发 focusin、focusout
window.addEventListener('focusin', () => {
	console.log('键盘弹出')
});
window.addEventListener('focusout', () => {
	console.log('键盘收起')
})

// Android端:横屏功能也会触发 resize(可通过宽度是否改变判断)
const innerHeight = window.innerHeight
window.addEventListener('resize', () => {
  const newInnerHeight = window.innerHeight;
  if (innerHeight > newInnerHeight) {
		console.log('键盘弹出');     
  } else {
		console.log('键盘收起');
  }
})

相关问题

视口单位(Viewport units)

视口: PC 端指的是浏览器的可视区域;移动端指的就是 Viewport 中的 Layout Viewport。

根据 CSS3 规范,视口单位主要包括以下4个:

  • vw:1vw 等于视口宽度的1%。

  • vh:1vh 等于视口高度的1%。

  • vmin:选取 vw 和 vh 中最小的那个。

  • vmax:选取 vw 和 vh 中最大的那个。

vw 和 vh 是相对于视口的高度和宽度,而不是父元素的(CSS 百分比是相对于包含它的最近的父元素的高度和宽度)。1vh 等于1/100的视口高度,1vw 等于1/100的视口宽度。

vmax 相对于视口的宽度或高度中较大的那个,其中最大的那个被均分为 100 单位的 vmax。

vmin 相对于视口的宽度或高度中较小的那个,其中最小的那个被均分为 100 单位的 vmin。

参考资料

jianshu - 关于Android中的软键盘
viewport 与软键盘对布局的影响