首页 > 代码库 > Javascript横向/纵向合并单元格TD

Javascript横向/纵向合并单元格TD

在报表系统中,涉及“HTML的TD单元格的合并”恐怕为数不少。

比如,从DB查得数据并经过后台的整理后,可能是这样的:

Table1

   JOBTOTAL SALINDEXEMPNOENAMEJOBMGRHIREDATESALCOMMDEPTNO
1ANALYST600017788SCOTTANALYST75664/19/19873000.00 20
1ANALYST600027902FORDANALYST756612/3/19813000.00 20
2CLERK415037934MILLERCLERK77821/23/19821300.00 10
2CLERK415047900JAMESCLERK769812/3/1981950.00 30
2CLERK415057369SMITHCLERK790212/17/1980800.00 20
2CLERK415067876ADAMSCLERK77885/23/19871100.00 20
3MANAGER827577698BLAKEMANAGER78395/1/19812850.00 30
3MANAGER827587566JONESMANAGER78394/2/19812975.00 20
3MANAGER827597782CLARKMANAGER78396/9/19812450.00 10
4PRESIDENT5000107839KINGPRESIDENT 11/17/19815000.00 10
5SALESMAN5600117844TURNERSALESMAN76989/8/19811500.000.0030
5SALESMAN5600127654MARTINSALESMAN76989/28/19811250.001400.0030
5SALESMAN5600137521WARDSALESMAN76982/22/19811250.00500.0030
5SALESMAN5600147499ALLENSALESMAN76982/20/19811600.00300.0030

 

为了让用户更清晰地查看报表,结果可能需要是这样的(各职位的薪水总额及分布明细):

Table2

   JOBTOTAL SALINDEXEMPNOENAMEJOBMGRHIREDATESALCOMMDEPTNO
1ANALYST600017788SCOTTANALYST75664/19/19873000.00 20
27902FORDANALYST756612/3/19813000.00 20
2CLERK415037934MILLERCLERK77821/23/19821300.00 10
47900JAMESCLERK769812/3/1981950.00 30
57369SMITHCLERK790212/17/1980800.00 20
67876ADAMSCLERK77885/23/19871100.00 20
3MANAGER827577698BLAKEMANAGER78395/1/19812850.00 30
87566JONESMANAGER78394/2/19812975.00 20
97782CLARKMANAGER78396/9/19812450.00 10
4PRESIDENT5000107839KINGPRESIDENT 11/17/19815000.00 10
5SALESMAN5600117844TURNERSALESMAN76989/8/19811500.000.0030
127654MARTINSALESMAN76989/28/19811250.001400.0030
137521WARDSALESMAN76982/22/19811250.00500.0030
147499ALLENSALESMAN76982/20/19811600.00300.0030

 

 这就需要我们对单元格进行相应的合并。

一般来说,有两种处理方法,

  1. 在编写HTML时已设置单元格的合并;
  2. 在编写HTML时未作合并处理,后期由Javascript完成单元格合并工作。

本文讲的是第2种情况。

 

合并单元格的步骤

比如,我要把上表Table1中数值为ANALYST的两个单元格合并。

JOB
ANALYST
ANALYST
  • 我们首先需要将第二个ANALYST的单元格删除掉
  • 然后设置第一个ANALYST的单元格的rowSpan属性为2(表示“跨越2行”)

 

写得不好的公用方法

之前写了一个公用Javascript方法,用以合并相邻间文本相同的单元格。

这个方法写得不好,有明显的Bug。对已做过合并的Table,这个方法有可能导致合并单元格错误。

主要的原因在于,对于合并过单元格的表格,那么被合并的行就会相应的少一个TD,而此方法依旧按未合并过单元格的情况来获取TD对象。

比如,获取第二列的所有单元格,此程序用“获取全部TR中的第二个TD”来实现,这在未合并过单元格的Table是正确的,但对于合并过单元格的Table,则未必,因为被合并的行相应地少一个单元格。

此处将代码贴出,希望大家引以为鉴

