学习链接
js如何禁止滚动条滚动,但不消失! - 这个是完美解决方案(在线demo示例)
解决窗口滚动条消失而导致的页面内容抖动的问题
完美解决js 禁止滚动条滚动,并且滚动条不消失,页面大小不闪动
蒙层禁止页面滚动的方案
蒙层禁止页面滚动-自己的上篇
最终的效果
前言
在页面中,经常会需要打开一些弹窗,弹窗下面会有一个蒙层,蒙层下面是页面。蒙层需要覆盖整个视口。但是一般会遇到3个问题不那么好解决
- 打开弹窗后,当鼠标滚动时,底下的页面也会跟随滚动。
- 浏览器右侧的滚动条在一些方案中会隐藏,而导致打开弹窗和关闭弹窗时,由于滚动条的显示和隐藏而导致页面抖动闪烁。
- 浏览器右侧的滚动条在一些方案中,打开弹窗时候,可能会丢失当前的滚动位置,而直接回到了顶部。
如果有上面这3个问题,直接找上面提到的完美方案即可解决
蒙层禁止页面滚动的方案
直接拷贝的蒙层禁止页面滚动的方案 - 来自WindrunnerMax,以作记录。
弹窗是一种常见的交互方式,而蒙层是弹窗必不可少的元素,用于隔断页面与弹窗区块,暂时阻断页面的交互。但是在蒙层出现的时候滚动页面,如果不加处理,蒙层底部的页面会开始滚动,实际上我们是不希望他进行滚动的,因此需要阻止这种行为。当弹出蒙层时禁止蒙层下的页面滚动,也可以称为滚动穿透的问题,文中介绍了一些常用的解决方案。
实现
首先需要实现一个蒙层下滚动的效果示例,当我们点击弹窗按钮显示蒙层之后,再滚动鼠标的话能够看到蒙层下的页面依旧是能够滚动的。如果在蒙层的内部进行滚动,当蒙层内滚动条滚动到底部的时候再继续滚动的话,蒙层下的页面也是能够滚动的,这样的交互就比较混乱,文中内容的测试环境是Chrome 96.0.4664.110。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>蒙层禁止页面滚动的方案</title>
<style type="text/css">
#mask{
position: fixed;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.6);
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hide{
display: none !important;
}
.long-content > div{
height: 300px;
}
.mask-content{
width: 300px;
height: 100px;
overflow-x: auto;
background: #fff;
}
.mask-content > div{
height: 300px;
}
</style>
</head>
<body>
<button id="btn">弹窗</button>
<div class="long-content">
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
</div>
<div id="mask" class="hide">
<div class="mask-content">
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
</div>
</div>
</body>
<script type="text/javascript">
(() => {
const btn = document.getElementById("btn");
const mask = document.getElementById("mask");
btn.addEventListener("click", e => {
mask.classList.remove("hide");
})
mask.addEventListener("click", e => {
mask.classList.add("hide");
})
})();
</script>
</html>
body hidden
此方案是一种比较常用的方案,即打开蒙层时给body添加overflow: hidden;,在关闭蒙层时就移除这个样式,例如思否的登录弹窗、antd的Modal对话框就是这样的方式。 这种方案的优点是简单方便,只需添加css样式,没有复杂的逻辑。缺点是在移动端的适配性差一些,部分安卓机型以及safari中,无法阻止底部页面滚动,另外有些机型可能需要给根节点添加overflow: hidden;样式才有效果,此外由于实际上是将页面的内容给裁剪了,所以在设置这个样式的时候滚动条会消失,而移除样式的时候滚动条又会出现,所以在视觉上是会有一定的闪烁现象的,当然也可以定制滚动条的样式,但滚动条样式就是另一个兼容性的问题了,还有同样是因为裁剪。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>蒙层禁止页面滚动的方案</title>
<style type="text/css">
#mask{
position: fixed;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.6);
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hide{
display: none !important;
}
.long-content > div{
height: 300px;
}
.body-overflow-hidden{
overflow: hidden;
}
.mask-content{
width: 300px;
height: 100px;
overflow-x: auto;
background: #fff;
}
.mask-content > div{
height: 300px;
}
</style>
</head>
<body>
<button id="btn">弹窗</button>
<div class="long-content">
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
</div>
<div id="mask" class="hide">
<div class="mask-content">
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
</div>
</div>
</body>
<script type="text/javascript">
(() => {
const btn = document.getElementById("btn");
const mask = document.getElementById("mask");
const body = document.body;
btn.addEventListener("click", e => {
mask.classList.remove("hide");
body.classList.add("body-overflow-hidden");
})
mask.addEventListener("click", e => {
mask.classList.add("hide");
body.classList.remove("body-overflow-hidden");
})
})();
</script>
</html>
touch preventDefault
上边的方案对于移动端的效果不是很理想,如果需要在移动端进行处理的话,可以利用移动端的touch事件,来阻止默认行为,当然这是适用于移动端的方式,另外要是把手机通过蓝牙也好转接线也好接上鼠标的话,那就是另一回事了。假如蒙层内容不会有滚动条,那么上述方法是没有问题的,但是假如蒙层内容有滚动条的话,那么它再也无法动弹了。所以如果在蒙层内部有元素需要滚动的话,需要用Js控制其逻辑,但是逻辑控制起来又是比较复杂的,我们可以判断事件的event.target元素,如果touch的目标是弹窗不可滚动区域即背景蒙层就禁掉默认事件,反之就不做控制,之后又出现了问题,需要判断滚动到顶部和滚动到底部的时候禁止滚动,否则触碰到上下两端,弹窗可滚动区域的滚动条到了顶部或者底部,依旧穿透到body,使得body跟随弹窗滚动,这样的话逻辑的复杂程度就比较高了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>蒙层禁止页面滚动的方案</title>
<style type="text/css">
#mask{
position: fixed;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.6);
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hide{
display: none !important;
}
.long-content > div{
height: 300px;
}
.mask-content{
width: 300px;
height: 100px;
overflow-x: auto;
background: #fff;
}
.mask-content > div{
height: 300px;
}
</style>
</head>
<body>
<button id="btn">弹窗</button>
<div class="long-content">
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
</div>
<div id="mask" class="hide">
<div class="mask-content">
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
</div>
</div>
</body>
<script type="text/javascript">
(() => {
const btn = document.getElementById("btn");
const mask = document.getElementById("mask");
const body = document.body;
const scrollerContainer = document.querySelector(".mask-content");
let targetY = 0; // 记录下第一次按下时的`clientY`
scrollerContainer.addEventListener("touchstart", e => {
targetY = Math.floor(e.targetTouches[0].clientY);
});
const touchMoveEventHandler = e => {
if(!scrollerContainer.contains(e.target)) {
e.preventDefault();
}
let newTargetY = Math.floor(e.targetTouches[0].clientY); //本次移动时鼠标的位置,用于计算
let scrollTop = scrollerContainer.scrollTop; // 当前滚动的距离
let scrollHeight = scrollerContainer.scrollHeight; // 可滚动区域的高度
let containerHeight = scrollerContainer.clientHeight; //可视区域的高度
if (scrollTop <= 0 && newTargetY - targetY > 0) { // 到顶
console.log("到顶");
if(e.cancelable) e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`
} else if (scrollTop >= scrollHeight - containerHeight && newTargetY - targetY < 0 ) { // 到底
console.log("到底");
if(e.cancelable) e.preventDefault(); // 必须判断`cancelable` 否则容易出现滚动正在进行无法取消的`error`
}
}
btn.addEventListener("click", e => {
mask.classList.remove("hide");
body.addEventListener("touchmove", touchMoveEventHandler, { passive: false });
})
mask.addEventListener("click", e => {
mask.classList.add("hide");
body.removeEventListener("touchmove", touchMoveEventHandler);
})
})();
</script>
</html>
body fixed
目前常用的方案就是该方案了,要阻止页面滚动,可以将其固定在视图中即position: fixed,这样它就无法滚动了,当蒙层关闭时再释放,当然还有一些细节要考虑,将页面固定视图后内容会回头最顶端,这里我们需要记录一下用来同步top值,这样就可以得到一个兼容移动端与PC端的较为完善的方案了,当然对于浏览器的api兼容性是使用document.documentElement.scrollTop控制还是window.pageYOffset + window.scrollTo控制就需要另行适配了。在示例中为了演示弹窗时不会导致视图重置到最顶端,将弹窗按钮移动到了下方。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>蒙层禁止页面滚动的方案</title>
<style type="text/css">
#mask{
position: fixed;
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 0.6);
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hide{
display: none !important;
}
.long-content > div{
height: 300px;
}
.mask-content{
width: 300px;
height: 100px;
overflow-x: auto;
background: #fff;
}
.mask-content > div{
height: 300px;
}
</style>
</head>
<body>
<div class="long-content">
<div>long content</div>
<div>long content</div>
<div>long content</div>
<button id="btn">弹窗</button>
<div>long content</div>
<div>long content</div>
<div>long content</div>
<div>long content</div>
</div>
<div id="mask" class="hide">
<div class="mask-content">
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
<div>mask-content</div>
</div>
</div>
</body>
<script type="text/javascript">
(() => {
const btn = document.getElementById("btn");
const mask = document.getElementById("mask");
const body = document.body;
let documentTop = 0; // 记录按下按钮时的 `top`
btn.addEventListener("click", e => {
mask.classList.remove("hide");
documentTop = document.scrollingElement.scrollTop;
body.style.position = "fixed"
body.style.top = -documentTop + "px";
})
mask.addEventListener("click", e => {
mask.classList.add("hide");
body.style.position = "static";
body.style.top = "auto";
document.scrollingElement.scrollTop = documentTop;
})
})();
</script>
</html>
解决js 禁止滚动条滚动,并且滚动条不消失,页面大小不闪动
下面就是:
- 关闭页面滚动的时候,记录页面的滚动位置。然后监听滚轮滚动事件,它一滚动就恢复原来的位置
- 恢复页面滚动的时候,把记录的位置都清空,然后该怎么滚就怎么滚
- 在scrollTo方法中,最好加上behavior:'instant’属性。
- 例:window.scrollTo({top:winY,behavior:‘instant’});
但是,我自己在测试的时候,还是会有下方页面滚动的现象,但是最终还是回到原来的位置,就想弹簧一样。
var winX = null;
var winY = null;
window.addEventListener('scroll', function () {
if (winX !== null && winY !== null) {
window.scrollTo(winX, winY);
}
});
function disableWindowScroll() {
winX = window.scrollX;
winY = window.scrollY;
}
function enableWindowScroll() {
winX = null;
winY = null;
}
解决窗口滚动条消失而导致的页面内容抖动的问题
今天在项目中遇到了这样一个问题,点击按钮出现遮罩层和弹窗,不过弹窗出现后还是可以滚动底部的页面,于是写了一段js使弹窗出来后使底部页面增添overflow: hidden的样式解决了这个问题,但是还有一个细节问题就是页面的滚动条也占据宽度,滚动条消失后会有一个抖动的效果,给人体验很不好
尝试了几个办法,以下是个人认为比较好的解决办法:
首先获取窗口文档显示区的宽度和DOM文档的根节点html元素对象的宽度,前者减去后者计算出滚动条的宽度
// 滚动条宽度
let scrollWidth = window.innerWidth - document.documentElement.offsetWidth;
弹窗打开时,滚动条消失先给body元素设置overflow:hidden防止底部页面滚动,然后再给底部页面右侧的padding 设置scrollWidth的值来模拟滚动条存在时底部页面内容的位置
document.body.style.overflow = 'hidden';
this.$refs.index.style.paddingRight = scrollWidth + 'px';
弹窗关闭时,body元素设置overflow:auto,因为弹窗关闭时弹窗又恢复了,重新获取 scrollWidth 的值设置padding为0,这样底部页面的内容就不会在抖动啦,以下为完整代码(不是一个组件的方法,主要逻辑就是上面的代码)
show() {
this.isShow = !this.isShow;
// 滚动条宽度
let scrollWidth = window.innerWidth - document.documentElement.offsetWidth;
document.body.style.overflow = 'hidden';
this.$refs.index.style.paddingRight = scrollWidth + 'px';
}
close() {
this.isShow = false;
this.$parent.show();
document.body.style.overflow = 'auto';
}
完美解决方案
<script>
document.getElementById("enable").onclick = function () {
enableScroll();
document.getElementById("status").innerHTML = "enabled";
document.getElementById("status").className = "enabled";
};
document.getElementById("disable").onclick = function () {
disableScroll();
document.getElementById("status").innerHTML = "disabled";
document.getElementById("status").className = "disabled";
};
// left: 37, up: 38, right: 39, down: 40
const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };
function preventDefault(e) {
e.preventDefault();
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
let supportsPassive = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, "passive", {
get: function () {
supportsPassive = true;
},
})
);
} catch (e) {}
const wheelOpt = supportsPassive ? { passive: false } : false;
const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";
// call this to Disable
function disableScroll() {
window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
window.addEventListener("keydown", preventDefaultForScrollKeys, false);
}
// call this to Enable
function enableScroll() {
window.removeEventListener("DOMMouseScroll", preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener("touchmove", preventDefault, wheelOpt);
window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
}
</script>
- preventDefaultForScrollKeys事件主要阻止键盘↑↓←→键的默认行为;
- e.preventDefault()该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。相关文档
- { passive: false }的作用:passive的作用
- window.addEventListener(“test”, null…这段代码来源于:addEventListener
- …“onwheel” in document.createElement(“div”)…滚轮事件的不同浏览器兼容
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
}
body {
width: 100%;
}
.box1 {
height: 1000px;
width: 100%;
background-color: #bfa;
}
</style>
<script>
// left: 37, up: 38, right: 39, down: 40
const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };
function preventDefault(e) {
e.preventDefault();
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
let supportsPassive = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, "passive", {
get: function () {
supportsPassive = true;
},
})
);
} catch (e) { }
const wheelOpt = supportsPassive ? { passive: false } : false;
const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";
// call this to Disable
function disableScroll() {
window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
window.addEventListener("keydown", preventDefaultForScrollKeys, false);
}
// call this to Enable
function enableScroll() {
window.removeEventListener("DOMMouseScroll", preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener("touchmove", preventDefault, wheelOpt);
window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
}
window.onload = () => {
let btn1 = document.querySelector('#btn1')
let btn2 = document.querySelector('#btn2')
btn1.onclick = () => {
disableScroll()
}
btn2.onclick = () => {
enableScroll()
}
}
</script>
</head>
<body>
<div class="box1">
一段很长的文字...[使用lorem1000]
</div>
<button id="btn1">disableWindowScroll</button>
<button id="btn2">enableWindowScroll</button>
</body>
</html>
vue中使用
把这两个函数暴露出去就行了,然后打开弹窗的时候,调用disableScroll方法,在关闭弹窗的时候,调用enableScroll的方法
scrollCtrl.js
// 控制页面的滚动条是否可以跟随鼠标滚动而滚动
// left: 37, up: 38, right: 39, down: 40
const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };
function preventDefault(e) {
e.preventDefault();
}
function preventDefaultForScrollKeys(e) {
if (keys[e.keyCode]) {
preventDefault(e);
return false;
}
}
let supportsPassive = false;
try {
window.addEventListener("test", null, Object.defineProperty({}, "passive", {
get: function () {
supportsPassive = true;
},
})
);
} catch (e) { }
const wheelOpt = supportsPassive ? { passive: false } : false;
const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";
// call this to Disable
function disableScroll() {
window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF
window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
window.addEventListener("touchmove", preventDefault, wheelOpt); // mobile
window.addEventListener("keydown", preventDefaultForScrollKeys, false);
}
// call this to Enable
function enableScroll() {
window.removeEventListener("DOMMouseScroll", preventDefault, false);
window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
window.removeEventListener("touchmove", preventDefault, wheelOpt);
window.removeEventListener("keydown", preventDefaultForScrollKeys, false);
}
export {
enableScroll,
disableScroll
}
PrevImg.vue
组件中使用scrollCtrl.js
<template>
<transition name="slidedown">
<div class="prev-container" v-show="isShow">
<div class="mask"></div>
<div class="indicator">
<i @click="operate('zoomIn')" class="iconfont icon-jiahao1"></i>
<i @click="operate('zoomOut')" class="iconfont icon-jian"></i>
<i @click="operate('rotate')" class="iconfont icon-reset"></i>
<i @click="close" class="iconfont icon-close"></i>
</div>
<div :class="['prev-next', 'left', { 'pointer-allowd': isPrev }]" @click="operate('left')">
<i class="iconfont icon-houtuishangyige"></i>
</div>
<div :class="['prev-next', 'right', { 'pointer-allowd': isNext }]" @click="operate('right')">
<i class="iconfont icon-qianjinxiayige"></i>
</div>
<div ref="imgWrapperRef" class="img-wrapper">
<img :src="imgPaths[imgIdx]" :style="imgStyle" alt="">
</div>
</div>
</transition>
</template>
<script>
import {enableScroll,disableScroll} from '@/utils/scrollCtrl.js'
export default {
name: 'PrevImg',
components: {
},
data() {
return {
isShow: false,
imgIdx: 0,
imgPaths: [],
transform: {
rotate: 0,
scale: 1
},
enterAniClass: '',
winY: null,
}
},
computed: {
imgStyle() {
let { rotate, scale } = this.transform
return {
transform: `scale(${scale}) rotate(${rotate}deg)`
}
},
isNext() {
return this.imgIdx != this.imgPaths.length - 1
},
isPrev() {
return this.imgIdx != 0
},
},
mounted() {
this.$refs['imgWrapperRef'].onclick = e => {
console.log(e.target, 2323);
if (e.target == this.$refs['imgWrapperRef']) {
this.close()
}
}
console.log(enableScroll,disableScroll,'11');
},
methods: {
open({ imgIdx, imgPaths }) {
this.isShow = true
this.imgIdx = imgIdx
this.imgPaths = imgPaths
disableScroll() // 关闭滚动
},
close() {
console.log('object');
this.isShow = false
this.transform = {
rotate: 0,
scale: 1
}
enableScroll() // 打开滚动
},
operate(command) {
if (command == 'zoomIn') {
this.transform.scale += 0.04
} else if (command == 'zoomOut') {
this.transform.scale -= 0.04
} else if (command == 'rotate') {
this.transform.rotate += 90
} else {
this.transform = {
rotate: 0,
scale: 1
}
if (command == 'left') {
if (this.imgIdx == 0) {
return
}
this.imgIdx = this.imgIdx - 1
} else if (command == 'right') {
if (this.imgIdx == this.imgPaths.length - 1) {
return
}
this.imgIdx = this.imgIdx + 1
}
}
}
}
}
</script>
<style lang="scss">
.slidedown-enter-active,
.slidedown-leave-active {
transition: all 0.5s;
}
.slidedown-enter,
.slidedown-leave-to {
opacity: 0;
transform: translateY(-20px);
}
.slidedown-enter-to,
.slidedown-leave {
opacity: 1;
transform: translateY(0)
}
.prev-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99999999999;
.mask {
position: absolute;
background-color: rgba(0, 0, 0, .3);
width: 100%;
height: 100%;
z-index: -1;
}
.pointer-allowd {
cursor: pointer !important;
}
.prev-next {
width: 36px;
height: 36px;
border-radius: 50%;
background-color: rgba(255, 255, 255, .3);
text-align: center;
line-height: 36px;
cursor: not-allowed;
position: absolute;
top: 0;
bottom: 0;
margin: auto;
z-index: 2000;
}
.left {
left: 30px;
}
.right {
right: 30px;
}
.indicator {
display: flex;
justify-content: flex-end;
padding: 20px;
z-index: 100;
i {
padding: 10px;
background-color: rgba(255, 255, 255, .3);
border-radius: 50%;
margin: 0 5px;
cursor: pointer;
}
}
.img-wrapper {
position: absolute;
top: 0;
width: 100%;
height: 100%;
z-index: -1;
display: flex;
align-items: center;
justify-content: center;
img {
max-height: 100%;
max-width: 80%;
transition: all 0.5s;
}
}
}</style>