具有固定标头的仅CSS可滚动表

时间:2012-08-09 20:00:25

标签: html css internet-explorer css3

我有一个解决方案,我可以通过使用次要的jQuery和CSS创建可修改页眉/页脚的可滚动表 - 但我正在寻找一种方法,使其成为跨浏览器兼容的仅CSS解决方案。

要明确的是,我要做的是使用一个table标记(并且它是有效的子标记,colgroupcoltheadtbodytfoottrthtd),但采用一组符合以下条件的CSS规则:

  1. 必须保持页眉/页脚/内容行之间的列对齐
  2. 必须允许页眉/页脚在内容垂直滚动时保持固定
  3. 不得要求任何jQuery或其他JavaScript才能提供功能
  4. 只能使用上面提供的标签
  5. 此代码示例:http://jsfiddle.net/TroyAlford/SNKfd/显示了我当前的方法。大多数JS只是用随机值填充表,但最后一部分是驱动左/右可滚动性的原因。

    $tbody.bind('scroll', function(ev) {
        var $css = { 'left': -ev.target.scrollLeft };
        $thead.css($css);
        $tfoot.css($css);
    });
    

    注意:提供的示例在IE中无法正确呈现,并且需要jQuery来提供水平滚动。无论如何我不关心水平滚动,所以如果解决方案不这样做就没问题。

13 个答案:

答案 0 :(得分:16)

使用flexbox的解决方案尚未发布。

这是我使用display: flex的解决方案以及:after的基本用法(感谢Luggage),即使滚动条填充tbody,也能保持对齐。这已在 Chrome 45,Firefox 39和MS Edge 中得到验证。可以使用前缀属性修改它以在IE11中工作,并在IE10中使用CSS hack2012 flexbox syntax进行修改。

注意表格宽度可以修改;这甚至适用于100%宽度。

唯一需要注意的是,所有表格单元格必须具有相同的宽度。下面是一个明确设计的例子,但是当单元格内容不同时(表格单元格具有相同的宽度和自动换行,强制flexbox保持相同的宽度,无论内容如何),这都可以正常工作。 Here是单元格内容不同的示例。

只需将.scroll类应用于您想要滚动的表格,并确保它有thead

.scroll {
  border: 0;
  border-collapse: collapse;
}

.scroll tr {
  display: flex;
}

.scroll td {
  padding: 3px;
  flex: 1 auto;
  border: 1px solid #aaa;
  width: 1px;
  word-wrap: break;
}

.scroll thead tr:after {
  content: '';
  overflow-y: scroll;
  visibility: hidden;
  height: 0;
}

.scroll thead th {
  flex: 1 auto;
  display: block;
  border: 1px solid #000;
}

.scroll tbody {
  display: block;
  width: 100%;
  overflow-y: auto;
  height: 200px;
}
<table class="scroll" width="400px">
  <thead>
    <tr>
      <th>Header</th>
      <th>Header</th>
      <th>Header</th>
      <th>Header</th>
      <th>Header</th>
      <th>Header</th>
    </tr>
  </thead>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
  <tr>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
    <td>Data</td>
  </tr>
</table>

答案 1 :(得分:13)

此答案将用作不完全支持的position: sticky的占位符,并将随着时间的推移而更新。目前建议不要在生产环境中使用此方法的本地实现。

有关当前支持,请参见:https://caniuse.com/#feat=css-sticky


使用position: sticky

一个替代答案是使用position: stickyAs described by W3C

  

粘性放置的框的位置与相对放置的框的位置类似,但是偏移量是使用滚动框参照最近的祖先,如果没有祖先具有滚动框,则参照视口进行计算。

这恰好描述了相对静态标头的行为。将其分配给<thead>或第一个<tr> HTML标签很容易,因为应该支持according to W3C。但是,ChromeIE and Edge都在为这些标签分配粘性位置属性时遇到问题。目前,解决这个问题似乎还没有优先考虑。

对于表格元素似乎起作用的是将sticky属性分配给表格单元。在这种情况下,<th>个单元格。

由于表不是尊重您为其分配的静态大小的块元素,因此最好使用包装器元素来定义滚动溢出。

代码

div {
  display: inline-block;
  height: 150px;
  overflow: auto
}

table th {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}


/* == Just general styling, not relevant :) == */

table {
  border-collapse: collapse;
}

th {
  background-color: #1976D2;
  color: #fff;
}

