问题概述
今天遇到了一个问题, 在安卓手机上, 当我要点击输入 "店铺名称" 时, 手机软键盘弹出来刚好把输入框挡住了; 挡住就算了, 关键是页面还不能向上滑动, 整个手机窗口被压为原来的二分之一左右;
然后
然后找了一些方案, 不过不大适用, 或者是有点麻烦; 所以需要整合一下, 首先, 我想一下我要实现的效果
想要实现的效果
如图, 当手机键盘出现时, 页面是可以自由滚动的, 而且当前聚焦的输入框往红线处靠齐, 这样就刚好在剩下的窗口的垂直正中间, 这样就不会出现输入框被挡住, 看不到自己输入的内容了 ;
第一步, 使屏幕压小时, 页面内容可以滚动查看
如下图所示, 黑色框代表屏幕, 蓝色框代表页面大小, 当屏幕被压小时, 页面内容必须保持原来的高度:
实现原理, 页面一进来时, 我就获取窗口的高度, 给最外层的 div 设置一个最小高度, 这样就算窗口压小了, 页面还能维持原来的高度, 可以滚动浏览:
- let initWindowHeight=window.innerHeight
- let wrapDiv=document.getElementsByClassName('animated-router-forward-enter-done')[0]
- wrapDiv.style.minHeight =initWindowHeight+'px'
复制代码
第二步, 滚到红线处
由于我们不能直接知道软键盘什么时候出来, 不过软键盘出来的时候窗口高度会缩小, 所以我们可以通过监听窗口大小变化事件来判断软键盘是否弹出, 比如浏览器窗口高度突然缩小 25% 以上, 那么我们就认为是软键盘出来了, 然后我们获取聚焦 input 距离页面顶部的距离, 计算距离红线有多少距离, 假设距离是 60, 那么我们就让页面向上滚动 60, 这时 input 就刚刚好到了红线处;
- window.onresize=function(){
- if(initWindowHeight-window.innerHeight>initWindowHeight/4&&document.querySelectorAll(':focus').length>0){
- if(offset(document.querySelectorAll(':focus')[0]).top>initWindowHeight/4){
- document.body.scrollTop=offset(document.querySelectorAll(':focus')[0]).top-initWindowHeight/4
- }
- }else if(window.innerHeight-initWindowHeight<20){
- document.body.scrollTop=0
- }
- };
复制代码
完整代码
因为可能有多个页面要调用, 所以我把代码放到一个单独的 js 文件中:
- function pageInputScroll() {
- let initWindowHeight=window.innerHeight
- setTimeout(() => {
- let wrapDiv=document.getElementsByClassName('animated-router-forward-enter-done')[0]
- //console.log(wrapDiv.style)
- wrapDiv.style.minHeight =initWindowHeight+'px'
- }, 500);
- // 由于我们不能直接知道软键盘什么时候出来, 不过软键盘出来的时候窗口高度会缩小, 所以我们可以通过监听窗口大小变化事件来判断软键盘是否弹出
- window.onresize=function(){ // 如果浏览器窗口高度缩小 25% 以上, 就认为是软键盘出来了
- if(initWindowHeight-window.innerHeight>initWindowHeight/4&&document.querySelectorAll(':focus').length>0){
- if(offset(document.querySelectorAll(':focus')[0]).top>initWindowHeight/4){
- document.body.scrollTop=offset(document.querySelectorAll(':focus')[0]).top-initWindowHeight/4
- }
- }else if(window.innerHeight-initWindowHeight<20){
- document.body.scrollTop=0
- }
- };
- }
- function offset(element) {
- var offest = {
- top: 0,
- left: 0
- };
- var _position;
- getOffset(element, true);
- return offest;
- // 递归获取 offset, 可以考虑使用 getBoundingClientRect
- function getOffset(node, init) {
- // 非 Element 终止递归
- if (node.nodeType !== 1) {
- return;
- }
- _position = window.getComputedStyle(node)['position'];
- // position=static: 继续递归父节点
- if (typeof(init) === 'undefined' && _position === 'static') {
- getOffset(node.parentNode);
- return;
- }
- offest.top = node.offsetTop + offest.top - node.scrollTop;
- offest.left = node.offsetLeft + offest.left - node.scrollLeft;
- // position = fixed: 获取值后退出递归
- if (_position === 'fixed') {
- return;
- }
- getOffset(node.parentNode);
- }
- }
- export {pageInputScroll};
复制代码
在 react 页面中引入 js 并调用:
- import {pageInputScroll} from '../../util/pageInputScroll'
- ......
- componentDidMount(){
- pageInputScroll()
- }
复制代码
备注
offset() 方法是使用 js 实现类似 jquery 的 offset() 的一个方法, 参考自: 原生 js 实现 offset 方法 https://www.jianshu.com/p/8342388445bd
推荐: 一个可以扫描商品条码进行商品评论的小程序 https://minapp.com/miniapp/8371
来源: https://juejin.im/post/5b874a14f265da437174c0e0