如何在可滚动的div中冻结html表的第一列和最后一列?

时间:2009-04-13 11:44:55

标签: javascript html layout html-table

我有一个表格,它有一个标题行,但也有一个标题列和一个总列,其中有几列。

这样的事情:

Name    Score 1    Score 2  ...  Total
--------------------------------------
John       5          6            86
Will       3          7            82
Nick       7          1            74

整个表是在固定宽度的可滚动div中定义的,因为可能存在大量“Score”行,并且我有一个固定宽度的页面布局。

<div id="tableWrapper" style="overflow-x: auto; width: 500px;">
    <table id="scoreTable">
        ...
    </table>
</div>

我希望第一个(Name)和最后一个(Total)列在内部列滚动时保持可见。

任何人都可以帮我吗?

编辑:我的意思是仅水平滚动 - 更改为指定。


更新:我已经为自己解决了这个问题,并在下面发布了答案。如果您需要更多信息,请告诉我 - 这有点痛苦,我讨厌其他人不得不重写所有内容。

7 个答案:

答案 0 :(得分:5)

我可以提出一个有点非正统的解决方案吗?

您如何考虑在“名称”列之后放置“总计”列,而不是在最后?这不会避免只需要滚动表的一部分吗?

这并不是你所要求的,但也许这是一个充分的解决方案,因为替代方案会非常混乱。 (例如,在表格之外放置'total'和'name'列会在不是所有行都具有相同高度时产生对齐问题。你可以用javascript来纠正这个问题,但是你会进入一个全新的世界疼痛)。

同样从UI的角度来看,'name'和'total'可能是最重要的数据,在这种情况下将它们组合在一起是有意义的,然后是总计的“细分”。当然,我们似乎有一种直觉,即'总数'应该在它的组成部分之后,但我不认为如果订单被这样颠倒,它会给用户带来太多的混淆(尽管这是一个问题)您,基于您的产品和用户)。

无论如何,需要考虑的事情。

编辑:

以下是一些更为非正统的解决方案,现在我认为我的意图更加明白了:

  1. 分数得分。给出最近的十个,比如说和总数,和 一个提供10个旧分数的链接
  2. 仅提供姓名,总数和其他一些有意义的措施 比如mean和sd,然后提供 显示每个名称的链接 所有结果都与之相对应 名称。然后你也可以提供 显示a的所有结果的链接 给出分数设置,以便不同用户之间进行比较 可以制作。关键在于你 只需给出1维 每个视图的数据,而不是 拥有笨重的2D数据集
  3. 使行可排序(使用jQuery UI很容易),以便我可以 比较玛丽和简,我可以拖 一个接一个地放,所以我不会 需要继续向左滚动 有权查看哪些分数对应 名称
  4. 通过更改背景,在单击时突出显示一行 颜色或类似,再次,所以我不需要继续左右滚动。
  5. 无论如何,你明白了。也许最好是寻找UI解决方案而不是扭曲的标记解决方案。最后,我会质疑一次向用户提供如此多的数据是多么重要,它的一部分需要以特定方式滚动才能使数据可读。也许您正在构建电子表格应用程序,并且您确实需要在单个视图中显示100x100矩阵。如果没有,你肯定会提出比我分割结果更有创意的方法。

答案 1 :(得分:3)

看看这个jQuery插件:

http://fixedheadertable.com/

答案 2 :(得分:2)

我假设您只想水平滚动它。否则,它可能会与静态列混淆。

你可以将overflow:auto放到内部表格单元格的包含元素上......我不确定浏览器会如何处理这个问题,但是你可以将你想要修复的元素放在thead和tfoot中元素,并将滚动部分放在tbody元素中,并将其溢出设置为auto。

如果不这样做,您可能需要删除语义并在表格外部编写左列和右列。

就个人而言,我尝试将其编码为尽可能语义,然后使用JavaScript来定位左右列。如果没有JavaScript,你应该让它优雅地失败到一个宽桌子(不知道这有多难,就像你说你有一个固定的宽度)

您可以尝试这个(粗略的)jQuery示例将第一列的值放在外面。

var nameTable = '<table id="outside-name-col">
                  <thead>
                   <tr>
                    <th>Name</th>
                   </tr>
                  </thead>
                 <tbody>'; // start making a table for name column only

$('#scoreTable tbody tr').each(function() { // iterate through existing table

   var nameCol = $(this).find(':first'); // get column of index 0, i.e. first

   var cellHeight = nameCol.height(); // get the height of this cell

   $(this).find('td').height(cellHeight); // equalise the height across the row so removing this element will not collapse the height if it is taller than the scores and total       

   nameTable += '<tr><td style="height: ' + cellHeight + 'px">' + nameCol.html() + '</td></tr>'; // append the next row with new height and data

   nameCol.remove(); // remove this cell from the table now it's been placed outside

});