th,
td {
  padding: 1em .5em;
}

table tr {
  color: #212121;
}

table tr:nth-child(odd) {
  background-color: #BBDEFB;
}
<div>
  <table border="0">
    <thead>
      <tr>
        <th>head1</th>
        <th>head2</th>
        <th>head3</th>
        <th>head4</th>
      </tr>
    </thead>
    <tr>
      <td>row 1, cell 1</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
    </tr>
    <tr>
      <td>row 2, cell 1</td>
      <td>row 2, cell 2</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
    </tr>
    <tr>
      <td>row 2, cell 1</td>
      <td>row 2, cell 2</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
    </tr>
    <tr>
      <td>row 2, cell 1</td>
      <td>row 2, cell 2</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
    </tr>
    <tr>
      <td>row 2, cell 1</td>
      <td>row 2, cell 2</td>
      <td>row 1, cell 2</td>
      <td>row 1, cell 2</td>
    </tr>
  </table>
</div>

在此示例中,我使用一个简单的<div>包装器来定义静态高度为150px的滚动溢出。当然可以是任何大小。现在已经定义了滚动框,粘性<th>元素将对应于“使用滚动框的最近祖先”,即div包装器。


使用position: sticky polyfill

不受支持的设备可以使用polyfill,该polyfill通过代码实现行为。 stickybits是一个示例,它与浏览器实现的position: sticky类似。

带有polyfill的示例http://jsfiddle.net/7UZA4/6957/

答案 2 :(得分:8)

据我所知,没有标准的方法来实现这一点,只有CSS,虽然我认为应该有。 Mozilla浏览器过去常常用滚动体支持固定标题,但是在过去的几年里它们已经将它删除了。

在研究了一下之后,包括找到这个帖子,一位朋友刚刚为我开发了this solution;它使用Javascript但没有预制库,HTML标记的唯一要求是该表具有id名称。然后,在window.onload中,为每个表调用一个Javascript函数,给出id,height和width。如果在浏览器中禁用了Javascript,则会根据其原始标记显示整个表。如果启用了Javascript,则表格适合指定的高度和宽度,并且tbody会滚动,如果存在thead和tfoot,则它们将固定在顶部和底部。

答案 3 :(得分:7)

受@ Purag的回答启发,这是另一个flexbox解决方案:

&#13;
&#13;
/* basic settings */
table { display: flex; flex-direction: column; width: 200px; }
tr { display: flex; }
th:nth-child(1), td:nth-child(1) { flex-basis: 35%; }
th:nth-child(2), td:nth-child(2) { flex-basis: 65%; }
thead, tbody { overflow-y: scroll; }
tbody { height: 100px; }

