一、开篇
这是这个拖拽系列的第三篇了,第一篇简单的介绍了一下这个拖拽框架,第二篇用这个框架做了一个Tab标签的拖放。这次用这个拖拽框架做一个更复杂一点的效果——跨列拖放。就像iGoogle和netvibes的个性页面布局那样。
二、原理
框架在第一篇做了介绍,这里直接使用。
首先要找到每个可以拖动的item,对于每个拖动的item,对其注册组件Drag.init(handle,dragBody);并且要把这个对象所在的列赋值给这个对象
还是分三个步骤说这个拖放的过程
1、鼠标拖动开始的时候,除了要将dragGhost放到当前被拖动的对象原来的位置上,还要记录每一列的左边距,当然还是要设置被拖动对象的position
2、鼠标拖动的过程中
先找到拖动对象是在哪一列,通过现在拖动对象的位置和在拖动开始时记录的列的左边距相比较,得出当前拖动对象在哪一列。
如果在原来那一列上,则不用管了,如果在别的列上,则将dragGhost插入到这个新的列中去(用column.appendChild插入到最后)。无论是本来就在这一列的元素还是从别的列拖过来的元素,都得让拖动对象与他所在的列的每一个元素的纵坐标相比较,得出应该在这一列的什么位置,将dragGhost插入(用column.insertBefore)。至于怎么找到拖动元素应该放在这一列的什么位置,这个原理和上边找列的原理差不多,只是比较的是纵坐标的值,比较的对象是本列的所有对象(注意,遍历的时候一定要排除dragGhost和本身,要不然在本列不能向下拖动)。
3、拖动完成
将ghost替换为被拖动的元素,设置相应的样式
三、代码
Code
//------------------------Utility------------------------
function findPosX(obj) {//辅助函数 得到元素左边与浏览器左边的边距
var curleft = 0;
if (obj && obj.offsetParent) {
while (obj.offsetParent) {
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
} else if (obj && obj.x) curleft += obj.x;
return curleft;// + document.body.scrollLeft - document.body.clientLeft;
}
function findPosY(obj) {//辅助函数 得到元素上边与浏览器上边的边距
var curtop = 0;
if (obj && obj.offsetParent) {
while (obj.offsetParent) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
} else if (obj && obj.y) curtop += obj.y;
return curtop;// + document.body.scrollTop - document.body.clientTop;
}
var dragGhost = document.createElement("div");
dragGhost.style.border = "dashed 1px #CCCCCC";
dragGhost.style.background = "white";
dragGhost.style.display = "none";
dragGhost.style.margin = "10px";
var container;
var columns = [];
//------------------------Start Here------------------------
window.onload = function(){
container = document.getElementById("container");
for(var i=0;i<container.childNodes.length;i++){
if(container.childNodes[i].className == "column"){//筛选出所有的列 ff下的childNodes不可靠 :\
columns.push(container.childNodes[i]);
}
}
for(var i=0;i<columns.length;i++){
var column = columns[i];
for(var j=0;j<column.childNodes.length;j++){
var item = column.childNodes[j];
if(item.className == "item"){
item.column = column;//给每个拖拽对象要指明它属于哪一列 而且这个属性会随着拖动而更新的
new dragItem(item);
}
}
}
}
var isIE = document.all;
//------------------------Drag Item------------------------
function dragItem(item){
//item实际上是dragBody(拖动的时候移动的整体)
//在这里需要根据item找到handle(能够拖动的把手)
var handle;
for(var i=0;i<item.childNodes.length;i++){
if(item.childNodes[i].nodeName.toLowerCase() == "h3"){
handle = item.childNodes[i];
break;
}
}
if(!handle)return;
Drag.init(handle,item);
item.onDragStart = function(left,top,mouseX,mouseY){
//开始拖动的时候设置透明度
this.style.opacity = "0.5";
this.style.filter = "alpha(opacity=50)";
dragGhost.style.height = isIE?this.offsetHeight:this.offsetHeight - 2;
//this指的是item
this.style.width = this.offsetWidth;//因为初始的width为auto
this.style.left = findPosX(this) - 5;
this.style.top = findPosY(this) - 5;
this.style.position = "absolute";
//将ghost插入到当前位置
dragGhost.style.display = "block";
this.column.insertBefore(dragGhost,this);
//记录每一列的左边距 在拖动过程中判断拖动对象所在的列会用到
this.columnsX = [];
for(var i=0;i<columns.length;i++){
this.columnsX.push(findPosX(columns[i]));
}
}
item.onDrag = function(left,top,mouseX,mouseY){
//先要判断在哪一列移动
var columnIndex = 0;
for(var i=0;i<this.columnsX.length;i++){
if((left + this.offsetWidth/2) > this.columnsX[i]){
columnIndex = i;
}
}
//如果columnIndex在循环中没有被赋值 则表示当前拖动对象在第一列的左边
//此时也把它放到第一列
var column = columns[columnIndex];
if(this.column != column){
//之前拖动对象不在这个列
//将ghost放置到这一列的最下方
column.appendChild(dragGhost);
this.column = column;
}
//然后在判断放在这一列的什么位置
var currentNode = null;
for(var i=0;i<this.column.childNodes.length;i++){
if(this.column.childNodes[i].className == "item"
&& this.column.childNodes[i] != this//不能跟拖动元素自己比较 否则不能在本列向下移动
&& top < findPosY(this.column.childNodes[i])){//从上到下找到第一个比拖动元素的上边距大的元素
currentNode = this.column.childNodes[i];
break;
}
}
if(currentNode)
this.column.insertBefore(dragGhost,currentNode);
else//拖到最下边 没有任何一个元素的上边距比拖动元素的top大 则添加到列的最后
this.column.appendChild(dragGhost);
}
item.onDragEnd = function(left,top,mouseX,mouseY){
this.style.opacity = "1";
this.style.filter = "alpha(opacity=100)";
this.column.insertBefore(this,dragGhost);
this.style.position = "static";
this.style.display = "block";
this.style.width = "auto";
dragGhost.style.display = "none";
}
}
如果这样不方便观看,可以下载示例。
四、示例下载
点此下载示例