function mergeCell(tableObj, col) {        var $tab = $(tableObj);        var $trs = $tab.find("tr");                var oldval;        var firstTD;        var counter = 0;        $trs.each(function(index) {                        if (!oldval && !firstTD) {                oldval = $(this).find("td:eq(" + col + ")").text();                firstTD = $(this).find("td:eq(" + col + ")").get(0);                counter = 0;                counter++;            } else {                if ($(this).find("td:eq(" + col + ")").text() == oldval) {                    $(this).find("td:eq(" + col + ")").remove();                    counter++;                } else {                    $(firstTD).attr("rowSpan", counter);                    oldval = $(this).find("td:eq(" + col + ")").text();                    firstTD = $(this).find("td:eq(" + col + ")").get(0);                    counter = 0;                    counter++;                }            }                        if (index >= $trs.length - 1) {                $(firstTD).attr("rowSpan", counter);            }                    });    }
mergeCell

 

 页面DEMO如下(HTML代码,较长):

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><script type="text/javascript" src="../js/jquery.js"></script><script type="text/javascript" src="../js/tdmerger.js"></script><script type="text/javascript">    $().ready(function() {        mergeCell($("#list_table").get(0), 1);        mergeCell($("#list_table").get(0), 2);            });        function mergeCell(tableObj, col) {        var $tab = $(tableObj);        var $trs = $tab.find("tr");                var oldval;        var firstTD;        var counter = 0;        $trs.each(function(index) {                        if (!oldval && !firstTD) {                oldval = $(this).find("td:eq(" + col + ")").text();                firstTD = $(this).find("td:eq(" + col + ")").get(0);                counter = 0;                counter++;            } else {                if ($(this).find("td:eq(" + col + ")").text() == oldval) {                    $(this).find("td:eq(" + col + ")").remove();                    counter++;                } else {                    $(firstTD).attr("rowSpan", counter);                    oldval = $(this).find("td:eq(" + col + ")").text();                    firstTD = $(this).find("td:eq(" + col + ")").get(0);                    counter = 0;                    counter++;                }            }                        if (index >= $trs.length - 1) {                $(firstTD).attr("rowSpan", counter);            }                    });    }</script><body>    <TABLE id="list_table" BORDER="1">        <TR>            <TH>&nbsp;&nbsp;&nbsp;</TH>            <TH>JOB</TH>            <TH>TOTAL SAL</TH>            <TH>INDEX</TH>            <TH>EMPNO</TH>            <TH>ENAME</TH>            <TH>JOB</TH>            <TH>MGR</TH>            <TH>HIREDATE</TH>            <TH>SAL</TH>            <TH>COMM</TH>            <TH>DEPTNO</TH>        </TR>        <TR>            <TD>1</TD>            <TD>ANALYST</TD>            <TD>6000</TD>            <TD>1</TD>            <TD>7788</TD>            <TD>SCOTT</TD>            <TD>ANALYST</TD>            <TD>7566</TD>            <TD>4/19/1987</TD>            <TD>3000.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>1</TD>            <TD>ANALYST</TD>            <TD>6000</TD>            <TD>2</TD>            <TD>7902</TD>            <TD>FORD</TD>            <TD>ANALYST</TD>            <TD>7566</TD>            <TD>12/3/1981</TD>            <TD>3000.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>3</TD>            <TD>7934</TD>            <TD>MILLER</TD>            <TD>CLERK</TD>            <TD>7782</TD>            <TD>1/23/1982</TD>            <TD>1300.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>4</TD>            <TD>7900</TD>            <TD>JAMES</TD>            <TD>CLERK</TD>            <TD>7698</TD>            <TD>12/3/1981</TD>            <TD>950.00</TD>            <TD>&nbsp;</TD>            <TD>30</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>5</TD>            <TD>7369</TD>            <TD>SMITH</TD>            <TD>CLERK</TD>            <TD>7902</TD>            <TD>12/17/1980</TD>            <TD>800.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>6</TD>            <TD>7876</TD>            <TD>ADAMS</TD>            <TD>CLERK</TD>            <TD>7788</TD>            <TD>5/23/1987</TD>            <TD>1100.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>7</TD>            <TD>7698</TD>            <TD>BLAKE</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>5/1/1981</TD>            <TD>2850.00</TD>            <TD>&nbsp;</TD>            <TD>30</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>8</TD>            <TD>7566</TD>            <TD>JONES</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>4/2/1981</TD>            <TD>2975.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>9</TD>            <TD>7782</TD>            <TD>CLARK</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>6/9/1981</TD>            <TD>2450.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>4</TD>            <TD>PRESIDENT</TD>            <TD>5000</TD>            <TD>10</TD>            <TD>7839</TD>            <TD>KING</TD>            <TD>PRESIDENT</TD>            <TD>&nbsp;</TD>            <TD>11/17/1981</TD>            <TD>5000.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>4</TD>            <TD>SUPER PRESIDENT</TD>            <TD>5000</TD>            <TD>10</TD>            <TD>7839</TD>            <TD>KING</TD>            <TD>PRESIDENT</TD>            <TD>&nbsp;</TD>            <TD>11/17/1981</TD>            <TD>5000.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>11</TD>            <TD>7844</TD>            <TD>TURNER</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>9/8/1981</TD>            <TD>1500.00</TD>            <TD>0.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>12</TD>            <TD>7654</TD>            <TD>MARTIN</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>9/28/1981</TD>            <TD>1250.00</TD>            <TD>1400.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>13</TD>            <TD>7521</TD>            <TD>WARD</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>2/22/1981</TD>            <TD>1250.00</TD>            <TD>500.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>14</TD>            <TD>7499</TD>            <TD>ALLEN</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>2/20/1981</TD>            <TD>1600.00</TD>            <TD>300.00</TD>            <TD>30</TD>        </TR>    </TABLE></body></html>
WrongMergeTD.html

 

 卷土重来

