很久以来都在做一些没有营养的东西,即使工作之中有时会有一些比较有技巧性的东西也是基于一些不是非常用的第三方组件的,所以也就没有记下来。前一阵做了个编辑表格列头行头定义的功能,写了一大堆javascript,回头来看看还算有点用途,保不准以后会用到,这二天有空把它整理一下。
先说明一下,下面的代码都是基于Jquery 1.3.2的。
因为要动态生成表格的行头列头,所以把一些定义放到数据库中,这些定义包括标题、层级关系、行列融合、序号、以及其他一些跟计算有关的属性定义等。现在就是做一个能编辑这个定义的简单界面,在这篇文章里说说它们之间的顺序调整,即表格行列的移动。
如果是没有任何行列融合的话,移动是非常容易的,但一旦有了行列融合存在,移动就得考虑很多情况了,同一行的元素还好说,如何找同一列的元素是个问题,所以我最终是对行列头每个单元格都定义了相应的class来标识它们是否是属于同一列的,同一列的class是相同的。
还有这里假定不会出现奇异表格。即列头从上往下,列融合的数目只会越来越小,最后一个td只占一列;同理行头从左往右,行融合的数目也只会越来越小,最后一个td也只占一行。当移动的时候,如果是碰上有行列融合的,最终移动的时候是把整个融合在一起那些行列移动到新位置的。最后在行头定义的最后一列,列头定义的最后一行,我加入一列(或行)来放置操作符,这也是为了明确当前操作的是哪一行、哪一列,因为这些单元格是铁定只占一行一列的。
a.列的移动
主要是讲列的左移,因为右移是可以化为左移来处理的。
var $column = $(sender).parent();
var className = $column.attr( " class " );
var preClassName = $column.prev().attr( " class " );
if ( ! preClassName)
return ;
var $row = $column.parent();
var $table = $row.parent();
var ri = 0 ;
var $sCol, $pCol;
那个return,是为了当前列是第一列的情况下直接返回用。
基于不会出现奇异表格的事实,从上往下找当前列的第一个单元格
$sCol = $table.find("td." + className).eq(0);
记下第一个单元格出行时的行索引
var mcRowIndex = $table.children("tr").index($sCol.parent());
记下这个索引以后要用。
$pCol = $sCol.prev();
preClassName = $pCol.attr( " class " );
var $preOpColumn = $row.children( " td. " + preClassName);
var preEndIndex = $row.children( " td " ).index($preOpColumn); // 移动列时,最终的位置,用操作符所在单元格的列索引来判定最终移动列的位置
var moveCount = 1 ; // 要移动的列数
moveCount = $sCol.attr( " colspan " );
if ( ! moveCount)
moveCount = 1 ;
位置的取得主要就是利用同列的class相同,以及操作符单元格只占一列这二个特性
下面是多列移动的骨架,单列移动下面单独列出
var moveIndex = 0 ;
var clsName;
var $mvColumn = $column;
while (moveIndex < moveCount) {
clsName = $mvColumn.attr( " class " );
$mvColumn = $mvColumn.next();
var $preColumn, $iterOpCol, opIndex;
$( " td. " + clsName).each( function () {
// ......单列的移动......
});
moveIndex ++ ;
preEndIndex ++ ;
}
单独一列的移动,即在那个$("td." + clsName).each()里面的遍历函数
$preColumn = $( this ).prev();
$iterOpCol = $row.children( " td. " + $preColumn.attr( " class " )); // 前一单元格所在列的操作符单元格
if ($iterOpCol) {
opIndex = $row.children( " td " ).index($iterOpCol);
while (opIndex > preEndIndex) { // 找移动目的地的单元格
$preColumn = $preColumn.prev();
$iterOpCol = $row.children( " td. " + $preColumn.attr( " class " )); // 前一单元格所在列的操作符单元格
if ( ! $iterOpCol)
break ;
opIndex = $row.children( " td " ).index($iterOpCol);
}
if (opIndex >= preEndIndex)
$( this ).insertBefore($preColumn);
else
$( this ).insertAfter($preColumn);
}
用一个while (opIndex > preEndIndex)循环找目的地,那是因为表格融合的关系,这一行不一定会存在class名为preClassName的单元格。最终找到的是“它”之前或之后的单元格。
从表象上讲,移动的代码到这里就结束了,但后续操作的关系,还有一个东西要处理,即那些未参与移动的单元格的class的变动。比如:
< tr >
< td colspan ="2" class ="1" />
</ tr >
< tr >
< td class ="1" />
< td class ="2" />
</ tr >
当第2列左移时,第一行那个单元格并没有移动过,最张要把它的class从“1”改为“2”.
这时前面那个mcRowIndex就用得上了。
$table.children("tr:lt(" + mcRowIndex + ")").children("td." + preClassName).each(function() {
$(this).attr("class", className);
});
在mcRowIndex这一行之前的其实都是没有参与移动的。
b.行的移动
行的移动处理方式其实跟列的移动差不多,唯一要说明的是,操作符所在那一列是专门定义class的。移动行比移动列简单,一是不用处理class的变换问题,二是交换二行比交换二列要容易。
这里就不作一一解释了。
var $row = $(sender).parent().parent();
var $preRow = $row.prev();
var $opCol = $row.children( " td.op:first " );
var $preopCol = $preRow.children( " td.op:first " );
if ($preopCol.size() == 0 )
return ;
var $rowFCell = $row.children( " td:first " );
var clsName = $rowFCell.attr( " class " );
var moveCount = $rowFCell.attr( " rowspan " );
if ( ! moveCount)
moveCount = 1 ;
while ($preRow.children( " td. " + clsName).size() == 0 )
$preRow = $preRow.prev();
var stIndex = $preRow.children( " td " ).index($preRow.children( " td. " + clsName));
var moveIndex = 0 ;
var $moveRow = $row;
var $nRow;
while (moveIndex < moveCount) {
$nRow = $moveRow.next();
$moveRow.insertBefore($preRow);
$moveRow = $nRow;
moveIndex ++ ;
}
$preRow.children( " td:lt( " + stIndex + " ) " ).each( function () {
$( this ).insertBefore($rowFCell);
});
下移一行也可以化为上移一行处理的,这里略。