前言
模态框是指覆盖在父窗口上的子窗口,但在HTML网页中,并没有父窗口和子窗口的概念。这里是通过可隐藏的遮罩层和一个可隐藏的盒子来实现模态框的效果。
效果演示:
下面开始详细介绍如何实现一个可拖拽的模态框。只对 JS 部分详解,HTML 和 CSS 会放在文章底部的源代码中!
JavaScript详解
整体效果是由以下几个事件构成:
-
点击立即登录按钮,弹出遮罩层和模态框。
-
点击小叉号关闭模态框和遮罩层。
-
鼠标在模态框的标题上按下时,计算鼠标在模态框中的坐标。
-
给整个HTML文档添加鼠标移动事件,通过算法实现模态框跟随鼠标移动。
-
鼠标在HTML文档中松开时,移除HTML文档的鼠标移动事件。
首先获取我们需要操作的元素
let but = document.querySelector('.but') // 立即登录按钮
let shade = document.querySelector('.shade') // 遮罩层
let loginBox = document.querySelector('.login-box') // 模态框
let title = document.querySelector('.title') // 模态框标题:用户登录
let exit = document.querySelector('.exit'); // 小叉号
点击立即登录按钮,弹出遮罩层和模态框。
but.addEventListener('click', function() {
shade.style.display = "block"; // 显示遮罩层
loginBox.style.display = "block"; // 显示模态框
});
点击小叉号关闭模态框和遮罩层。
exit.addEventListener('click', function() {
shade.style.display = "none"; // 隐藏遮罩层
loginBox.style.display = "none"; // 隐藏遮罩层
});
鼠标在模态框标题上按下时计算鼠标在模态框中的坐标:
title.addEventListener('mousedown', function(event) {
let x = event.pageX - loginBox.offsetLeft;
let y = event.pageY - loginBox.offsetTop;
});
event.pageX 和 event.pageY:获取鼠标在整个页面中的 x 坐标、y 坐标。
loginBox.offsetLeft 和 loginBox.offsetTop:获取模态框距离页面左边和上边的距离。
通过相减的方式计算出鼠标在模态框中的坐标。
鼠标在模态框标题上按下后,再给 document 对象添加鼠标移动事件:
title.addEventListener('mousedown', function(event) {
let x = event.pageX - loginBox.offsetLeft;
let y = event.pageY - loginBox.offsetTop;
document.addEventListener('mousemove', function() {
// 鼠标移动后的新坐标减去鼠标在模态框中的坐标,实现模态框跟随鼠标移动
loginBox.style.left = (event.pageX - x)+"px";
loginBox.style.top = (event.pageY - y)+"px";
});
});
这里为什么不把鼠标移动事件给 title ?
如果把鼠标移动事件给 title 的话,鼠标移动过快,会脱离模态框,导致模态框无法跟随移动。
如果想看效果,把这里的 document 换成 title,然后快速拖动即可,这里不做演示!
到这里已经实现了模态框跟随鼠标移动,但当我们松开鼠标后,发现模态框依旧跟随鼠标移动。所以,还需要给 document 添加鼠标松开事件。
title.addEventListener('mousedown', function(event) {
let x = event.pageX - loginBox.offsetLeft;
let y = event.pageY - loginBox.offsetTop;
document.addEventListener('mousemove', loginBoxMove);
// 这里需要把鼠标移动事件函数写在外面,因为移除事件监听器时也会用到!
function loginBoxMove(event) {
loginBox.style.left = (event.pageX - x)+"px";
loginBox.style.top = (event.pageY - y)+"px";
}
document.addEventListener('mouseup', function() {
document.removeEventListener('mousemove', loginBoxMove);
})
});
这里为什么不把鼠标松开事件给 title ?
还是会遇到上述类似的情况,大家可以自行尝试!
源代码
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>模态框</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
letter-spacing: 3px;
}
body {
background-color: #ffbf84;
}
/* 立即登录按钮和模态框水平垂直居中 */
.but, .login-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 遮罩层 */
.shade {
display: none;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .3);
/* 这里一定要把遮罩层移到重叠元素的上层,就可以造成父窗口无法操作的效果 */
z-index: 1;
}
/* 立即登录按钮 */
.but {
cursor: pointer;
display: block;
width: 200px;
height: 60px;
border-radius: 30px;
box-shadow: 0 10px 10px rgba(10, 20, 20, .2);
background-color: #fa8282;
color: #fff;
font-size: 25px;
text-align: center;
line-height: 60px;
}
.but:hover {
transition: background-color 0.5s;
background-color: #f36886;
}
/* 模态框 */
.login-box {
display: none;
width: 350px;
height: 250px;
border: 1px solid rgb(216, 216, 216);
border-radius: 10px;
box-shadow: 5px 5px 10px rgba(10, 20, 20, .2), -5px -5px 10px rgba(10, 20, 20, .2);
background-color: #fff;
/* 和遮罩层同理,模态框一定要在最上层 */
z-index: 1;
}
.login-box .title {
cursor: move;
user-select: none; /* 禁止用户选中文字 */
position: relative;
width: 100%;
height: 70px;
color: #3f3f3f;
font-size: 20px;
font-weight: 700;
text-align: center;
line-height: 70px;
}
.login-box .title .exit {
position: absolute;
top: -10px;
right: 10px;
font-size: 30px;
}
.login-box .title .exit:hover {
cursor: pointer;
text-shadow: 2px 2px 4px rgba(10, 20, 20, .5);
}
.login-box form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 10px 0;
}
.login-box form .input-box {
width: 60%;
height: 35px;
margin-bottom: 20px;
padding: 0px 10px;
border: 1px solid #3f3f3f;
border-radius: 8px;
color: #3f3f3f;
font-size: 16px;
font-weight: 700;
}
.login-box form .input-box:focus {
outline: none;
}
.login-box form .login-but {
width: 120px;
height: 35px;
border: none;
background-color: #fa8282;
border-radius: 8px;
color: #fff;
font-size: 20px;
font-weight: 700;
}
.login-box form .login-but:hover {
background-color: #f36886;
}
</style>
</head>
<body>
<!-- 遮罩层 -->
<div class="shade"></div>
<!-- 登录按钮 -->
<span class="but">立即登录</span>
<!-- 模态框 -->
<div class="login-box">
<div class="title">
用户登录
<span class="exit">×</span>
</div>
<form action="">
<input type="text" class="input-box" placeholder="用户名">
<input type="password" class="input-box" placeholder="密码">
<input type="submit" class="login-but" value="登录">
</form>
</div>
<script>
let but = document.querySelector('.but');
let shade = document.querySelector('.shade');
let loginBox = document.querySelector('.login-box');
let title = document.querySelector('.title');
let exit = document.querySelector('.exit');
// 立即登录按钮点击事件
but.addEventListener('click', function() {
shade.style.display = "block";
loginBox.style.display = "block";
});
// 关闭模态框事件
exit.addEventListener('click', function() {
shade.style.display = "none";
loginBox.style.display = "none";
});
// 拖动标题区域可移动模态框
title.addEventListener('mousedown', function(event) {
// 计算鼠标在登录框中坐标
let x = event.pageX - loginBox.offsetLeft;
let y = event.pageY - loginBox.offsetTop;
// 给页面添加鼠标移动事件
document.addEventListener('mousemove', loginBoxMove);
function loginBoxMove(event) {
loginBox.style.left = (event.pageX - x)+"px";
loginBox.style.top = (event.pageY - y)+"px";
}
// 鼠标松开后移除页面的鼠标移动事件
document.addEventListener('mouseup', function() {
document.removeEventListener('mousemove', loginBoxMove);
})
});
</script>
</body>
</html>