73

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

需要明确的是,我要做的是使用一个table标签(它是有效的子标签,colgroup, col, thead, tbody, tfoot, tr, th, td),但采用一组符合以下条件的 CSS 规则:

  1. 必须保持页眉/页脚/内容行之间的列对齐
  2. 必须允许页眉/页脚在内容垂直滚动时保持固定
  3. 必须不需要任何 jQuery 或其他 JavaScript 才能提供功能
  4. 只能使用上面提供的标签

此代码示例: 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 来提供水平滚动。无论如何我都不关心水平滚动,所以如果解决方案不这样做也没关系。

4

13 回答 13

41

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

查看当前支持:https ://caniuse.com/#feat=css-sticky


用于position: sticky

另一种答案是使用position: sticky. 如 W3C 所述

粘性定位框的定位类似于相对定位的框,但偏移量是参考最近的带有滚动框的祖先计算的,或者如果没有祖先有滚动框,则参考视口。

这准确描述了相对静态标头的行为。<thead>将它分配给第一个或第一个HTML 标记很容易,因为根据 W3C<tr>应该支持它。但是,ChromeIE 和 Edge都存在为这些标签分配粘性位置属性的问题。目前似乎也没有优先解决这个问题。

似乎对表格元素有效的是将粘性属性分配给表格单元格。在这种情况下,<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-wrapper。


使用position: sticky polyfill

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

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

于 2018-09-21T13:13:25.090 回答
18

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

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

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

唯一需要注意的是所有表格单元格必须具有相同的宽度。下面是一个明显人为设计的例子,但是当单元格内容不同时这很好用(表格单元格都具有相同的宽度和自动换行,强制 flexbox 保持它们相同的宽度而不管内容)。是一个单元格内容不同的示例。

只需将该.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-word;
}

.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>

于 2015-08-03T22:31:33.447 回答
8

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

/* 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); }
<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>

于 2016-09-28T14:18:33.350 回答
7

据我所知,没有标准的方法可以仅使用 CSS 来实现这一点,尽管我认为应该有。Mozilla 浏览器曾经支持带有滚动正文的固定标题,但在过去几年中他们已将其删除。

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

于 2013-10-23T21:11:13.843 回答
5

我使用以下代码轻松实现了这一点:

所以你有一个这样的结构:

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

只需使用以下样式设置标题:

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

需要考虑的三件事:

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

其次,如果您确实使用它,则需要合并供应商前缀。也许位置:sticky 有一天会起作用。不过,现在,您需要使用 position:-webkit-sticky (以及其他;在这篇文章中进一步检查 css 块)。

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

于 2017-04-20T20:25:01.883 回答
3

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

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

您只需将其粘贴在iframe. 您也可以通过提供滚动条来进行水平滚动iframe(我认为)。


编辑 2015

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

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

于 2012-08-11T03:47:12.617 回答
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;
}

引导层:http : //www.bootply.com/AgI8LpDugl

于 2017-03-30T07:42:52.410 回答
1

我看到这个线程已经有一段时间没有活动了,但是这个话题让我很感兴趣,现在有了一些 CSS3 选择器,这变得更容易了(而且只用 CSS 就很可行)。

此解决方案依赖于表格容器的最大高度。但只要你能使用:first-child选择器就支持它。

在这里拉小提琴。

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

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;
}
于 2014-06-27T13:11:42.753 回答
1

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

这是链接:

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

我希望这有帮助

于 2015-07-03T08:28:45.600 回答
1

如果在所有提到的解决方案都不起作用的情况下(对我来说),它变得很困难,请尝试一个两表解决方案,正如我在这个答案中解释的那样

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

于 2017-12-08T21:37:57.443 回答
0

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

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

必须注意,滚动条childTbl必须25pxparentTbl滚动条正确显示的短。

是我的想法的来源。我在没有弃用标签和内联 CSS 的情况下使它对 HTML5 友好。

.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;
}
<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>

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

于 2015-03-27T15:30:10.410 回答
0

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

诀窍是使用两个相同的桌子,它们相互叠放。下表是可见的,而上表是不可见的,除了标题。上表也pointer-events: none设置在 tbody 上,因此鼠标交互会击中下表。这样下层表格在上层表格的标题下滚动。

为了正确布局和调整大小(例如当用户调整屏幕宽度时),两个表需要具有相同的滚动条行为。但是,上表的滚动条会被忽略,这要归功于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,使用 react 很容易做到这一点,但 php 或其他服务器端生成或 javascript 也可以。

于 2016-08-04T18:07:33.870 回答
0

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

列标题是使用span元素创建display: inline-block的,负边距用于水平滚动标题。还优化使用RequestAnimationFrame以避免任何卡顿。

function rAF(scrollLeft) {
  var offsetLeft = 0 - scrollLeft;
  $('.hdr__inner span:first-child').css('margin-left', offsetLeft);
}

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

于 2016-09-24T23:41:19.003 回答