/* color settings*/
table, th, td { border: 1px solid black; }
tr:nth-child(odd) { background: #EEE; }
tr:nth-child(even) { background: #AAA; }
thead tr:first-child { background: #333; }
th:first-child, td:first-child { background: rgba(200,200,0,0.7); }
th:last-child, td:last-child { background: rgba(255,200,0,0.7); }
&#13;
<table>
    <thead>
      <tr>
        <th>a
        <th>bbbb
    <tbody>
      <tr>
        <td>fooo vsync dynamic
        <td>bar
      <tr>
        <td>a
        <td>b
      <tr>
        <td>a
        <td>b
      <tr>
        <td>a
        <td>b
      <tr>
        <td>a
        <td>b
      <tr>
        <td>a
        <td>b
      <tr>
        <td>a
        <td>b
  </table>
&#13;
&#13;
&#13;

答案 4 :(得分:4)

我已经使用此代码轻松实现了这一目标:

所以你有这样的结构:

<table>
<thead><tr></tr></thead>
<tbody><tr></tr></tbody>
</table>

使用以下方式为thead设计样式:

<style>
thead{ 
    position: -webkit-sticky;
    position: -moz-sticky;
    position: -ms-sticky;
    position: -o-sticky;
    position: sticky;
    top: 0px;
}
</style>

需要考虑三件事:

首先,这个属性是新的。除了基于Webkit的浏览器的beta版本之外,它根本不受支持。请注意格式化。同样,如果您真的希望您的用户从粘性标头中受益,请使用javascript实现。

其次,如果您使用它,则需要合并供应商前缀。也许位置:粘性将在某一天起作用。但是现在,你需要使用position:-webkit-sticky(和其他人一样;在这篇文章中进一步检查css块)。

第三,目前没有任何定位默认值,所以你需要至少包括top:0;在与位置相同的css声明中:-webkit-sticky。否则,它只会在屏幕外滚动。

答案 5 :(得分:3)

如果您可以选择为表格单元格提供固定宽度(以及标题的固定高度),则可以使用position: fixed选项:

http://jsfiddle.net/thundercracker/ZxPeh/23/

你只需要将它贴在iframe中即可。你也可以通过给iframe一个滚动条(我认为)来进行水平滚动。


编辑2015

如果您可以预先定义表格单元格的宽度(以百分比表示),那么这里有一些更优雅(仅限CSS)的解决方案:

http://jsfiddle.net/7UBMD/77/

答案 6 :(得分:3)

仅限CSS:

<强> CSS

tr {
  width: 100%;
  display: inline-table;
  table-layout: fixed;
}

table{
 height:300px;              // <-- Select the height of the table
 display: -moz-groupbox;    // Firefox Bad Effect
}
tbody{
  overflow-y: scroll;      
  height: 200px;            //  <-- Select the height of the body
  width: 100%;
  position: absolute;
}

Bootply http://www.bootply.com/AgI8LpDugl

答案 7 :(得分:1)

我看到这个帖子已经暂停了一段时间,但是这个主题让我感兴趣,现在有了一些CSS3选择器,这变得更容易了(而且只用CSS非常可行)。

此解决方案依赖于具有表容器的最大高度。但只要您可以使用:first-child选择器,它就会受到支持。

小提琴here

如果有人能改进这个答案,请做!我计划很快在商业应用中使用这个解决方案!

<强> HTML

<div id="con">
<table>
    <thead>
        <tr>
            <th>Header 1</th>
            <th>Header 2</th>
            <th>Header 3</th>
        </tr>
    </thead>        
    <tbody>             
    </tbody>
</table>
</div>

<强> CSS

#con{
    max-height:300px;
    overflow-y:auto;
}
thead tr:first-child {
    background-color:#00f;
    color:#fff;
    position:absolute;
}
tbody tr:first-child td{
    padding-top:28px;
}

答案 8 :(得分:1)

我遇到了同样的问题,经过2天的研究后,我发现Ksesocss的解决方案适合我,也许对你也有好处。它允许固定的标题和动态宽度,只使用CSS。唯一的问题是源是西班牙语,但你可以在那里找到html和css代码。

这是链接:

http://ksesocss.blogspot.com/2014/10/responsive-table-encabezado-fijo-scroll.html

我希望这会有所帮助

答案 9 :(得分:1)

如果在所有提到的解决方案都不起作用的情况下(因为它适合我),请尝试使用两个解决方案,正如我在此回答中所解释的那样

https://stackoverflow.com/a/47722456/6488361

答案 10 :(得分:0)

由于我最近需要这个,我将分享使用3个表的解决方案,但不需要JavaScript。

表1(父)包含两行。第一行包含列标题的表2(子1)。第二行包含滚动内容的表3(子2)。

必须注意,childTbl必须比25pxparentTbl才能使滚动条正确显示。

This是来源,我从中得到了这个想法。我没有弃用的标签和内联CSS,使HTML5友好。

&#13;
&#13;
.parentTbl table {
  border-spacing: 0;
  border-collapse: collapse;
  border: 0;
  width: 690px;
}
.childTbl table {
  border-spacing: 0;
  border-collapse: collapse;
  border: 1px solid #d7d7d7;
  width: 665px;
}
.childTbl th,
.childTbl td {
  border: 1px solid #d7d7d7;
}
.scrollData {
  width: 690;
  height: 150px;
  overflow-x: hidden;
}
&#13;
<div class="parentTbl">
  <table>
    <tr>
      <td>
        <div class="childTbl">
          <table class="childTbl">
            <tr>
              <th>Header 1</th>
              <th>Header 2</th>
              <th>Header 3</th>
              <th>Header 4</th>
              <th>Header 5</th>
              <th>Header 6</th>
            </tr>
          </table>
        </div>
      </td>
    </tr>
    <tr>
      <td>
        <div class="scrollData childTbl">
          <table>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
            <tr>
              <td>Table Data 1</td>
              <td>Table Data 2</td>
              <td>Table Data 3</td>
              <td>Table Data 4</td>
              <td>Table Data 5</td>
              <td>Table Data 6</td>
            </tr>
          </table>
        </div>
      </td>
    </tr>
  </table>
</div>
&#13;
&#13;
&#13;

这在不同的浏览器上是可靠的,缺点是必须对表格宽度进行硬编码。

答案 11 :(得分:0)

我最近试图弄清楚这一点,我想出了一个很好的解决方案,可以在我的浏览器中完美运行(Chrome 51),支持动态列宽。我应该提一下,在我独立地得出我的答案后,我也发现了一种在网络上其他地方描述的类似技术......

诀窍是使用两个相同的表位于彼此之上。下表是可见的,上表对于标题是不可见的。上表还在tbody上设置了pointer-events: none,因此鼠标交互点击了下面的表格。这样,下表在上表的标题下滚动。

对于要正确布局和调整大小的所有内容(例如,当用户调整屏幕宽度时),两个表都需要具有相同的滚动条行为。但是,由于pointer-events: none,上表的滚动条会被忽略,并且可以通过以下方式隐藏:

<style>
    .hiddenScrollbar::-webkit-scrollbar {
        background-color: transparent;
    }
</style>

以下是完整的代码:

<html>

<head>
  <style>
    td {
      border: 1px solid black; 
      white-space: nowrap;
    }
    th {
      background-color: gray;
      border: 1px solid black;
      white-space: nowrap;
    }
    .hiddenScrollbar::-webkit-scrollbar {
      background-color: transparent;
    }
  </style>
</head>

<body>
  Table test. Use mouse wheel to scroll or scroll to the right to see vertical scroll bar. You can also remove the outermost div if you don't want a horizontal scroll bar.
  <br/>
  <div style="display: inline-block; height: 10em; width: 15em; overflow-x: scroll; overflow-y: hidden">
    <div style="position: relative;">
      <div style="display: inline-block; position: absolute; left: 0px; top: 0px; height: 10em; overflow-y: scroll">
        <table style="border-collapse: collapse;">
          <thead>
            <tr>
              <th>Column 1</th>
              <th>Another Column</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Data 1</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 2</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 3</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 4</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 5</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 6</td>
              <td>12340921375021342354235 very long...</td>
            </tr>
            <tr>
              <td>Data 7</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 8</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 9</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 10</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 11</td>
              <td>123409213750213</td>
            </tr>
            <tr>
              <td>Data 12</td>
              <td>123409213750213</td>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="hiddenScrollbar" style="display: inline-block; pointer-events: none; position: relative; left: 0px; top: 0px; height: 10em; overflow-y: scroll">
        <table style="border-collapse: collapse;">
          <thead style="pointer-events: auto">
            <tr>
              <th>Column 1</th>
              <th>Another Column</th>
            </tr>
          </thead>
          <tbody style="visibility: hidden">
            <tr>
              <tr>
                <td>Data 1</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 2</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 3</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 4</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 5</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 6</td>
                <td>12340921375021342354235 very long...</td>
              </tr>
              <tr>
                <td>Data 7</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 8</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 9</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 10</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 11</td>
                <td>123409213750213</td>
              </tr>
              <tr>
                <td>Data 12</td>
                <td>123409213750213</td>
              </tr>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div><br/>
Stuff after table.
</body>

</html>

警告:需要做更多工作才能证明这在其他浏览器中有效。我在混合内联和样式表样式方面相当自由。但是我相信一般的概念是最好的方法,因为目标浏览器支持它。 唯一的功能/显示问题是,如果您需要水平滚动条,则垂直滚动条可以滚出视图(如代码段中所示)。您仍然可以使用鼠标滚轮滚动。 此外,您无法在标题上显示透明背景(否则下方表格会显示)。最后,您需要一种方法来生成两个相同的表。我个人使用react.js并且很容易做出反应,但php或其他服务器端生成或javascript也可以。

答案 12 :(得分:0)

另一个实现,但tbody和动态列没有任何溢出。但需要JavaScript。容器div用于容纳列标题。当表格滚过视口时,顶部会出现一个固定的标题。如果表格水平滚动,固定标题也会滚动。

使用带有span的{​​{1}}元素创建列标题,并使用负边距水平滚动标题。还使用display: inline-block优化以避免任何混乱。

RequestAnimationFrame

https://codepen.io/lloydleo/pen/NRpqEE