最近学到了js中的事件,感觉和windows的事件队列和处理函数很相似哈。需要监听事件然后要有处理事件的函数。然后做了一个小练习,元素的拖放。废话不说了,上效果。实现子元素任意框拖放。
首先在布局上,三个大盒子中的元素都是绝对定位于他们的父元素,给三个大盒子相对定位。上HTML和CSS代码。
<div id="cnt">
<div><span>1</span><span>2</span></div>
<div><span>3</span><span>4</span></div>
<div><span>5</span><span>6</span></div>
</div>
以上是html代码,比较简单。CSS布局如下:
* {
margin: 0;
padding: 0;
list-style: none;
}
#cnt {
width: 1440px;
margin: 30px auto;
text-align: center;
font-size: 0;
}
#cnt>div {
width: 450px;
height: 360px;
margin: 0 15px;
font-size: 20px;
padding: 0 25px;
vertical-align: top;
box-sizing: border-box;
display: inline-block;
background: papayawhip;
position: relative;
}
#cnt>div>span {
width: 400px;
height: 50px;
line-height: 50px;
margin: 5px 0;
display: block;
color: #fff;
background: aqua;
position: absolute;
z-index: 99;
}
由于子元素的布局放在js中实现,所以子元素叠在一起,最关键的js代码如下:
var nthpar = document.querySelectorAll("#cnt>div");//所有的父元素。兼容不太好,最好用谷歌打开
var nthchild = document.querySelectorAll("#cnt span");
function addevent() {
nthchild = document.querySelectorAll("#cnt span");//所有的span子元素
for(var i = 0; i < nthchild.length; i++) {//遍历添加事件
(function(num) {
nthchild[num].onmousedown = function() {
begaindrag(num);
};
})(i);//闭包添加事件,主要是保存num的私有
}
}
function sortchild(){
for(var i = 0; i < nthpar.length; i++) {//遍历父元素
var child = nthpar[i].children,pos = 0;//父元素下的子元素,由于每次拖放会改变,子元素要重新获取
for(var j = 0; j < child.length; j++) {
child[j].style.top = pos + "px";
child[j].style.left = "25px";
pos += child[0].offsetHeight + 5;//top值累加,每个子元素相隔5px
}
}
}
addevent();//初始化每个子元素添加事件
sortchild();//初始化布局每个盒子子元素
var drag = true;//判断拖放状态
function begaindrag(num) {//鼠标按下事件原型
drag = true;
var isout = false;//判断元素是否出了父元素
var posx = event.pageX - nthchild[num].offsetLeft;//按下时鼠标在元素内部的偏移
var posy = event.pageY - nthchild[num].offsetTop;
window.onmousemove = function() {
if(drag == true) {
var posx_new = event.pageX - posx;
//计算出新的left值,实际上是新的pagex-旧的pagex+元素的offset的值,就是新的left值
var posy_new = event.pageY - posy;
nthchild[num].style.position = "absolute";
nthchild[num].style.left = posx_new + "px";//使得被拖放元素跟随鼠标移动
nthchild[num].style.top = posy_new + "px";
}
window.onmouseup = function() {//关键点,判断是否出界,出界后在哪个盒子里
(Math.abs(posx_new) > Math.abs(nthchild[num].offsetWidth / 2)) && (isout = true);
//鼠标抬起时元素拖放的left位移超过1/2父元素长度就会判断出界
if(isout == true) {
console.log("out");
var par=nthchild[num].parentElement;//被拖放元素的原始父元素保存起来
for(var k = 0; k < nthpar.length; k++){//遍历三个父元素判断进到了哪里
if(nthpar[k] != par && isout != false) {//判断进入哪个盒子,既然出去了,排除原始父元素
//console.log(Math.abs((nthpar[k+1].offsetLeft - nthchild[num].offsetParent.offsetLeft)*0.75));
if( 720 < Math.abs(nthchild[num].offsetLeft)){
//实际上是水平位移超出了1.5个盒子宽度就进入相隔盒子,后来发现访问越界,要加诸多判断,索性给了个常数值
var state=0;if(nthchild[num].offsetLeft>0){state=1;}
//state主要是判断被拖放的方向,左or右。根据位移left的正负判断
console.log("进入相隔");
var nod = nthchild[num].cloneNode(true);//拷贝被拖放元素
par.removeChild(nthchild[num]);//从原始父元素移除被拖放元素
state==1 && par.nextElementSibling.nextElementSibling.appendChild(nod);//根据方向可以判断唯一的被放置的盒子
state==0 && par.previousElementSibling.previousElementSibling.appendChild(nod);
isout = false;
}else
if(Math.abs((nthpar[k].offsetLeft - nthchild[num].offsetParent.offsetLeft)/2)<Math.abs(nthchild[num].offsetLeft)){//进入相邻的盒子,具体的操作如上
var state=0;if(nthchild[num].offsetLeft>0){state=1;}
console.log("进入相邻"+nthchild[num].offsetLeft);
var nod = nthchild[num].cloneNode(true);
par.removeChild(nthchild[num]);
state==1 && par.nextElementSibling.appendChild(nod);
state==0 && par.previousElementSibling.appendChild(nod);
isout = false;
}
}
}
}
drag = false;//若是没有拖拽成功不再响应mousemove的事件
sortchild();//拖放完成后重新排列。如果没有拖出去,重新排列后会回到原位置
addevent();//重新对子元素添加事件
}
}
}
如图可以实现简单的拖放。但是对于越界,和诸多的细节没有更进一步处理。比如第一个盒子中的元素往左移动,会出现访问不到的越界错误。也没有对高度进行进一步的限制。只是给大家提供下思路,希望能帮助到在路上的朋友们(≖ᴗ≖)✧。