表格行列的移动
时间:2010-11-09 来源:lichdr
先说明一下,下面的代码都是基于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);
});
下移一行也可以化为上移一行处理的,这里略。