【CSON原创】 支持行拖动,列拖动的表格插件

效果预览:
支持行拖动,列拖动的表格插件
col1_head col2_head col3_head col4_head
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
4 6 7 8
 
功能说明:

当鼠标移动到表头区域时可以对列进行拖动排序,鼠标移动到行区域时可以对行进行拖动排序。

支持IE6 7 8 firefox chrome

实现原理:

当鼠标在表头区域按下时,复制现有的table(不复制其后代结点),并把选择列的所有元素复制添加到新table中,通过按下时的鼠标位置和鼠标移动坐标,确定新table的位置,在行区域按下时同理。

代码分析:

var SortTable = ( function () {

return function (sTableId, options) {
this .init(sTableId, options);
}


})();

首先在构造函数里调用sortTable的init方法进行初始化。需要传入table的id和自定义选项对象,该对象里面包含用户自定义的属性值,对于没有传入的值,使用默认值。

SortTable.prototype = ( function () {

var dragTable; // 保存拖动的table
var isDragCol = false ; // 是否拖动列
var isDragsRow = false ; // 是否拖动行
var dragingIndex; // 拖动的列索引或行索引


/* 点击位置相对于元素位置的位置对象 */
var relPos = {
left:
0 ,
top:
0 ,
updatePos:
function (left, top) { // 更新目前的相对位置
this .left = left;
this .top = top;
}

};

    首先prototype里面有四个私有变量:

    1.拖动中的table的引用:就是拖动行或者拖动列对象的引用

    2.是否拖动列的布尔值:用户mouseover事件处理程序中判断是否处于拖动列的状态中,

    3.是否拖动行的布尔值:用户mouseover事件处理程序中判断是否处于拖动列的状态中,

    4.拖动行/列的索引:就是拖动的行或者列的索引值,用于查找该行/列并复制成新的table对象。

/* 设定索引和左位置值 */
var setIndexAndPos = function (elems, dir) {
dir
= dir || ' left ' ;

for ( var i = 0 , len = elems.length; i < len; i ++ ) {
elems[i].index
= i;
if (dir == ' left ' ) {
elems[i].posLeft
= util.getCurrentPosition(elems[i]).left;
}
else if (dir == ' top ' ) {
elems[i].posTop
= util.getCurrentPosition(elems[i]).top;
}

}

};

       该私有方法在初始化过程中被调用,遍历元素集合,并设置其index和left或top的位置值。其中left值用于列拖动,top值用于行拖动,设定哪个值根据用户传入的dir来判断。

/* 设置新结点的样式 */
var setNewNodeStyle = function (newNode, nodeToClone) {

var newStyle = newNode.style;
var oldStyle = util.getComputedStyle(nodeToClone);
newStyle.paddingLeft
= oldStyle.paddingLeft;
newStyle.paddingRight
= oldStyle.paddingRight;
newStyle.paddingTop
= oldStyle.paddingTop;
newStyle.paddingBottom
= oldStyle.paddingBottom;
newStyle.width
= nodeToClone.clientWidth - parseInt(newStyle.paddingLeft) - parseInt(newStyle.paddingRight) + ' px ' ;
newStyle.height
= nodeToClone.clientHeight - parseInt(newStyle.paddingTop) - parseInt(newStyle.paddingBottom) + ' px ' ;

}

      用于根据某结点设定新结点的样式,使新结点的样式和参考结点的样式相同。该私有方法将用于行拖动时使新table各个td的值和参考table中的一样,目的是使拖动行幻影的样式和表格中的行一致。

/* 把点击列整列结点复制到新table */
var copyCol = function (index, rows, dragingClass, oTable) {

var newNode;
var nodeToClone;
var newTr;
var newBody;

dragTable
= oTable.cloneNode( false );
newBody
= document.createElement( ' TBODY ' ); // ie下tr必须包在thead tbody或tfoot里
dragTable.appendChild(newBody);

dragTable.className
+= ' ' + dragingClass;
for ( var i = 0 , len = rows.length; i < len; i ++ ) {
newTr
= document.createElement( ' TR ' );

nodeToClone
= rows[i].cells[index];
newNode
= nodeToClone.cloneNode( true );
newTr.appendChild(newNode);
newBody.appendChild(newTr);
}
document.body.appendChild(dragTable);
}

      之后的函数用于把所点击列的结点复制到新的table用作拖动幻影。需要注意的是对于IE,必须创建tbody元素,并把新table的tr包含在tbody里,否则新table的结点会显示不出来。另外新table需要添加拖动样式类,该类在初始化时由用户传入,决定拖动幻影的样式。

/* 复制行 */
var copyRow = function (oTr, dragingClass, oTable) {
var newTd;
var oldTd;
var newBody;
var dragTr;


dragTable
= oTable.cloneNode( false );
newBody
= document.createElement( ' TBODY ' );
dragTable.appendChild(newBody);

dragTr
= oTr.cloneNode( true );
newBody.appendChild(dragTr);


dragTable.className
+= ' ' + dragingClass;
for ( var i = 0 , len = dragTr.cells.length; i < len; i ++ ) {
newTd
= dragTr.cells[i];
oldTd
= oTr.cells[i];
setNewNodeStyle(newTd, oldTd);
}

document.body.appendChild(dragTable);

}

      复制行的函数,方法和复制列大致一样,通过创建新table,并根据所拖动行的索引获取对应的tr,把该tr复制到新table中去,同样要记得创建tbody标签。

/* 拖动时更新位置 */
var updateDragingPosition = function (left, top) {
dragTable.style.left
= left + ' px ' ;
dragTable.style.top
= top + ' px ';
}

      该方法在每次触发mousemove事件时调用,作用是更新拖动幻影的位置坐标,使之随着鼠标的移动而移动。

/* 放置拖动列 */
var placeCol = function (left, headers, rows) {

var halfDistant;
var parent = headers[ 0 ].parent;
var len = headers.length;

if (left < headers[ 0 ].posLeft) {
placeAllColCells(
0 , rows);
return ;
}
else {
for ( var i = 0 ; i < len; i ++ ) {
halfDistant
= headers[i].posLeft + (headers[i].clientWidth / 2 );
if (left >= headers[i].posLeft && left < halfDistant) {

placeAllColCells(i, rows);

return ;
}
else if (left >= halfDistant && left < headers[i].posLeft + headers[i].clientWidth) {
placeAllColCells(i
+ 1 , rows);
return ;
}
}
placeAllColCells(i, rows);
}

};

      该方法用于在每次鼠标松开时,把选定的列插入到目标位置。在确定位置之前会左三个判断:

     1.首先判断目前拖动幻影左位置是否小于第一个表头的左位置,是则把选定列插入到第一个列之前。

     2.逐个判断处于列的前半部分还是后半部分,如果是前半部分,则插入到该列的之前,否则插入到该列之后。

     3.如果之前的情况都不符合,则把结点插入到列集合的最尾。

/* 放置拖动列的所有结点 */

var placeAllColCells = function (index, rows) {


for ( var i = 0 , len = rows.length; i < len; i ++ ) {
var parent = rows[i].cells[ 0 ].parentNode;

if (index == rows[i].cells.length) {
parent.appendChild(rows[i].cells[dragingIndex]);
}
else {
parent.insertBefore(rows[i].cells[dragingIndex], rows[i].cells[index]);
}
}


}

 

      函数在上面的函数确定了插入列位置之后,使用该函数进行具体的插入操作,其中如果传入的i和length相等,则表示需要插入到列集合的最后,这时需要用appendchild而非insertbefore(虽然非IE的浏览器中insertbefore可以使用不存在的“尾结点”作为参考点来进行插入操作,但是IE会报错,所以为了兼容,还是应该使用appendchild进行插入)。

/* 放置拖动行 */
var placeRow = function (top, rows) {
var halfDistant;
var parent = rows[ 0 ].parentNode;
var len = rows.length;

if (top < rows[ 0 ].posTop) {
parent.insertBefore(rows[dragingIndex], rows[
0 ]);
return ;
}
else {

for ( var i = 0 ; i < len; i ++ ) {
halfDistant
= rows[i].posTop + (rows[i].clientHeight / 2 );
if (top >= rows[i].posTop && top < halfDistant) {
parent.insertBefore(rows[dragingIndex], rows[i]);
return ;
}
else if (top >= halfDistant && top < rows[i].posTop + rows[i].clientHeight) {
i
+ 1 < len ? parent.insertBefore(rows[dragingIndex], rows[i + 1 ]) : parent.appendChild(rows[dragingIndex]);
return ;
}
}
parent.appendChild(rows[dragingIndex]);
}

};

 

      放置拖动行的函数,该函数思路和放置拖动列的差不多,也是通过对三种情况的判断,确定行插入位置,再进行具体的插入操作,详细可以参考上面的列插入操作。

/* 删除拖动幻影列 */
var removeShadowArray = function () {

document.body.removeChild(dragTable);

};

 

      该函数用于在鼠标松开时,删除拖动幻影。

/* 鼠标按下事件处理程序 */
var downHandler = function (rows, dragingClass, oTable, onBeginDrag) {

return function (event) {
var _elemPosition;
var event = util.getEventObj(event);
var target = util.getEventTarget(event);
var selectedTr;

if (target.tagName !== ' TH ' && target.tagName !== ' TD ' && target.parentNode.tagName !== ' TH ' ) {
return ;
}
else {
onBeginDrag();
// 执行开始拖动的回调函数

if (target.tagName == ' TH ' ) { // 判断点击是否为th(列拖动)
dragingIndex = target.index;
_elemPosition
= util.getCurrentPosition(target);
relPos.updatePos(event.clientX
- _elemPosition.left, event.clientY - _elemPosition.top);
copyCol(dragingIndex, rows, dragingClass, oTable);
isDragCol
= true ;
}

else if (target.tagName == ' TD ' ) { // 判断点击是否为td(行拖动)

selectedTr
= target.parentNode;
dragingIndex
= selectedTr.index;
_elemPosition
= util.getCurrentPosition(selectedTr);
relPos.updatePos(event.clientX
- _elemPosition.left, event.clientY - _elemPosition.top);
copyRow(selectedTr, dragingClass, oTable);
isDragsRow
= true ;
}

updateDragingPosition(event.clientX
- relPos.left, event.clientY - relPos.top); // 更新拖动幻影位置
}

}

};

      鼠标按下时的事件处理程序,其中的onBeginDrag是用户初始化时定义的开始拖动的回调函数。然后判断点击区域 ,再进行相应的行拖动或列拖动。最后调用updateDragingPosition函数更新拖动幻影的位置。

/* 鼠标拖动事件处理程序 */
var moveHandler = function (oTable, onDraging) {
return function (event) {

var event = util.getEventObj(event);
var target = util.getEventTarget(event);

if (isDragCol || isDragsRow) {
onDraging();
updateDragingPosition(event.clientX
- relPos.left, event.clientY - relPos.top, oTable);

}

}

};

      鼠标拖动的处理程序,其中如果是行拖动或者列拖动状态,则更新拖动幻影的坐标(行拖动和列拖动使用同一个更新函数)。并调用onDraging函数,该函数在初始化时由用户定义,在拖动期间执行。

/* 鼠标松开事件处理程序 */
var upHandler = function (headers, rows, tBodyRows, onEndDrag) {
return function (event) {

var event = util.getEventObj(event);
var target = util.getEventTarget(event);
if ( ! isDragCol && ! isDragsRow) {
return ;
}
else {
if (isDragCol) {

placeCol(event.clientX
- relPos.left, headers, rows);
isDragCol
= false ;
setIndexAndPos(headers,
' left ' );

}
else if (isDragsRow) {
placeRow(event.clientY
- relPos.top, tBodyRows);
isDragsRow
= false ;
setIndexAndPos(tBodyRows,
' top ' );

}
removeShadowArray();
onEndDrag();

}

}

}

        鼠标松开时的事件处理程序,把相应的拖动状态值设置为false,并调用removeShadowArray函数把拖动幻影的table从文档中删除。最后触发onEndDrag回调函数,该函数由用户在初始化时定义。

/* 为事件添加处理程序 */
var addDragHandler = function (rows, dragingClass, oTable, headers, tBodyRows, onBeginDrag, onEndDrag, onDraging) {
util.addEventHandler(document,
' mousedown ' , downHandler(rows, dragingClass, oTable, onBeginDrag));
util.addEventHandler(document,
' mousemove ' , moveHandler(oTable, onDraging));
util.addEventHandler(document,
' mouseup ' , upHandler(headers, rows, tBodyRows, onEndDrag));

};

      为拖动事件添加事件处理程序。

return {
/* 初始化 */
init:
function (sTableId, options) {


var defaults = {
dragingClass:
' draging ' ,
onBeginDrag: util.emptyFunction,
onEndDrag: util.emptyFunction,
onDraging: util.emptyFunction


};
util.extend(defaults, options);

this .oTable = util.$(sTableId);
this .rows = this .oTable.rows;
this .tHead = this .oTable.tHead;
this .tBody = this .oTable.tBodies[ 0 ];
this .headers = this .tHead.rows[ 0 ].cells;
this .dragingClass = defaults.dragingClass;
this .onBeginDrag = util.bindFunction(defaults.onBeginDrag);
this .onEndDrag = util.bindFunction(defaults.onEndDrag);
this .onDraging = util.bindFunction(defaults.onDraging);

setIndexAndPos(
this .headers, ' left ' );
setIndexAndPos(
this .tBody.rows, ' top ' );

addDragHandler(
this .rows, this .dragingClass, this .oTable, this .headers, this .tBody.rows, this .onBeginDrag, this .onEndDrag, this .onDraging);



}


}

})();

      最后返回prototype对象,其中的init方法用户初始化。另外需要注意的是三个回调函数由于使用了bindFunction,因此它们调用时this指向该SortTable对象,可以方便地使用该对象中的属性值。