nameTable += '</tbody></table>'; // finish table string


$('#scoreTable').before(nameTable); // insert just before the score table

使用CSS定位它们以正确对齐。这是未经测试的,但它应该给你一些想法。

答案 3 :(得分:2)

现在还没有完成(我只是非常快速地嘲笑它),但这是使用直接HTML的方法。你想要三个表......两个在&lt; div&gt;之外和一个在&lt; div&gt;内。所有三个都浮在左边,所以它们都在同一条线上。

Table Test

和代码本身:

  <table style="float: left;">
     <tr>
        <td>Name</td>
     </tr>
     <tr>
        <td>John</td>
     </tr>
     <tr>
        <td>Will</td>
     </tr>
  </table>
  <div id="tableWrapper" style="overflow-x: auto; width: 500px;float:left;padding-bottom: 10px;">
      <table>
        <tr>
           <td>Score1</td>
           <td>Score2</td>
           ...
        </tr>
        <tr>
           <td>5</td>
           <td>6</td>
           ...
        </tr>
        <tr>
           <td>3</td>
           <td>7</td>
           ...
        </tr>
        <tr>
           <td>7</td>
           <td>1</td>
           ...
        </tr>
      </table>
  </div>
  <table style="float: left;">
     <tr>
        <td>Total</td>
     </tr>
     <tr>
        <td>86</td>
     </tr>
     <tr>
        <td>82</td>
     </tr>
  </table>

注意:div上的填充底部是这样的,滚动条不会掩盖IE中的表格。此外,我必须解决的错误是如何指定中间表内的标题元素的宽度。出于某种原因,指定width =“”属性不起作用。因此,如果标题元素的文本太宽(并且打破另一行),则布局被打破(关闭一行)

答案 4 :(得分:2)

我已经尝试了一些方法(感谢所有帮助过的人),这就是我使用jQuery提出的方法。它似乎在我测试的所有浏览器中都运行良好。随意拿它并随意使用它。对我来说,下一步将把它变成一个可重用的jQuery插件。

要点:

我开始使用包含所有内容的普通表( Id =“ladderTable”),然后我写了三种方法 - 一种用于剥离第一列,一种用于删除第一列剥离最后一列,另一列修正行高。

stripFirstColumn 方法创建一个新表( Id =“nameTable”),遍历原始表并取出第一列,并将这些单元格添加到nameTable中

stripLastColumn 方法基本上做同样的事情,除了它取出最后一列并将单元格添加到名为 totalTable 的新表中。

fixHeights 方法查看每个表中的每一行,计算最大高度,并将其应用于相关表。

在文档就绪事件中,我按顺序调用了所有三种方法。请注意,所有三个表都向左浮动,因此它们只是水平堆叠。

HTML结构:

<h1>Current Ladder</h1> 
<div id="nameTableSpan" style="float:left;width:100px;border-right:2px solid gray;"></div> 
<div id="ladderDiv" style="float:left;width:423px;overflow:auto;border:1px solid gray;margin-top:-1px;"> 
    <table id="ladderTable" class="ladderTable">
        <thead> 
            <tr><td>Name</td><td>Round 1</td> ... <td>Round 50</td><td class="scoreTotal">Total</td></tr>
        </thead>
        <tr><td>Bob</td><td>11</td> ... <td>75</td><td>421</td></tr>
           ... (more scores)
    </table>
</div>
<div id="totalTableSpan" style="float:left;width:70px;border-left:2px solid gray;"></div> 

jQuery:

function stripFirstColumn() {                
    // pull out first column:
    var nt = $('<table id="nameTable" cellpadding="3" cellspacing="0" style="width:100px;"></table>');
    $('#ladderTable tr').each(function(i)
    {
        nt.append('<tr><td style="color:'+$(this).children('td:first').css('color')+'">'+$(this).children('td:first').html()+'</td></tr>');
    });
    nt.appendTo('#nameTableSpan');
    // remove original first column
    $('#ladderTable tr').each(function(i)
    {
        $(this).children('td:first').remove();
    });
    $('#nameTable td:first').css('background-color','#8DB4B7');
}

function stripLastColumn() {                
    // pull out last column:
    var nt = $('<table id="totalTable" cellpadding="3" cellspacing="0" style="width:70px;"></table>');
    $('#ladderTable tr').each(function(i)
    {
        nt.append('<tr><td style="color:'+$(this).children('td:last').css('color')+'">'+$(this).children('td:last').html()+'</td></tr>');
    });
    nt.appendTo('#totalTableSpan');
    // remove original last column
    $('#ladderTable tr').each(function(i)
    {
        $(this).children('td:last').remove();
    });
    $('#totalTable td:first').css('background-color','#8DB4B7');
}

