emm。。。我真的很想问一下,天底下tm哪来的那么多弹窗啊。操作功了来个成功的弹窗,失败了了来个弹窗,点个人头像来个弹窗,随便点个按钮都有个弹窗,一个页面里面弹窗的代码比堆页面的代码多一万倍,不好直接展示在页面的东西第一反映一定是用弹窗,也不动脑子想想是不是能把一些不方便直接展示的统一放到一个页面去或者有没其他的地方,你不烦我tm都烦...三步一弹窗你跟我说用户体验,搞笑呢?(我喷完了...)
如果仅仅只是弹窗还好,怕的是那些在弹窗里面带了输入框元素;当然在安卓里面不存在这些问题,但是在ios里面就发生了蛋疼菊紧的光标错位;
这真的是个让人脑阔痛的问题,fixed 定位里面的弹窗 有input,在多次次点击之后就会光标错位,(一次2次还不会出现这个问题) 造成这一问题的原因就是 ios 的虚拟键盘顶起视图,这时候input跟着视图一起被顶起,在键盘收起之后,视图看起来还原了,但是光标还是有一定几率停留在被顶起的那个地方;
更深的原因就是ios 对于fixed 的不友好支持;
从ios8开始就有开发者反馈这问题,到ios13还存在这个问题,我在-webkit的反馈记录里面看到说在ios12里面已经修复了该问题,但是要是真的解决了我还会这么蛋疼?反正截至到9102年9月30日,ios13更新,这个问题依然存在;
在网上搜索很久,大致有两种解法,第一种就是说在弹窗出来之后,把body 固定住,弹窗消失后在取消body 的fixed 定位;
.bodyFixed{
position:fixed
width:100%;
height:100%;
left:0;
top:0;
}
var ModalHelper = (function (bodyCls) {
var scrollTop;
return {
afterOpen: function () {
scrollTop = document.scrollingElement.scrollTop; //记录这东西是为了不让弹窗在显示/隐藏的时候回到0的位置
document.body.classList.add(bodyCls);
document.body.style.top = -scrollTop + 'px';
},
beforeClose: function () {
document.body.classList.remove(bodyCls);
document.scrollingElement.scrollTop = scrollTop;
}
};
})('bodyFixed');
$(".open").click(function () {
$(".pop").show();
ModalHelper.afterOpen();
})
$(".close").click(function () {
$(".pop").hide();
ModalHelper.beforeClose();
})
上面其实主要是解决弹窗之后背景还可以滑动的问题,也就是滚动穿透问题,但是却也顺带实现了上面说解决光标错位的实录,然而实际却依然没办法解决光标错位问题...
警告,这个东西只能用来解决滚动穿透问题,如果在弹窗里面有input绝对不能用这玩意儿在防止滚动穿透,遇到这种情况你可以尝试使用better-scroll.js,我跟你讲,简单又超实用
我不知道为什么网上那么多人说可以使用这种方式来处理光标错位问题,可以是以前的确可以吧,但是反正我是没成功过,因为造成光标错位的根本原因就是ios对fixed的不友好支持,所以只要有fixed 那就肯定不行
第二种办法就是不要使用fixed 定位,既然fixed 不被支持,那么不使用就好了...
我试过,确实不使用fixed 就不会出现光标错位的问题,但是随之而来一大堆问题;
1,弹窗可以滚动
本来使用fixed 最最要的目的就是占一屏,不要出现滚动,取消了fixed 弹窗自然就是跟着body滚动了,解决这个问题也很简单,你可以在 背景元素上 使用 touchmove event.prentDefault 去阻止默认滑动;
2,解决了滑动问题之后,第二个问题来了,定位
你不使用fixed 定位,那么在mask 上肯定要使用absolute 或者 relative,但是这时候定位的top 不好取,不过你可以获取滚动距离,$(document).scrollTop();
mask的top就是页面滚过的距离,如果这一点搞定之后就可以模拟出fixed 的情况;
3,这是最难受的一点,在这种相对或者绝对定位情况下,虚拟键盘弹起是会顶起弹框,但是收起之后tmd 不会给你还原,这时候你在弹起键盘,弹窗会被一直往上面顶...
后面我找到一个方法 scrollIntoView(bool|obj),这是用在原生dom 上的,所以如果是jq对象要转成js对象; 这个api的作用就是让dom对象滚动到浏览器窗口的可视区域内。
方法可接受 一个布尔参数 或者一个对象参数 ;如果为true,元素的顶端将和其所在滚动区的可视区域的顶端对齐,为false 则与低部对齐,详情可以问度娘
知道这个方法之后,你可以在input focusin 或者focusout 的时候去使用这个方法,让那个弹窗的内容容器调用scrollIntoView(),让他保持在可视区的顶部,这样就防止弹窗被不停的被顶上去
但是在使用了上面的方法之后,又会带来2个新的问题
1,位置还原问题
在使用了scrollIntoView 之后,弹窗是贴顶了,但是当你收起键盘的时候,你的弹窗也回不来了...womt一口老血;这时候只有监听收起键盘事件了,很遗憾,并没有这种回调函数...你可以在网上找到一些相关方法,但是我试过一些,没乱用...
2,抖动
ios 在切换input 的时候会自动的 调整聚焦的input的距离,他会让input元素一直处于可视范围内,这样可以和用户获得更良好的输入体验,但是这就和scrollIntoView 冲突了,scrollIntoView 是让元素贴顶或贴低,但是ios 会动态调整input (或者说弹窗)的 top,让后2个就干起来了,你想贴顶?劳资偏不让,我偏要他处于可视范围...然后就造成鬼畜的抖动;
那么说到底到底有没什么办法可以完美解决呢...很遗憾,我没找到;在这里找到一个还算不错的办法,至少我在测试文件中 里面来来回回戳了好多次,中途也收起过键盘,又弹出键盘,中途其实也出现了小距离的错位情况(从点击input时候的蓝色背景看出来的),但是还是触发了键盘,去掉点击时候产生的蓝色背景几乎也感受不到错位情况
贴一个例子
.login-modal{
z-index: 750;
position: fixed;
height: 100vh;
width: 100vw;
padding: 0;
margin: 0;
top: 0;
left: 0;
display: none;
&.act{
display: table;
}
.mask{
display: table-cell;
vertical-align: middle;
text-align: center;
background-color:rgba(0, 0, 0, 0.5);
.content.container{
display: inline-block;
margin: auto;
border-radius: .04rem;
width: 90%;
background-color:white;
padding:.25rem;
input{
width: 100%;
height: .75rem;
margin-bottom: .35rem;
display:block;
background-color:#ccc;
border:1px solid red;
}
}
}
}
原理很简单,就是利用了table 的自动居中,也没有用到多余的js,算是一个不错的方法;
想要完美解决只能看肾家的心情了;