问题描述
在ISO15.x真机版本系统上,页面普通input框上面盖个蒙层 直接可以点击穿透让 nput框聚焦,导致手机上的软键盘弹出影响体验和布局,而且极其影响用户使用和体验,给产品带来比较坏的影响
效果展示:
原因分析
微信小程序中部分组件是由客户端创建的原生组件,input,textarea都是原生组件,而事件在微信中原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上,并且 input 组件 focus 状态目前是不支持同层渲染多。所以在真机上会出现在原生组件input和textarea中,获取焦点的层级高于遮罩元素,所以表现出在遮罩元素中点击误触input聚焦事件。
解决方案
为了解决原生组件层级最高的限制。小程序专门提供了 cover-view 和 cover-image 组件,可以覆盖在部分原生组件上面。但是实际操作下来,这种方案只能解决开发工具上的问题,真机上还是存在穿透现象
在出现弹窗的时候,把input设置为不可用,或者用text标签替换input,这样在出现遮罩的时候就不会有input元素,这样也能避免穿透问题,但是在页面元素多或者input/遮罩数量未知的时候,这种解决方案存在一定的问题,每次有新遮罩都要去坚挺遮罩的出现/消失状态,同时也会导致在很多地方写出怪逻辑,提高项目的维护成本。所以为了尽量降低维护成本,选择在input中做兼容。解决方案如下:
在input/input组件中多添加两个状态 readonly是否可读/focus是否获取焦点和两个事件bindtap组组件受到点击事件/bindblur输入框失去焦点
readonly用来控制当前的input是否可选(或者是否用text元素替换,按实际情况使用),默认不可选,在组件受到点击时修改状态
focus用来控制组件获取焦点的状态,默认为false,这是为了优化input组件由text/不可选状态变为正常的组件聚焦状态,让用户无感知input变化
bindtap在组件受到点击时触发,同时会模拟用户正常的获取输入框焦点事件,把readonly和focus设置为true
bindblur在失去焦点时候触发,是为了让input恢复到默认的状态,防止一次使用之后再出现穿透问题
这样,利用bindtap事件不会穿透来解决了原生input穿透问题,项目中的实操(代码片段):wxml
<titian-stepper
key="{{value}}“
disable-input=”{{stepperReadonly}}“
border=”{{false}}“
focus=”{{stepperFocus}}"
bind:tapInput="handleTapInput"
input-class=“shop-cart-g-total__stepper-input"
max=”{{limitBuyNum}}“
value=”{{num}}"
bind:blur="onBlur"
integer
bind:plus="onChange"
bind:minus=“onChange”
></titian-stepper>
js
data: {
stepperReadonly: true,
stepperFocus: false,
},
handleTapInput() {
this.setData({ stepperReadonly: false }, () => {
this.setData({ stepperFocus: true });
});
},
handleBlur() {
this.setData({ stepperReadonly: true }, () => {
this.setData({ stepperFocus: false });
});
},
当然,本文中提到的也只是一种比较讨巧的方式,真正要解决还是期待微信官方去解决