很多时候,我们需要通过一层半透明的遮罩层来把后面的一切整体调暗,以便凸显某个特定的 UI 元素,引导用户关注。这个效果最常见的实现方式就是增加一个额外的 HTML 元素用于遮挡背景(.overlay
类):
<style>
body {
/* 需要加上这个属性,不然背景还是可滚动 */
overflow: hidden;
}
/* 构造页面 */
.container {
height: 100000px;
}
.overlay { /* 用于遮挡背景 */
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, .5);
}
.lightbox { /* 需要吸引用户注意的元素 */
position: absolute;
z-index: 1;
/* 其余样式 */
top: 50%;
left: 50%;
color: white;
}
</style>
<div class="container">页面</div>
<div class="lightbox">这里可以弹出一个对话框</div>
<div class="overlay"></div>
这里我们直接显示遮罩层了,通常情况下是需要 js 交互触发才显示的
我们可以看下 bootstrap 的 modal 实现,思路类似,当 modal 出现时,body 元素会加上 modal-open
这个类,其实就是添加 overflow: hidden
样式,使得背景不可滚动(这不是本文重点,只是顺带提一下)
在代码中,.lightbox
需要指定一个更高的 z-index,以便绘制在遮罩层的上层(当然可以有其他办法,比如将 .lightbox
当作 .overlay
的子元素,或者将 .lightbox
当作 .overlay
的后一个兄弟元素,只是设置了 z-index 比较保险,不需要再考虑那些情况)
这个方法需要增加一个额外的 HTML 元素(.overlay
,这里不考虑 .lightbox
)这意味着该效果无法由 CSS 单独实现,不过,我们有其他办法可以摆脱这个麻烦
我们可以利用伪元素来消除额外的 HTML 元素:
<body class="dimmend">
<style>
body {
/* 需要加上这个属性,不然背景还是可滚动 */
overflow: hidden;
}
body.dimmend:before { /* 用于遮挡背景 */
/* 注意 content 属性不能忘 */
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, .5);
}
/* 构造页面 */
.container {
height: 100000px;
}
.lightbox { /* 需要吸引用户注意的元素 */
position: absolute;
z-index: 1;
/* 其余样式 */
top: 50%;
left: 50%;
color: white;
}
</style>
<div class="container">页面</div>
<div class="lightbox">这里可以弹出一个对话框</div>
</body>
这样实现有几个问题:
body::before
可能已经被占用dimmend
这个类后两个问题是无法解决的,针对第一个问题,我们可以把遮罩层交给这个元素自己的 ::before
伪元素实现:
<style>
body {
/* 需要加上这个属性,不然背景还是可滚动 */
overflow: hidden;
}
/* 构造页面 */
.container {
height: 100000px;
}
.lightbox { /* 需要吸引用户注意的元素 */
position: absolute;
z-index: 1;
/* 其余样式 */
top: 50%;
left: 50%;
color: white;
}
.lightbox::before {
content: '';
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, .5);
z-index: -1;
}
</style>
<div class="container">页面</div>
<div class="lightbox">这里可以弹出一个对话框</div>
给伪元素设置 z-index: -1
,就可以让它出现在元素的背后,但无法对遮罩层的 Z 轴进行细粒度的控制。它可能出现在这个元素之后(我们期望),也可能会出现在这个元素的父元素或祖先元素之后
对于简单的应用场景来说,我们可以利用 box-shadow
:
<style>
body {
/* 需要加上这个属性,不然背景还是可滚动 */
overflow: hidden;
}
/* 构造页面 */
.container {
height: 100000px;
}
.lightbox { /* 需要吸引用户注意的元素 */
position: absolute;
z-index: 1;
/* 其余样式 */
top: 50%;
left: 50%;
color: white;
background: red;
/* 模拟遮罩 */
box-shadow: 0 0 0 50vmax rgba(0,0,0,.8);
}
</style>
<div class="container"><a href="https://github.com/hanzichi">页面</a></div>
<div class="lightbox">这里可以模拟弹出一个对话框</div>
该方法有个严重的问题,就是无法阻止用户和页面的其他部分交互
<style>
/**
* Native modal dialog (limited support)
*/
dialog::backdrop {
background: rgba(0,0,0,.8)
}
</style>
<button onclick="document.querySelector('#modal').showModal()">Click me</button>
<dialog id="modal">
O HAI!
<button onclick="this.parentNode.close()">Close</button>
</dialog>
注意下浏览器兼容性