最近在工作中遇到了这么一个需求:
点击页面按钮,显示弹窗(弹窗内可展开多个列表,列表中有多条输入框,展开时整个弹窗内容超一屏),当弹窗内的内容滚动时,背景不随之滚动(一般滚动到底部或顶部后,内部滚动条会触发背景主页面滚动,如果背景页面超一屏)。当关闭弹窗时,主页面又可以恢复滚动。
简而言之,就是弹窗背景锁定问题
解决的思路很简单,当弹窗显示时,获取页面scrollTop高度,给背景主页面设置样式position:fixed;top:-${scrollTop}px;让主页锁定,不能滚动。关闭弹窗时,删除样式,令页面的scrollTop为之前获取的高度。这样在关闭弹窗后,页面依旧是点开弹窗时的位置。
问题
1、pc端和移动端对于获取页面scroll高度以及设置页面高度,需要做兼容性写法。
这倒不难,稍微查一下就可以解决,网上有很多兼容性写法
2、一个棘手的ios端出现的异常bug:当连续展开两个列表(含输入框),背景主页面会被挤到可视区域上面,顶部会被切掉,再关闭弹窗,页面滚不下来。
这个问题是我写的代码里多方面综合因素导致的,后面可总结为ios端弹窗内输入框失焦导致其父元素可视区域改变。
解决
为了纪录这个问题和解决办法,下面用一个demo重现了当时的问题。包括背景锁定的实现和兼容性问题解决,都可在demo中查看
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>弹窗弹出背景锁定调试</title>
</head>
<body>
<div class="text-all">
<div class="popwindow" id="pop">
<div class="pop-coin">
<div class="close-pop" id="closePop">点此关闭弹窗</div>
<div class="pop-conent">
<ul class="pop-ul">
<li class="box1">
<div id="min-box1">点此展开收货地址1</div>
<div class="showbox1" id="address1">1
<input type="text" name="name" placeholder="请输入联系人" autofocus>
<input type="tel" name="phone" placeholder="请输入手机号码'">
<textarea id="address" placeholder="请输入收货地址" maxlength="50"
rows="2" style="resize:none;"></textarea>
</div>
</li>
<li class="box2">
<div id="min-box2">点此展开收货地址2</div>
<div class="showbox2" id="address2">2
<input type="text" name="name" placeholder="请输入联系人" autofocus>
<input type="tel" name="phone" placeholder="请输入手机号码'">
<textarea id="address" placeholder="请输入收货地址" maxlength="50"
rows="2" style="resize:none;"></textarea>
</div>
</li>
<li class="box3">
<div id="min-box3">点此展开收货地址3</div>
<div class="showbox3" id="address3">3
<input type="text" name="name" placeholder="请输入联系人" autofocus>
<input type="tel" name="phone" placeholder="请输入手机号码'">
<textarea id="address" placeholder="请输入收货地址" maxlength="50"
rows="2" style="resize:none;"></textarea>
</div>
</li>
</ul>
</div>
<div>假定的弹窗内容</div>
</div>
</div>
<div class="text-pop" id="show">显示我的中奖记录</div>
<div class="content">demo测试:背景里的内容</div>
</div>
<div style="height: 0;"></div>
<div class="iphonex-block" style="opacity: 0;"></div>
</body>
<style>
body,html {
width: 100%;
height: 100%;
display: block;
margin: 0;
padding: 0;
}
.g-lock {
color:indigo;
}
.text-all {
display: block;
position: relative;
overflow: hidden;
width: 375px;
height: 400px;
background-color: rgb(209, 152, 152);
}
.popwindow {
display: none;
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.6969);
}
.pop-coin {
/* position: relative; */
margin: 50px auto;
border: 5px solid lawngreen;
}
.pop-conent {
overflow: auto;
min-height: 300px;
max-height: 350px;
}
ul {
margin: 0;
padding: 0;
min-height: 300px;
max-height: 350px;
list-style: none;
}
li {
list-style: none;
min-height: 200px;
background-color: yellowgreen;
min-height: 50px;
}
.showbox1,.showbox2,.showbox3 {
display: none;
min-height: 200px;
background-color: peru;
}
input,textarea {
display: block;
height: 80px;
}
.close-pop {
background-color: rgb(214, 241, 153);
}
.text-pop {
width: 100%;
height: 200px;
background-color: rgb(115, 116, 199);
border: 2px solid purple;
}
.content {
width: 100%;
/* 问题在下面这里,因为背景的内容本来就超过高度了,原本被遮挡,然后点击弹窗之后,获取焦点,然后背景就会被顶上去。要是这个高度没有超过外面盒子的高度,则不会被顶上去 */
/* 解决办法1,去掉父盒子的overflow:hidden即可 */
/* 解决办法2,去掉输入框的那个autofocus */
/* 解决办法3,把弹窗组件的位置放到body下,而不是主页面下 */
height: 210px;
background-color: rgb(114, 211, 138);
vertical-align: middle;
text-align: center;
}
</style>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var closepop=document.getElementById("closePop");
var showpop=document.getElementById("show");
var popwindow=document.getElementById("pop");
var box1=document.getElementById("min-box1"); // 打开收货地址
var box2=document.getElementById("min-box2");
var box3=document.getElementById("min-box3");
var address1=document.getElementById("address1"); // 收货地址盒子
var address2=document.getElementById("address2");
var address3=document.getElementById("address3");
var scrollTop=0;
showpop.onclick = function () {
popwindow.style.display = "block";
let top= document.documentElement.scrollTop || window.pageYOffset ||document.body.scrollTop
scrollTop = top
document.querySelector('body').setAttribute('style', `position:fixed;top:-${scrollTop}px`)
// alert()
address1.style.display = "none";
address2.style.display = "none";
address3.style.display = "none";
box1.style.display = "block";
box2.style.display = "block";
box3.style.display = "block";
}
closepop.onclick = function () {
popwindow.style.display = "none";
document.querySelector('body').removeAttribute('style')
document.documentElement.scrollTop = scrollTop; // 不加这句,safari浏览器中关闭弹窗会置顶,即document.body.scrollTop不起效
// window.pageYOffset = scrollTop; // 加上这句,PC端和苹果端都会出现问题
document.body.scrollTop = scrollTop // 不加这句,苹果手机关闭弹窗会置顶,即 document.documentElement.scrollTop在苹果移动端不起效
document.body.classList.add('g-lock')
}
box1.onclick = function () {
box1.style.display = "none";
address1.style.display = "block";
}
box2.onclick = function () {
box2.style.display = "none";
address2.style.display = "block";
}
box3.onclick = function () {
box3.style.display = "none";
address3.style.display = "block";
}
</script>
</html>