数据表格表头与首列固定的完全实现

前几天项目经理跟我说有这么个需求:一个很大的表格,垂直滑动时表头固定,水平滑动里第一、二列固定。我一想这也太难实现了,直接回复说可能实现不了,可是没过会经理发个12306网站的一个现存的实现样子,汗!我只能说那我先调研下实现的成本吧。心里想想反正客户给钱,没有什么理由不做啊。

1. 前期调研

先打开12306网站上面的那个表格,打开浏览器调试工具,发现他们好像用的是插件(dhtmlxgrid),再看了下布局,发现它们把固定的表头和列放在一个DIV里面,心里想这样的需求肯定很常见,说不定网上早有相应插件了,于是上google搜“table header fixed  ”(个人比较喜欢用英文关键字搜索信息),第一条信息就是一个基于jquery的插件叫Fixed Header Table,而且还不要钱的,于是下载下来,研究它的源码,发现它的布局与12306上面的类似也是把固定的表头和列放在一个DIV,然后在此DIV里面放table,整体布局如下图所示:

table_thumb1

通过控制单元格的宽度和高度,使整体的单元格对齐,最后监听maindiv的滑动事件,当它滑动时通过js去滑动headerdiv与columndiv的滑动。其实里面最麻烦的应该是单元格对齐问题,因为单元格宽和高会随里面的内容变化(如果你想自适应的话)。

知道实现原理后,然后对照下我实际的项目,我项目中的表格和12306中的表格有的类似,不是普通的数据表格,里面的单元格的合并,而且我项目中的表格还有分组(grounp),截个图:

image_thumb1_thumb

里面有编辑功能,还得在行和列分别加个total,表格够复杂吧。Fixed Header Table这个插件不支持我的表格(单元格的合并),dhtmlxgrid插件很强大,但要钱,项目中本来就有jquery了,不想再引用其它框架了,自己动手,丰衣足食!

2. 具体实现

2.1 简化需求

以前的布局就是一个table,要做这种行列锁定的表格,布局肯定大改,为了简单点,我把我表格中的单元格宽度和高度固定,超出的文字用…表示,呵呵,我还是想怎么简单怎么来,这么做客户也接受。把单元格定死后,代码量会少很多。布局尽量用CSS去控制,JS只控制滑动和初始化区域大小(因为我页面是自适应的)。

2.2 html+CSS

先画个两行两列的表格,把整体布局定好,我做布局都先做整体,再做局部。先看下整体布局的html:

<table>
    <thead>
        <tr >
           <th></th>
           <th>
               <div id="Headerdiv" style=" overflow: hidden"></div>
           </th>  
        </tr>
    </thead>
    <tbody>
        <tr>
            <td >
                <div id="Columndiv" style=" overflow: hidden"></div>
            </td>
            <td>
                <div id="maindiv" style=" overflow: scroll" onscroll="fnScroll()"></div>
            </td>
        </tr>
    </tbody>
</table>

里面div的主要属性和事件都已经声明,接下来做thead里的两行固定列,第一个th的源代码:

<th id="firstTd " class="tdborder">
    <table cellspacing="0" cellpadding="0">
        <tr>
            <td  class="tableFirstCol  txtcenter td_right td_bottom">Project</td>
            <td class="tableSecondCol   td_bottom">
                     <table cellspacing="0" cellpadding="0" class="Tamount">
                        <tr>
                            <td colspan="3" class="txtcenter td_bottom">Total</td>
                        </tr>
                        <tr>
                            <td class="current td_right">current</td>
                            <td class="scenario td_right">scenario</td>
                            <td class="different">different</td>
                        </tr>
                    </table>
              
              </td>
        </tr>
        <tr>
            <td class="tableFirstCol td_right">Total</td>
            <td class="tableSecondCol ">
                <table cellpadding="0" cellspacing="0">
                      <tr>
                            <td class="current td_right"><input type="text" /></td>
                            <td class="scenario td_right"><input type="text" /</td>
                            <td class="different"><input type="text" /</td>
                        </tr>
                </table>
            </td>
        </tr>
    </table>
    </th>

然后是第二个th里面的Headerdiv的源代码:

 

<div id="Headerdiv" >
       <table cellspacing="0" cellpadding="0"  width="1560" id="headertable" class="td_right" >
          <tr >
            <td class="td_left">
              
              <table cellspacing="0" cellpadding="0" class="Tamount">
                        <tr>
                            <td colspan="3"  class="txtcenter td_bottom">1999</td>
                        </tr>
                        <tr>
                            <td class="current td_right">current</td>
                            <td class="scenario td_right">scenario</td>
                            <td class="different">different</td>
                        </tr>
                    </table>
            </td>
           重复TD...
          </tr>
        <tr >
            <td class="td_left td_top">
                <table cellpadding="0" cellspacing="0">
                      <tr>
                            <td class="current td_right"><input type="text" /></td>
                            <td class="scenario td_right"><input type="text" /</td>
                            <td class="different"><input type="text" /</td>
                        </tr>
                </table>

            </td>
           重复TD...
        </tr>
        </table>
       </div>  

大家可能注意到里面对套了这么多table有异议,本人是为了站单元格对齐才出此下策,在整个页面的制作过程中单元格的边框对齐是最烦人的。

接下来展示固定列的html源码:

 <div id="Columndiv"  class="fixedcol">
        <table cellspacing="0" cellpadding="0" >
            <tr>
                <td colspan="2" class="tdgroup">
                     group name group name group name group nam name group name group name
                    </td>
            </tr>
          <tr>
            <td class="tableFirstCol">Project Name 1 </td>
            <td class="tableSecondCol">
                <table cellpadding="0" cellspacing="0">
                      <tr>
                            <td class="current td_right"><input type="text" /></td>
                            <td class="scenario td_right"><input type="text" /</td>
                            <td class="different"><input type="text" /</td>
                        </tr>
                </table>
            </td>
          </tr>

里面class为tdgrounp的为表格的分组名,这分组名如果太长就会换行,我会在JS里面控制maindiv里对应单元格的高度,也是JS代码里唯一控制单元格的代码。

maindiv里的HTML就不粘出来了,里就就放了一个table。

3.3 javascript

接下来讲讲JS,JS 还是很简单的(基于jquery):

 $(document).ready(function () {
        fnAdjustTable();
        //先求页面给定的高度
        var _h = $("#tablediv").height();
        var _w = $("#tablediv").width();

        //然后设定相关div的高度
        var _head_h = $("#thead").height();
        $("#maindiv").height(_h - _head_h);
        $("#Columndiv").height(_h - _head_h - 18);//18是空出了相应滚动条的距离
        var _clo_w = $("#Columndiv").width();
        $("#Headerdiv").width(_w - _clo_w-18 );
        $("#maindiv").width(_w - _clo_w);
      
    });

    function fnAdjustTable() {
        //调整组名的单元格高度
        $('#Columndiv .tdgroup').each(function (i) {
            //不同浏览器这高度可能不一样,相关一两个像素
            if ($.browser.msie) {
                $("#maindiv .tdgroup:eq(" + i + ")").height($(this).height());
            } else {
                $("#maindiv .tdgroup:eq(" + i + ")").height($(this).height() + 1);
            }
           
        });
    }

    //滑动事件
     function fnScroll () {
        $('#Headerdiv').scrollLeft($('#maindiv').scrollLeft());
        $('#Columndiv').scrollTop($('#maindiv').scrollTop());
    }
posted @ 2012-05-10 16:21  文旺  阅读(19899)  评论(10编辑  收藏  举报