前端新手项目练习之可拖动弹窗
前端新手记录自己在网络上找到的前端练习项目。最近工作忙得要死,感觉自己有些扛不住,这篇文章花了好久的时间。
项目简介
一个简单的弹窗,可以通过标题栏上下左右拖动,还可以通过四个边和四个角改变窗口的大小。另外,还可以通过右上角有最大化、最小化、复原、关闭的按钮来控制窗口。但是限制了窗口的最大和最小的宽度和高度。
可拖动窗口.gif
html部分
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>窗口拖曳</title>
<link type="text/css" rel="stylesheet" href="popupBox.css">
</head>
<body>
<div id="drag">
<!--窗口标题栏-->
<div class="title">
<h2>可拖动的窗口</h2>
<div>
<!--右上的窗口控制按钮,title是鼠标移上去显示的内容-->
<a class="min" href="javascript:;" title="最小化"></a>
<a class="max" href="javascript:;" title="最大化"></a>
<a class="revert" href="javascript:;" title="还原"></a>
<a class="close" href="javascript:;" title="关闭"></a>
</div>
</div>
<!--控制窗口改变大小的元素-->
<div class="resizeL"></div>
<div class="resizeT"></div>
<div class="resizeR"></div>
<div class="resizeB"></div>
<div class="resizeLT"></div>
<div class="resizeTR"></div>
<div class="resizeBR"></div>
<div class="resizeLB"></div>
<!--窗口内容-->
<div class="content">
1. 窗口可以拖动;<br />
2. 窗口可以通过八个方向改变大小;<br />
3. 窗口可以最小化、最大化、还原、关闭;<br />
4. 限制窗口最小宽度/高度。
</div>
</div>
</body>
<script src="popupBox.js" type="text/javascript"></script>
</html>
整个弹窗分成两个部分,一部分是标题栏,上面有标题和4个按钮,一部分是内容栏,上面有一些窗口的介绍。另外还有控制窗口大小改变的8个元素。
CSS部分
body, div, h2 {
margin: 0;
padding: 0;
}
body {
background-color: #333;
font: 12px/1.5 \5fae\8f6f\96c5\9ed1; /* 12px字体,1.5倍行距,微软雅黑 */
}
#drag {
position: absolute;
top: 100px;
left: 100px;
width: 300px;
height: 160px;
background: #e9e9e9;
border: 1px solid #444;
border-radius: 5px;
box-shadow: 0 1px 3px 2px #666;
}
#drag .title {
position: relative;
height: 27px;
margin: 5px;
}
#drag .title h2 {
font-size: 14px;
height: 27px;
line-height: 24px;
border-bottom: 1px solid #A1B4B0;
}
#drag .title div {
position: absolute;
height: 19px;
top: 2px;
right: 0;
}
#drag .title a, a.open {
float: left;
width: 21px;
height: 19px;
display: block;
margin-left: 5px;
background:url(img/tool.png) no-repeat;
}
a.open{
position: absolute;
top: 10px;
left: 50%;
margin-left: -10px;
background-position: 0 0;
}
a.open:hover {
background-position: -29px -29px;
}
#drag .title a.min {
background-position: -29px 0;
}
#drag .title a.min:hover{
background-position: -29px -29px;
}
#drag .title a.max {
background-position: -60px 0;
}
#drag .title a.max:hover{
background-position: -60px -29px;
}
#drag .title a.revert {
background-position: -149px 0;
}
#drag .title a.revert:hover{
background-position: -149px -29px;
}
#drag .title a.close {
background-position: -89px 0;
}
#drag .title a.close:hover{
background-position: -89px -29px;
}
#drag .content {
overflow: auto;
margin: 0 5px;
}
#drag .resizeBR {
position: absolute;
width: 14px;
height: 14px;
right: 0;
bottom: 0;
overflow: hidden;
cursor: nw-resize;
background:url(img/resize.png) no-repeat;
}
#drag .resizeL, #drag .resizeT, #drag .resizeB, #drag .resizeR,
#drag .resizeLT, #drag .resizeTR, #drag .resizeLB {
position: absolute;
background: #000;
overflow: hidden;
opacity: 0; /* 透明度为0,不可见但可以触发事件 */
filter: alpha(opacity=0); /* 透明度兼容IE */
}
#drag .resizeL, #drag .resizeR {
top: 0;
width: 5px;
height: 100%;
cursor: w-resize;
}
#drag .resizeR {
right: 0;
}
#drag .resizeT, #drag .resizeB {
width: 100%;
height: 5px;
cursor: n-resize;
}
#drag .resizeT {
top: 0;
}
#drag .resizeB {
bottom: 0;
}
#drag .resizeLT, #drag .resizeTR, #drag .resizeLB {
width: 8px;
height: 8px;
background: #FF0;
}
#drag .resizeLT{
top: 0;
left: 0;
cursor: nw-resize;
}
#drag .resizeTR{
top:0;
right:0;
cursor:ne-resize;
}
#drag .resizeLB{
left: 0;
bottom: 0;
cursor: ne-resize;
}
CSS部分,后半部分主要是将8个控制窗口大小的元素绝对定位到窗口的4个边和4个角,然后鼠标移上去会变成相应的形状。
javascript部分
本项目的javascript比较复杂,分着说明。
首先封装了个工具方法,用来根据id 、类名或标签获取元素内容。通过将常用的方法进行封装,一方面可以简化代码,方便开发,另一方面方便统一处理浏览器之间的兼容问题。
/**
* 根据id 类名或标签获取元素的工具方法
*/
var get = {
byId: function(id) {
return typeof id === "string" ? document.getElementById(id) : id;
},
byClass: function(sClass, oParent) {
//如果存在getElementsByClassName方法则使用,否则根据正则表达式搜索
if(oParent.getElementsByClassName) {
return oParent.getElementsByClassName(sClass);
}
else {
var aClass = [];
var reClass = new RegExp("(^| )" + sClass + "( |$)");
var aElem = this.byTagName("*", oParent);
for(var i = 0; i < aElem.length; i++) {
reClass.test(aElem[i].className) && aClass.push(aElem[i]);
}
return aClass;
}
},
byTagName: function(elem, obj) {
return (obj || document).getElementsByTagName(elem);
}
};
//最小宽度和高度,用来还原窗口和保证内容的样式
var dragMinWidth = 250;
var dragMinHeight = 124;
下面定义处理拖动事件和4个按钮的函数。拖动事件主要是在鼠标按下的事件中添加鼠标移动的逻辑,根据鼠标移动的距离来决定窗口移动的距离。
/**
* 用来处理拖动事件的函数
* @method drag
* @param {Element} oDrag 需要拖动的窗口元素
* @param {Element} handle 触发拖动事件的元素
*/
function drag(oDrag, handle) {
var disX = disY = 0;
var oMin = get.byClass("min", oDrag)[0];//最小化按钮
var oMax = get.byClass("max", oDrag)[0];//最大化按钮
var oRevert = get.byClass("revert", oDrag)[0];//复原按钮
var oClose = get.byClass("close", oDrag)[0];//关闭按钮
handle = handle || oDrag;
handle.style.cursor = "move";
handle.onmousedown = function(event) {
var event = event || window.event;//兼容IE,获取事件
disX = event.clientX - oDrag.offsetLeft;
disY = event.clientY - oDrag.offsetTop;
document.onmousemove = function(event) {
var event = event || window.event;
var iL = event.clientX - disX;
var iT = event.clientY - disY;
//最大可拖动的范围
var maxL = document.documentElement.clientWidth - oDrag.offsetWidth;
var maxT = document.documentElement.clientHeight - oDrag.offsetHeight;
if(iL <= 0) {
iL = 0;
}
else if(iL >= maxL) {
iL = maxL;
}
if(iT <= 0) {
iT = 0;
}
else if(iT >= maxT) {
iT = maxT;
}
oDrag.style.left = iL + "px";
oDrag.style.top = iT + "px";
return false;
};
//鼠标松开,清除事件
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
//释放鼠标捕获。IE才有
if(this.releaseCapture)
this.releaseCapture();
};
//设置鼠标捕获,IE才有
if(this.setCapture) {
this.setCapture();
}
return false;
};
//最大化按钮
oMax.onclick = function() {
oDrag.style.top = oDrag.style.left = 0;
oDrag.style.width = document.documentElement.clientWidth - 2 + "px";
oDrag.style.height = document.documentElement.clientWidth - 2 + "px";
this.style.display = "none";
oRevert.style.display = "block";
};
//还原按钮
oRevert.onclick = function() {
oDrag.style.width = dragMinWidth + "px";
oDrag.style.height = dragMinHeight + "px";
oDrag.style.left = (document.documentElement.clientWidth - oDrag.offsetWidth) / 2 + "px";
oDrag.style.top = (document.documentElement.clientHeight - oDrag.offsetHeight) / 2 + "px";
this.style.display = "none";
oMax.style.display = "block";
};
//最小化按钮
oMin.onclick = oClose.onclick = function() {
oDrag.style.display = "none";
var oA = document.createElement("a");
oA.className = "open";
oA.href = "javascript:;";
oA.title = "还原";
document.body.appendChild(oA);
oA.onclick = function() {
oDrag.style.display = "block";
document.body.removeChild(this);
this.onclick = null;
};
};
//阻止冒泡
oMin.onmousedown = oMax.onmousedown = oClose.onmousedown = function(event) {
this.onfocus = function () {
this.blur();//失去焦点
};
(event || window.event).cancelBubble = true;
};
}
接下来定义通过鼠标来改变窗口大小的函数。该函数的逻辑比较复杂,既要考虑4个角4个边8种情况,还要考虑窗口大小变化过程中相对页面位置的改变。
/**
* 改变窗口大小的函数
* @method resize
* @param {element} oParent 需要改变大小的窗口元素
* @param {element} handle 用来触发鼠标事件的元素
* @param {boolean} isLeft handle元素是否左侧元素
* @param {boolean} isTop handle元素是否顶部元素
* @param {boolean} lockX X轴宽度是否可以改变
* @param {boolean} lockY Y轴高度是否可以改变
*/
function resize(oParent, handle, isLeft, isTop, lockX, lockY) {
//鼠标按下事件
handle.onmousedown = function(event) {
var event = event || window.event;
var disX = event.clientX - handle.offsetLeft;//鼠标事件位置距离元素的x轴距离
var disY = event.clientY - handle.offsetTop;//鼠标事件位置距离元素的y轴距离
var iParentTop = oParent.offsetTop;//父元素(窗口)顶部位置
var iParentLeft = oParent.offsetLeft;//父元素左侧位置
var iParentWidth = oParent.offsetWidth;//父元素宽度
var iParentHeight = oParent.offsetHeight;//父元素高度
//鼠标移动事件
document.onmousemove = function(event) {
var event = event || window.event;
var iL = event.clientX - disX;//x轴移动距离
var iT = event.clientY - disY;//y轴移动距离
var maxW = document.documentElement.clientWidth - oParent.offsetLeft - 2;//x轴最大可移动距离
var maxH = document.documentElement.clientHeight - oParent.offsetTop - 2;//y轴最大可移动距离
//调整大小后的元素宽度
var iW = isLeft ? iParentWidth - iL : handle.offsetWidth + iL;
//调整大小后的元素高度
var iH = isTop ? iParentHeight - iT : handle.offsetHeight + iT;
if(isLeft) { //窗口左侧定位发生变化
oParent.style.left = iParentLeft + iL + "px";
}
if(isTop) { //窗口右侧定位发生变化
oParent.style.top = iParentTop + iT + "px";
}
if(iW < dragMinWidth) {
iW = dragMinWidth;
}
else if(iW > maxW) {
iW = maxW;
}
if(!lockX) {
oParent.style.width = iW + "px";
}
if(iH < dragMinHeight) {
iH = dragMinHeight;
}
else if(iH > maxH) {
iH = maxH;
}
if(!lockY) {
oParent.style.height = iH + "px";
}
//如果已经达到最小宽度或者高度,则清除事件
if((isLeft && iW == dragMinWidth) || (isTop && iH == dragMinHeight)) {
document.onmousemove = null;
}
return false;
}
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
return false;
}
};
最后就是加载页面时候的函数。
window.onload = window.onresize = function() {
//获取元素
var oDrag = document.getElementById("drag");
var oTitle = get.byClass("title", oDrag)[0];
var oL = get.byClass("resizeL", oDrag)[0];
var oT = get.byClass("resizeT", oDrag)[0];
var oR = get.byClass("resizeR", oDrag)[0];
var oB = get.byClass("resizeB", oDrag)[0];
var oLT = get.byClass("resizeLT", oDrag)[0];
var oTR = get.byClass("resizeTR", oDrag)[0];
var oBR = get.byClass("resizeBR", oDrag)[0];
var oLB = get.byClass("resizeLB", oDrag)[0];
drag(oDrag, oTitle);
//根据四角改变窗口大小
resize(oDrag, oLT, true, true, false, false);
resize(oDrag, oTR, false, true, false, false);
resize(oDrag, oBR, false, false, false, false);
resize(oDrag, oLB, true, false, false, false);
//根据四边改变窗口大小
resize(oDrag, oL, true, false, false, true);
resize(oDrag, oT, false, true, true, false);
resize(oDrag, oR, false, false, false, true);
resize(oDrag, oB, false, false, true, false);
//定位窗口的左侧和顶部,用来保证窗口居中
oDrag.style.left = (document.documentElement.clientWidth - oDrag.offsetWidth) / 2 + "px";
oDrag.style.top = (document.documentElement.clientHeight - oDrag.offsetHeight) / 2 + "px";
}
总结
本来以为像弹窗这样经常使用的组件写起来应该很简单,写完这个项目才发现并没有想象中那么容易,其中也涉及到很多逻辑和知识,感觉自己前端要学习的东西还很多。