于是,重新写了两个公用方法来合并单元格。

一个是合并相邻值相同的单元格;

另外一个是合并指定序号的单元格,比如2-5,6-8,是“合并第2个至第5个单元格、第6个和至第8个单元格”。

这俩方法将“获取需要合并的TD元素”的动作交由方法调用者,只负责对入参单元格作对应的合并。

JS

/** * Meger cells with same text * @param $tds TDs jquery object * @param type row/col */function mergeCell4SameText($tds, type) {        var oldval;    var firstTD;    var counter = 0;    $tds.each(function(index) {                if (index == 0) {            oldval = $(this).text();            firstTD = $(this).get(0);            counter = 0;            counter++;        } else {            if ($(this).text() == oldval) {                $(this).remove();                counter++;            } else {                if (type == ‘col‘) {                    $(firstTD).attr("rowSpan", counter);                } else if (type == ‘row‘) {                    $(firstTD).attr("colSpan", counter);                }                                oldval = $(this).text();                firstTD = $(this).get(0);                counter = 0;                counter++;            }        }                if (index >= $tds.length - 1) {                        if (type == ‘col‘) {                $(firstTD).attr("rowSpan", counter);            } else if (type == ‘row‘) {                $(firstTD).attr("colSpan", counter);            }                    }            });}/** * Meger cells by the parameters "index" * @param $tds TDs jquery object * @param type row/col * @param index for example, 0-1,2-5,6-8,10-13 */function mergeCellByIndex($tds, type, index) {        var indexArrs = index.split(‘,‘);        var fromTo;    var from;    var to;        for (var i in indexArrs) {        fromTo = indexArrs[i];                from = new Number(fromTo.split(‘-‘)[0]);        to = new Number(fromTo.split(‘-‘)[1]);                for (var j = 1 + from; j <= to; j++) {            $($tds.get(j)).remove();        }                if (type == ‘col‘) {            $($tds.get(from)).attr("rowSpan", to - from + 1);        } else if (type == ‘row‘) {            $($tds.get(from)).attr("colSpan", to - from + 1);        }            }    }
tdmerger

 