new SortTable( ' sTable ' );

      最后是调用方法。

所有源代码:

 

View Code
var SortTable = ( function () {



return function (sTableId, options) {
this .init(sTableId, options);
}


})();

SortTable.prototype
= ( function () {



var dragTable; // 保存拖动的table
var isDragCol = false ; // 是否拖动列
var isDragsRow = false ; // 是否拖动行
var dragingIndex; // 拖动的列索引或行索引


/* 点击位置相对于元素位置的位置对象 */
var relPos = {
left:
0 ,
top:
0 ,
updatePos:
function (left, top) {
this .left = left;
this .top = top;
}

};

/* 设定索引和左位置值 */
var setIndexAndPos = function (elems, dir) {
dir
= dir || ' left ' ;

for ( var i = 0 , len = elems.length; i < len; i ++ ) {
elems[i].index
= i;
if (dir == ' left ' ) {
elems[i].posLeft
= util.getCurrentPosition(elems[i]).left;
}
else if (dir == ' top ' ) {
elems[i].posTop
= util.getCurrentPosition(elems[i]).top;
}

}

};
/* 设置新结点的样式 */
var setNewNodeStyle = function (newNode, nodeToClone) {

var newStyle = newNode.style;
var oldStyle = util.getComputedStyle(nodeToClone);
newStyle.paddingLeft
= oldStyle.paddingLeft;
newStyle.paddingRight
= oldStyle.paddingRight;
newStyle.paddingTop
= oldStyle.paddingTop;
newStyle.paddingBottom
= oldStyle.paddingBottom;
newStyle.width
= nodeToClone.clientWidth - parseInt(newStyle.paddingLeft) - parseInt(newStyle.paddingRight) + ' px ' ;
newStyle.height
= nodeToClone.clientHeight - parseInt(newStyle.paddingTop) - parseInt(newStyle.paddingBottom) + ' px ' ;

}

/* 把点击列整列结点复制到新table */
var copyCol = function (index, rows, dragingClass, oTable) {

var newNode;
var nodeToClone;
var newTr;
var newBody;

dragTable
= oTable.cloneNode( false );
newBody
= document.createElement( ' TBODY ' ); // ie下tr必须包在thead tbody或tfoot里
dragTable.appendChild(newBody);

dragTable.className
+= ' ' + dragingClass;
for ( var i = 0 , len = rows.length; i < len; i ++ ) {
newTr
= document.createElement( ' TR ' );

nodeToClone
= rows[i].cells[index];
newNode
= nodeToClone.cloneNode( true );
newTr.appendChild(newNode);
newBody.appendChild(newTr);
}
document.body.appendChild(dragTable);
}


/* 复制行 */
var copyRow = function (oTr, dragingClass, oTable) {
var newTd;
var oldTd;
var newBody;
var dragTr;


dragTable
= oTable.cloneNode( false );
newBody
= document.createElement( ' TBODY ' );
dragTable.appendChild(newBody);

dragTr
= oTr.cloneNode( true );
newBody.appendChild(dragTr);


dragTable.className
+= ' ' + dragingClass;
for ( var i = 0 , len = dragTr.cells.length; i < len; i ++ ) {
newTd
= dragTr.cells[i];
oldTd
= oTr.cells[i];
setNewNodeStyle(newTd, oldTd);
}

document.body.appendChild(dragTable);

}
/* 拖动时更新位置 */
var updateDragingPosition = function (left, top) {

dragTable.style.left
= left + ' px ' ;
dragTable.style.top
= top + ' px ' ;

}
/* 放置拖动列的所有结点 */

var placeAllColCells = function (index, rows) {


for ( var i = 0 , len = rows.length; i < len; i ++ ) {
var parent = rows[i].cells[ 0 ].parentNode;

if (index == rows[i].cells.length) {
parent.appendChild(rows[i].cells[dragingIndex]);
}
else {
parent.insertBefore(rows[i].cells[dragingIndex], rows[i].cells[index]);
}
}


}
/* 放置拖动列 */
var placeCol = function (left, headers, rows) {

var halfDistant;
var parent = headers[ 0 ].parent;
var len = headers.length;

if (left < headers[ 0 ].posLeft) {
placeAllColCells(
0 , rows);
return ;
}
else {
for ( var i = 0 ; i < len; i ++ ) {
halfDistant
= headers[i].posLeft + (headers[i].clientWidth / 2 );
if (left >= headers[i].posLeft && left < halfDistant) {

placeAllColCells(i, rows);

return ;
}
else if (left >= halfDistant && left < headers[i].posLeft + headers[i].clientWidth) {
placeAllColCells(i
+ 1 , rows);
return ;
}
}
placeAllColCells(i, rows);
}

};
/* 放置拖动行 */
var placeRow = function (top, rows) {
var halfDistant;
var parent = rows[ 0 ].parentNode;
var len = rows.length;

if (top < rows[ 0 ].posTop) {
parent.insertBefore(rows[dragingIndex], rows[
0 ]);
return ;
}
else {

for ( var i = 0 ; i < len; i ++ ) {
halfDistant
= rows[i].posTop + (rows[i].clientHeight / 2 );
if (top >= rows[i].posTop && top < halfDistant) {
parent.insertBefore(rows[dragingIndex], rows[i]);
return ;
}
else if (top >= halfDistant && top < rows[i].posTop + rows[i].clientHeight) {
i
+ 1 < len ? parent.insertBefore(rows[dragingIndex], rows[i + 1 ]) : parent.appendChild(rows[dragingIndex]);
return ;
}
}
parent.appendChild(rows[dragingIndex]);
}

};

/* 删除拖动幻影列 */
var removeShadowArray = function () {

document.body.removeChild(dragTable);

};

/* 鼠标按下事件处理程序 */
var downHandler = function (rows, dragingClass, oTable, onBeginDrag) {

return function (event) {
var _elemPosition;
var event = util.getEventObj(event);
var target = util.getEventTarget(event);
var selectedTr;

if (target.tagName !== ' TH ' && target.tagName !== ' TD ' && target.parentNode.tagName !== ' TH ' ) {
return ;
}
else {
onBeginDrag();
// 执行开始拖动的回调函数

if (target.tagName == ' TH ' ) { // 判断点击是否为th(列拖动)
dragingIndex = target.index;
_elemPosition
= util.getCurrentPosition(target);
relPos.updatePos(event.clientX
- _elemPosition.left, event.clientY - _elemPosition.top);
copyCol(dragingIndex, rows, dragingClass, oTable);
isDragCol
= true ;
}

else if (target.tagName == ' TD ' ) { // 判断点击是否为td(行拖动)

selectedTr
= target.parentNode;
dragingIndex
= selectedTr.index;
_elemPosition
= util.getCurrentPosition(selectedTr);
relPos.updatePos(event.clientX
- _elemPosition.left, event.clientY - _elemPosition.top);
copyRow(selectedTr, dragingClass, oTable);
isDragsRow
= true ;
}

updateDragingPosition(event.clientX
- relPos.left, event.clientY - relPos.top); // 更新拖动幻影位置
}

}

};


/* 鼠标拖动事件处理程序 */
var moveHandler = function (oTable, onDraging) {
return function (event) {

var event = util.getEventObj(event);
var target = util.getEventTarget(event);

if (isDragCol || isDragsRow) {
onDraging();
updateDragingPosition(event.clientX
- relPos.left, event.clientY - relPos.top, oTable);

}

}

};



/* 鼠标松开事件处理程序 */
var upHandler = function (headers, rows, tBodyRows, onEndDrag) {
return function (event) {

var event = util.getEventObj(event);
var target = util.getEventTarget(event);
if ( ! isDragCol && ! isDragsRow) {
return ;
}
else {
if (isDragCol) {

placeCol(event.clientX
- relPos.left, headers, rows);
isDragCol
= false ;
setIndexAndPos(headers,
' left ' );

}
else if (isDragsRow) {
placeRow(event.clientY
- relPos.top, tBodyRows);
isDragsRow
= false ;
setIndexAndPos(tBodyRows,
' top ' );

}
removeShadowArray();
onEndDrag();

}

}

}
/* 为事件添加处理程序 */
var addDragHandler = function (rows, dragingClass, oTable, headers, tBodyRows, onBeginDrag, onEndDrag, onDraging) {
util.addEventHandler(document,
' mousedown ' , downHandler(rows, dragingClass, oTable, onBeginDrag));
util.addEventHandler(document,
' mousemove ' , moveHandler(oTable, onDraging));
util.addEventHandler(document,
' mouseup ' , upHandler(headers, rows, tBodyRows, onEndDrag));

};


return {
/* 初始化 */
init:
function (sTableId, options) {


var defaults = {
dragingClass:
' draging ' ,
onBeginDrag: util.emptyFunction,
onEndDrag: util.emptyFunction,
onDraging: util.emptyFunction


};
util.extend(defaults, options);

this .oTable = util.$(sTableId);
this .rows = this .oTable.rows;
this .tHead = this .oTable.tHead;
this .tBody = this .oTable.tBodies[ 0 ];
this .headers = this .tHead.rows[ 0 ].cells;
this .dragingClass = defaults.dragingClass;
this .onBeginDrag = util.bindFunction(defaults.onBeginDrag);
this .onEndDrag = util.bindFunction(defaults.onEndDrag);
this .onDraging = util.bindFunction(defaults.onDraging);

setIndexAndPos(
this .headers, ' left ' );
setIndexAndPos(
this .tBody.rows, ' top ' );

addDragHandler(
this .rows, this .dragingClass, this .oTable, this .headers, this .tBody.rows, this .onBeginDrag, this .onEndDrag, this .onDraging);



}


}

})();


欢迎转载,请标明出处:http://www.cnblogs.com/Cson/archive/2011/07/08/2101317.html

你可能感兴趣的:(【CSON原创】 支持行拖动,列拖动的表格插件)