兼容移动端ios,安卓,web端底部软键盘弹出,输入框被遮挡问题

思路:

1.监听键盘高度的变化

要监听键盘高度的变化,我们得先看看在键盘展开或收起的时候,分别会触发哪些浏览器事件:

  • iOS 和部分 Android 浏览器 展开:键盘展示时会依次触发 visualViewport resize -> focusin

    -> visualViewport scroll,部分情况下手动调用 input.focus 不触发 focusin 收起:键盘收起时会依次触发 visualViewport resize -> focusout -> visualViewport scroll

    -其他 Android 浏览器 展开:键盘展示的时候会触发一段连续的 window resize,约过 200 毫秒稳定

    收起:键盘收起的时候会触发一段连续的 window resize,约过 200 毫秒稳定,但是部分手机上有些异常的 case:键盘收起时viewport 会先变小,然后变大,最后再变小

html 复制代码
if (window.visualViewport) {
  window.visualViewport?.addEventListener("resize", listener);
  window.visualViewport?.addEventListener("scroll", listener);
} else {
  window.addEventListener("resize", listener);
}
​
window.addEventListener("focusin", listener);
window.addEventListener("focusout", listener);

2.获取「键盘顶部距离视口顶部的高度」

键盘顶部距离视口顶部的高度 = 视口当前的高度 + 视口滚动上去高度

3.设置 input 的位置

键盘距离页面顶部高度」再减去「元素高度」,从而获得「当前元素的位移」

代码

js 复制代码
import EventEmitter from "eventemitter3";

// 默认屏幕高度
const DEFAULT_HEIGHT = window.innerHeight;
const MIN_KEYBOARD_HEIGHT = 200;

export const KeyboardEvent = {
  Show: "keyboardShow",
  Hide: "keyboardHide",
  PositionChange: "keyboardPositionChange",
};

class KeyboardObserver extends EventEmitter {
  constructor() {
    super();
    this.inited = false;
    this.lastWinHeight = DEFAULT_HEIGHT;
    this.canChangeStatus = true;
    this._unbind = () => { };
  }

  // 键盘初始化
  init() {
    if (this.inited) {
      return;
    }
    const listener = () => this.adjustPos();

    if (window.visualViewport) {
      window.visualViewport.addEventListener("resize", listener);
      window.visualViewport.addEventListener("scroll", listener);
    } else {
      window.addEventListener("resize", listener);
    }

    window.addEventListener("focusin", listener);
    window.addEventListener("focusout", listener);

    this._unbind = () => {
      if (window.visualViewport) {
        window.visualViewport.removeEventListener("resize", listener);
        window.visualViewport.removeEventListener("scroll", listener);
      } else {
        window.removeEventListener("resize", listener);
      }

      window.removeEventListener("focusin", listener);
      window.removeEventListener("focusout", listener);
    };

    this.inited = true;
  }

  unbind() {
    this.inited = false;
    this._unbind();
  }

  // 调整元素位置
  adjustPos() {
    // 获取当前视口高度
    const height = window.visualViewport
      ? window.visualViewport.height
      : window.innerHeight;

    // 获取键盘高度
    const keyboardHeight = DEFAULT_HEIGHT - height;

    // 获取键盘顶部距离视口顶部的距离
    const top = height + (window.visualViewport?.pageTop || 0);

    this.emit(KeyboardEvent.PositionChange, { top });

    const diffHeight = height - this.lastWinHeight;

    this.lastWinHeight = height;

    // 如果高度减少,且减少高度大于 200,则视为键盘弹起
    if (diffHeight < 0 && keyboardHeight > MIN_KEYBOARD_HEIGHT) {
      this.onKeyboardShow({ height: keyboardHeight, top });
    } else if (diffHeight > 0) {
      this.onKeyboardHide({ height: keyboardHeight, top });
    }
  }

  onKeyboardShow(keyboardInfo) {
    if (this.canChangeStatus) {
      this.emit(KeyboardEvent.Show, keyboardInfo);
      this.setCanChangeStatus();
    }
  }

  onKeyboardHide(keyboardInfo) {
    if (this.canChangeStatus) {
      this.emit(KeyboardEvent.Hide, keyboardInfo);
      this.setCanChangeStatus();
    }
  }

  setCanChangeStatus() {
    this.canChangeStatus = false;
    const timer = setTimeout(() => {
      clearTimeout(timer);
      this.canChangeStatus = true;
    }, 200);
  }
}

const keyboardObserver = new KeyboardObserver();

export default keyboardObserver;

组件

vue 复制代码
<template>
  <div>
    <button @click="focusInput">点击我</button>
    <!-- <div>{{ statusText }}</div> -->
    <input
      ref="elRef"
      class="block"
      :style="{ transform: `translateY(${top}px)` }"
    />
  </div>
</template>

<script>
import keyboardObserver, { KeyboardEvent } from "./keyboard";

export default {
  name: 'App',
  data() {
    return {
      // statusText: '',
      top: window.innerHeight,
    };
  },
  created() {
    this.initKeyboardObserver();
  },
  beforeDestroy() {
    keyboardObserver.unbind();
  },
  methods: {
    initKeyboardObserver() {
      keyboardObserver.init();

      keyboardObserver.on(KeyboardEvent.PositionChange, this.onPositionChange);
      // keyboardObserver.on(KeyboardEvent.Show, this.onKeyboardShow);
      // keyboardObserver.on(KeyboardEvent.Hide, this.onKeyboardHide);
    },
    onPositionChange({ top }) {
      this.top = top - this.$refs.elRef.clientHeight;
    },
    // onKeyboardShow() {
    //   this.statusText = 'show';
    // },
    // onKeyboardHide() {
    //   this.statusText = 'hide';
    // },
    focusInput() {
      this.$refs.elRef.focus();
    },
  },
};
</script>

<style>
html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
}

#app {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
}

.block {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: 0.2s all;
  background-color: #000;
  color: #fff;
}

</style>

使用

页面初始化时输入框获取焦点,并平移键盘弹起的平移高度

初始化监听initKeyboardObserver

参考:

https://juejin.cn/post/7338335869709385780

相关推荐
健了个平_243 分钟前
iOS 26 适配笔记
ios·swift·wwdc
dualven_in_csdn1 小时前
搞了两天的win7批处理脚本问题
java·linux·前端
你的人类朋友2 小时前
✍️【Node.js程序员】的数据库【索引优化】指南
前端·javascript·后端
小镇学者3 小时前
【PHP】导入excel 报错Trying to access array offset on value of type int
android·php·excel
小超爱编程3 小时前
纯前端做图片压缩
开发语言·前端·javascript
应巅3 小时前
echarts 数据大屏(无UI设计 极简洁版)
前端·ui·echarts
Jimmy4 小时前
CSS 实现描边文字效果
前端·css·html
islandzzzz4 小时前
HMTL+CSS+JS-新手小白循序渐进案例入门
前端·javascript·css·html
Senar4 小时前
网页中如何判断用户是否处于闲置状态
前端·javascript
很甜的西瓜4 小时前
typescript软渲染实现类似canvas的2d矢量图形引擎
前端·javascript·typescript·图形渲染·canvas