toFrankie / blog

种一棵树,最好的时间是十年前。其次,是现在。
20 stars 1 forks source link

解决安卓收起键盘无法触发失焦事件的问题 #238

Open toFrankie opened 1 year ago

toFrankie commented 1 year ago

背景

最近在做一个移动端 Web 项目,在首页底部是有一个类似于 APP 导航栏(以下称 FootNav),采用的 fixed 布局固定于底部。同时页面有一些 <input> 输入框(以下称 Input)。

当聚焦于 Input 时,在 iOS 预期效果是没问题,但是在杀千刀的 Android 上,页面高度发生变化,导致 FootNav 固定在手机键盘上面,同时 FootNav 也直接挡住了输入框,交互体验非常的糟糕。

烦死了...

iOS 与 Android 键盘弹出收起的表现

先了解下背景,键盘的弹出收起,在 iOS 端与 Android 端的 WebView 中表现并非一致的。

键盘弹出

键盘收起

由于我并没有过多的深入了解两者的差异表现,以上内容来自此处

寻找解决方案

针对 Android 设备做处理就行了,iOS 无需处理。

方案一

处理方式:Input 聚焦隐藏 FootNav,失焦时再将其显示出来。(同理,修改布局方式也是一样的)

首先这种处理思路是没毛病的,但是...

某些机型、某些输入法,收起键盘并不会触发输入框的 blur 失焦事件,导致该方案直接流产。

方案二

监听页面高度的变化,利用这一点我们就可以处理 FootNav 的隐藏/显示了。

实现

思路很简单:首先进入页面时,先记录窗口的原始高度。每当 Input 聚焦时,设置 window.onresize 函数,当窗口宽高发生改变时便会触发。

以 React 为例:

class Home extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      showFootNav: true,
      initClientHeight: document.documentElement.clientHeight // 记录初始高度
    }
  }

  // 判断 Android 设备
  isAndroid() {
    const ua = navigator.userAgent.toLowerCase()
    return ua.includes('android') || ua.includes('linux')
  }

  // 监听窗口变化
  listenWindowResize() {
    let that = this

    if (this.isAndroid()) {
      window.onresize = () => {
        const { initClientHeight } = that.state
        const curClientHeight = document.documentElement.clientHeight // 当前页面高度

        if (curClientHeight < initClientHeight) {
          // 键盘弹出
          that.setState({ showFootNav: false })
        } else {
          // 键盘收起
          that.setState({ showFootNav: true }, () => {
            window.onresize = null // 清空 onresize
          })
        }
      }
    }
  }

  render() {
    const { showFootNav } = this.state

    // 以下 Input、FootNav 为自定义封装的组件
    return (
      <div>
        <Input onFocus={this.listenWindowResize.bind(this)} />
        {showFootNav ? <FootNav /> : null}
      </div>
    )
  }
}

本文之外的一些兼容性问题,iOS 也有一些 bug 的,比如微信浏览器里收起键盘之后,页面不回弹,可参考文章

References