调用的页面(HTML代码,较长):

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Insert title here</title></head><script type="text/javascript" src="../js/jquery.js"></script><script type="text/javascript" src="../js/tdmerger.js"></script><script type="text/javascript">    $().ready(function() {        $col1tds = $("table tr td:nth-child(1)");        $col2tds = $("table tr td:nth-child(2)");        $col3tds = $("table tr td:nth-child(3)");                mergeCellByIndex($col1tds, col, 0-1,2-5,6-8,10-13);        mergeCellByIndex($col2tds, col, 0-1,2-5,6-8,10-13);        mergeCellByIndex($col3tds, col, 0-1,2-5,6-8,10-13);                /*        $row2tds = $("table tr:eq(2) td");        mergeCellByIndex($row2tds, ‘row‘, ‘6-11‘);        */    });</script><body>    <TABLE BORDER="1">        <TR>            <TH>&nbsp;&nbsp;&nbsp;</TH>            <TH>JOB</TH>            <TH>TOTAL SAL</TH>            <TH>INDEX</TH>            <TH>EMPNO</TH>            <TH>ENAME</TH>            <TH>JOB</TH>            <TH>MGR</TH>            <TH>HIREDATE</TH>            <TH>SAL</TH>            <TH>COMM</TH>            <TH>DEPTNO</TH>        </TR>        <TR>            <TD>1</TD>            <TD>ANALYST</TD>            <TD>6000</TD>            <TD>1</TD>            <TD>7788</TD>            <TD>SCOTT</TD>            <TD>ANALYST</TD>            <TD>7566</TD>            <TD>4/19/1987</TD>            <TD>3000.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>1</TD>            <TD>ANALYST</TD>            <TD>6000</TD>            <TD>2</TD>            <TD>7902</TD>            <TD>FORD</TD>            <TD>ANALYST</TD>            <TD>7566</TD>            <TD>12/3/1981</TD>            <TD>3000.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>3</TD>            <TD>7934</TD>            <TD>MILLER</TD>            <TD>CLERK</TD>            <TD>7782</TD>            <TD>1/23/1982</TD>            <TD>1300.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>4</TD>            <TD>7900</TD>            <TD>JAMES</TD>            <TD>CLERK</TD>            <TD>7698</TD>            <TD>12/3/1981</TD>            <TD>950.00</TD>            <TD>&nbsp;</TD>            <TD>30</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>5</TD>            <TD>7369</TD>            <TD>SMITH</TD>            <TD>CLERK</TD>            <TD>7902</TD>            <TD>12/17/1980</TD>            <TD>800.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>2</TD>            <TD>CLERK</TD>            <TD>4150</TD>            <TD>6</TD>            <TD>7876</TD>            <TD>ADAMS</TD>            <TD>CLERK</TD>            <TD>7788</TD>            <TD>5/23/1987</TD>            <TD>1100.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>7</TD>            <TD>7698</TD>            <TD>BLAKE</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>5/1/1981</TD>            <TD>2850.00</TD>            <TD>&nbsp;</TD>            <TD>30</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>8</TD>            <TD>7566</TD>            <TD>JONES</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>4/2/1981</TD>            <TD>2975.00</TD>            <TD>&nbsp;</TD>            <TD>20</TD>        </TR>        <TR>            <TD>3</TD>            <TD>MANAGER</TD>            <TD>8275</TD>            <TD>9</TD>            <TD>7782</TD>            <TD>CLARK</TD>            <TD>MANAGER</TD>            <TD>7839</TD>            <TD>6/9/1981</TD>            <TD>2450.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>4</TD>            <TD>PRESIDENT</TD>            <TD>5000</TD>            <TD>10</TD>            <TD>7839</TD>            <TD>KING</TD>            <TD>PRESIDENT</TD>            <TD>&nbsp;</TD>            <TD>11/17/1981</TD>            <TD>5000.00</TD>            <TD>&nbsp;</TD>            <TD>10</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>11</TD>            <TD>7844</TD>            <TD>TURNER</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>9/8/1981</TD>            <TD>1500.00</TD>            <TD>0.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>12</TD>            <TD>7654</TD>            <TD>MARTIN</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>9/28/1981</TD>            <TD>1250.00</TD>            <TD>1400.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>13</TD>            <TD>7521</TD>            <TD>WARD</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>2/22/1981</TD>            <TD>1250.00</TD>            <TD>500.00</TD>            <TD>30</TD>        </TR>        <TR>            <TD>5</TD>            <TD>SALESMAN</TD>            <TD>5600</TD>            <TD>14</TD>            <TD>7499</TD>            <TD>ALLEN</TD>            <TD>SALESMAN</TD>            <TD>7698</TD>            <TD>2/20/1981</TD>            <TD>1600.00</TD>            <TD>300.00</TD>            <TD>30</TD>        </TR>    </TABLE></body></html>
mergeTD

 

 对于合并单元格,这两种方式都不太满意,可是,也想不到更好的方法对此操作作封装。

如果童靴有更好的方法,请指点!!