function fixHeights() {
    // change heights:
    var curRow = 1;
    $('#ladderTable tr').each(function(i){
        // get heights
        var c1 = $('#nameTable tr:nth-child('+curRow+')').height();    // column 1
        var c2 = $(this).height();    // column 2
        var c3 = $('#totalTable tr:nth-child('+curRow+')').height();    // column 3
        var maxHeight = Math.max(c1, Math.max(c2, c3));

        //$('#log').append('Row '+curRow+' c1=' + c1 +' c2=' + c2 +' c3=' + c3 +'  max height = '+maxHeight+'<br/>');

        // set heights
        //$('#nameTable tr:nth-child('+curRow+')').height(maxHeight);
        $('#nameTable tr:nth-child('+curRow+') td:first').height(maxHeight);
        //$('#log').append('NameTable: '+$('#nameTable tr:nth-child('+curRow+')').height()+'<br/>');
        //$(this).height(maxHeight);
        $(this).children('td:first').height(maxHeight);
        //$('#log').append('MainTable: '+$(this).height()+'<br/>');
        //$('#totalTable tr:nth-child('+curRow+')').height(maxHeight);
        $('#totalTable tr:nth-child('+curRow+') td:first').height(maxHeight);
        //$('#log').append('TotalTable: '+$('#totalTable tr:nth-child('+curRow+')').height()+'<br/>');

        curRow++;
    });

    if ($.browser.msie)
        $('#ladderDiv').height($('#ladderDiv').height()+18);
}

$(document).ready(function() {
    stripFirstColumn();
    stripLastColumn();
    fixHeights();
    $("#ladderDiv").attr('scrollLeft', $("#ladderDiv").attr('scrollWidth'));    // scroll to the last round
});

如果您有任何疑问,或者有任何不清楚的地方,我非常乐意提供帮助。

我花了很长时间才发现没有什么东西可以真正重复使用,而且写这个花了更长的时间。我讨厌别人也会遇到同样的麻烦。

答案 5 :(得分:1)

你的意思是水平滚动吗?

如果你想实现水平滚动,那么你可以使用3个容器。

  1. 第一栏(姓名)
  2. 对于要滚动的列。将此容器的overflow-x样式设置为auto
  3. 最后一栏(总计)

答案 6 :(得分:0)

滚动表的内容是一个有问题的问题,因为没有简单且浏览器安全的解决方案来实现这一目标。

最好和最安全的方法需要JavaScript。您可以使用我使用的这种技术轻松实现水平滚动(免费提供)。您可以轻松地将其转换为您的问题:

1)我制作了3张桌子

2)放置将数据保存在DIV容器中的中间表

3)将DIV样式设置为overflow:auto

4)给出页眉和页脚表格以及每个宽度为100%的div

5)用另一个div包围所有内容来控制整个表格宽度

6)确保所有三个表中的列数匹配,并且所有三个表都有关于填充,边距和边框的相同样式

现在有趣的部分:

6)在该表构造的最后一个元素下面,写一个javascript块(或从文件中包含它)

7)编写一个连续查看每一列的函数(使用for循环),从第一列开始。在该循环的每次迭代中,检查当前列中前两个表的td单元格。检查哪个是较大的并将其分配给较小的td单元格样式。您也可以将该值应用于页脚表。

8)调用该函数。您可能希望在间隔或特殊事件发生时执行此操作,例如单元格中的新数据(例如,如果使用ajax)。

注意:如果您进行水平滚动,则会同步列大小。但是,您必须将页眉和页脚表的滚动位置与内容表同步。或者你只需​​让整个div水平滚动整个东西,但不是垂直滚动。这将是包含数据表的DIV的工作。

这项技术在非常复杂的甘特图项目中的所有主要浏览器中都运行良好。

注意:您可以轻松地以90度旋转方式考虑该概念,以便在滚动时修复第一个和最后一个标题列。

编辑:这是我的代码库中的另一个示例,它可以帮助您获得解决方案:

var left_td = document.getElementById("left_td");
var right_td = document.getElementById("right_td");
var contentDiv = document.getElementById("contentDiv");
window.setInterval(function(){
    var left_td_width = Math.round((parseInt(contentDIV.offsetWidth) - 120) / 2);
    var right_td_width = (parseInt(contentDIV.offsetWidth) - left_td_width - 120;

    left_td.style.width = left_td_width + "px";
    right_td.style.width = right_td_width + "px";
}, 25);

此代码执行以下操作:确保具有3列的表始终如下:第一列和最后一列具有相同的大小,中间列的宽度为120像素。 当然这并不直接适用于您的特殊问题,但它可能会为您提供通过JavaScript进行动态表操作的起点。