表格固定标题和可滚动主体

447
我正在尝试使用Bootstrap 3表格来创建具有固定标题和可滚动内容的表格。不幸的是,我发现的解决方案不能与Bootstrap一起使用或破坏样式。
这里有一个简单的Bootstrap表格,但由于某些原因,我不知道tbody的高度为什么不是10px。
height: 10px !important; overflow: scroll;

例子:

<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">

<table class="table table-striped">
    <thead>
    <tr>
        <th>Make</th>
        <th>Model</th>
        <th>Color</th>
        <th>Year</th>
    </tr>
    </thead>
    <tbody style="height: 10px !important; overflow: scroll; ">
    <tr>
        <td class="filterable-cell">111 Ford</td>
        <td class="filterable-cell">Escort</td>
        <td class="filterable-cell">Blue</td>
        <td class="filterable-cell">2000</td>
    </tr>
    <tr>
        <td class="filterable-cell">Ford</td>
        <td class="filterable-cell">Escort</td>
        <td class="filterable-cell">Blue</td>
        <td class="filterable-cell">2000</td>
    </tr>
            <tr>
        <td class="filterable-cell">Ford</td>
        <td class="filterable-cell">Escort</td>
        <td class="filterable-cell">Blue</td>
        <td class="filterable-cell">2000</td>
    </tr>
     <tr>
        <td class="filterable-cell">Ford</td>
        <td class="filterable-cell">Escort</td>
        <td class="filterable-cell">Blue</td>
        <td class="filterable-cell">2000</td>
    </tr>
    </tbody>
    
</table>


5
这对你有帮助吗?https://dev59.com/YWQn5IYBdhLWcg3wIkQF#17380697 - Hashem Qolami
1
这绝对帮了我大忙。这是解决方案:http://jsfiddle.net/T9Bhm/7/谢谢。 - giulio
1
当td的高度不匹配时,无法正确处理table-striped属性。请参见我添加的td {overflow-wrap: break-word;}。http://jsfiddle.net/T9Bhm/4755/ - John Zabroski
2
我认为更好、更老的SO线程被标记为重复: p https://dev59.com/YWQn5IYBdhLWcg3wIkQF - DevLoverUmar
1
在Bootstrap 4中,sticky-top实用类设置position: sticky; top: 0;确保标题永远不会从屏幕顶部滚动出去(我不确定这在Bootstrap 3中是否有效)。只需将其应用于所有th标记即可。不确定这是否符合您的确切情况,我只在Chrome中进行了测试。 - jsaven
30个回答

596

固定表头 - 仅使用CSS

只需将您的th元素设置为position: sticky; top: 0;即可。(Chrome,FF,Edge)

.tableFixHead          { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }

/* Just common table stuff. Really. */
table  { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th     { background:#eee; }
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><td>A1</td><td>A2</td></tr>
      <tr><td>B1</td><td>B2</td></tr>
      <tr><td>C1</td><td>C2</td></tr>
      <tr><td>D1</td><td>D2</td></tr>
      <tr><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>

对于 在中的粘性垂直TH和水平TH列

.tableFixHead          { overflow: auto; height: 100px; width: 240px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead tbody th { position: sticky; left: 0; }

.tableFixHead          { overflow: auto; height: 100px; width: 240px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }
.tableFixHead tbody th { position: sticky; left: 0; }

/* Just common table stuff. Really. */
table  { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; white-space: nowrap; }
th     { background:#eee; }
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th></th><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><th>Foo</th><td>Some long text lorem ipsum</td><td>Dolor sit amet</td></tr>
      <tr><th>Bar</th><td>B1</td><td>B2</td></tr>
      <tr><th>Baz</th><td>C1</td><td>C2</td></tr>
      <tr><th>Fuz</th><td>D1</td><td>D2</td></tr>
      <tr><th>Zup</th><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>

TH边框问题修复

由于无法正确地在翻译的TH元素上绘制border,因此使用box-shadow属性重新创建和渲染"borders"

/* Borders (if you need them) */
.tableFixHead,
.tableFixHead td {
  box-shadow: inset 1px -1px #000;
}
.tableFixHead th {
  box-shadow: inset 1px 1px #000, 0 1px #000;
}

.tableFixHead          { overflow: auto; height: 100px; }
.tableFixHead thead th { position: sticky; top: 0; z-index: 1; }

/* Just common table stuff. Really. */
table  { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 16px; }
th     { background:#eee; }

/* Borders (if you need them) */
.tableFixHead,
.tableFixHead td {
  box-shadow: inset 1px -1px #000;
}
.tableFixHead th {
  box-shadow: inset 1px 1px #000, 0 1px #000;
}
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><td>A1</td><td>A2</td></tr>
      <tr><td>B1</td><td>B2</td></tr>
      <tr><td>C1</td><td>C2</td></tr>
      <tr><td>D1</td><td>D2</td></tr>
      <tr><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>

TH固定不起作用的解决方法

请确保th元素的父元素,至少到table元素(包括table元素本身),没有设置与overflow相关的样式(例如overflowoverflow-xoverflow-y)。

更多信息请参见stackoverflow.com/为什么'position: sticky'无法正常工作?


使用JavaScript固定表头

对于 老旧浏览器,您可以使用一些JS代码和 translateY 来移动 th 元素。

// Fix table head example:

function tableFixHead(evt) {
  const el = evt.currentTarget,
    sT = el.scrollTop;
  el.querySelectorAll("thead th").forEach(th =>
    th.style.transform = `translateY(${sT}px)`
  );
}

document.querySelectorAll(".tableFixHead").forEach(el =>
  el.addEventListener("scroll", tableFixHead)
);
.tableFixHead {
  overflow-y: auto;
  height: 100px;
}


/* Just common table stuff. */

table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  padding: 8px 16px;
}

th {
  background: #eee;
}
<div class="tableFixHead">
  <table>
    <thead>
      <tr><th>TH 1</th><th>TH 2</th></tr>
    </thead>
    <tbody>
      <tr><td>A1</td><td>A2</td></tr>
      <tr><td>B1</td><td>B2</td></tr>
      <tr><td>C1</td><td>C2</td></tr>
      <tr><td>D1</td><td>D2</td></tr>
      <tr><td>E1</td><td>E2</td></tr>
    </tbody>
  </table>
</div>


@Roko非常感谢您提供如此干净的解决方案,它在Chrome中运行得很好。不幸的是,在IE11中无法正常工作。是否有任何调整可以使其在IE11中工作? - A P
1
@AP适用于IE11(因为它不理解sticky),您可以使用第二个示例,该示例使用translateY(以及jQuery代码-如果您没有编译ES6 js代码)。 它会工作,但看起来有点“跳跃”。 但没关系。 如果有人想使用过时的浏览器,那么这个人/公司永远不会抱怨卡顿的问题-只要他能看到内容并与您的网站进行良好的交互即可。 有趣的阅读:https://nealbuerger.com/2018/01/the-end-of-life-of-internet-explorer-11/ - Roko C. Buljan
3
当表头有多行时,无法正常工作。当滚动时,第二行表头会跳上来并覆盖在第一行表头上,因为它们都设置了"top:0"。尝试将整个<thead>设为sticky,但无法使其正常工作。对于具有多行标题(还带有colspan),有什么想法吗? - user3191192
2
如果{ position: sticky; top: 0; }的顶部解决方案在您的代码中无法正常工作,请检查包含元素(特别是tabletheadtr)的overflow-xoverflow-y CSS属性。令人惊讶的是,两者都不应该被设置。您可能需要明确地将它们unset。更多信息请参见-https://dev59.com/77Lma4cB1Zd3GeqPUAP8#54806576。 - Ivan Koshelev
1
position: sticky 广泛被低估,有点违反直觉,特别是因为你还需要 top: 0。但这实际上是一个完美的案例,展示如何有目的地使用 position: sticky! - ViBoNaCci
显示剩余11条评论

100

使用纯CSS方法,您需要一个带有overflow-y: auto;属性的容器,并决定如何隐藏滚动/溢出的行:

  1. 通过非透明的粘性标题(position: sticky; top: 0; z-index: 1;)来覆盖,例如在@RokoCBuljan的答案中。
  2. 切换行的可见性(通过设置以下tr:after属性)。

注意,容器可以是外部的<div>,也可以是<table>本身或其一部分(例如<tbody>)。对于后两者,您必须设置display: block;,以便它们被视为

请参见下面修改过的@giulio的解决方案:

:root {
  --height-height: 150px;
  /* cell width has to reserve some space for scrolling. Hence the sum < 100% */
  --cell-width: 85px;
}

.header-fixed {
  width: 200px;
}

/* Treat all as divs */
.header-fixed > thead,
.header-fixed > tbody,
.header-fixed > thead > tr,
.header-fixed > tbody > tr,
.header-fixed > thead > tr > th,
.header-fixed > tbody > tr > td {
  display: block;
}

/* Prevent header to wrap */
.header-fixed > thead > tr > th {
  white-space: nowrap;
  background-color: lightgrey;
}

.header-fixed > tbody > tr:after,
.header-fixed > thead > tr:after {
  content: ' ';
  display: block;
  visibility: hidden;
  clear: both;
}

.header-fixed > tbody {
  overflow-y: auto;
  height: var(--height-height);
}

.header-fixed > tbody > tr > td,
.header-fixed > thead > tr > th {
  width: var(--cell-width);
  border: thin solid grey;
  float: left;
}
<table class="header-fixed">
 <thead>
   <tr> <th>Header 1</th> <th>Header 2</th> </tr>
 </thead>
 <tbody>
   <tr> <td>cell 11</td> <td>cell 12</td> </tr>
   <tr> <td>cell 21</td> <td>cell 22</td> </tr>
   <tr> <td>cell 31</td> <td>cell 32</td> </tr>
   <tr> <td>cell 41</td> <td>cell 42</td> </tr>
   <tr> <td>cell 51</td> <td>cell 52</td> </tr>
   <tr> <td>cell 61</td> <td>cell 62</td> </tr>
   <tr> <td>cell 71</td> <td>cell 72</td> </tr>
   <tr> <td>cell 81</td> <td>cell 82</td> </tr>
   <tr> <td>cell 91</td> <td>cell 92</td> </tr>
 </tbody>
</table>

注意:如果你有>2列,你需要相应地调整var(--cell-width)变量。


3
在尝试各种复杂的解决方案后,我发现这是最好的解决办法。然而,在垂直滚动条上有一定宽度(这是显而易见的),但由于它,标题和行之间存在轻微的不匹配对齐。我有7列,所以在使用提到的公式计算后,将宽度设置为14.28%。 - Chetan
1
我该如何为每一列分配特定的宽度? - user1807271
@user1807271,您可以通过JS为每一列分配宽度,或者创建一个按列分类的类(例如“col1”,“col2”等),并为所需的宽度分配类。将“col1”类分配给第一列中的所有单元格,“col2”类分配给第二列中的所有单元格,以此类推。 - Luke
4
我建议您也添加.header-fixed > thead > tr > th { white-space: nowrap; }。如果标题开始换行,它会搞乱事情。 - Omar Wagih
您介意使用 [<>] 代码片段编辑器制作一个 [mcve] 吗? - mplungjan
显示剩余2条评论

93

以下是可行的解决方案:

table {
    width: 100%;
}

thead, tbody, tr, td, th { display: block; }

tr:after {
    content: ' ';
    display: block;
    visibility: hidden;
    clear: both;
}

thead th {
    height: 30px;

    /*text-align: left;*/
}

tbody {
    height: 120px;
    overflow-y: auto;
}

thead {
    /* fallback */
}


tbody td, thead th {
    width: 19.2%;
    float: left;
}
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"/>

<table class="table table-striped">
    <thead>
        <tr>
            <th>Make</th>
            <th>Model</th>
            <th>Color</th>
            <th>Year</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td class="filterable-cell">Ford</td>
            <td class="filterable-cell">Escort</td>
            <td class="filterable-cell">Blue</td>
            <td class="filterable-cell">2000</td>
        </tr>
        <tr>
            <td class="filterable-cell">Ford</td>
            <td class="filterable-cell">Escort</td>
            <td class="filterable-cell">Blue</td>
            <td class="filterable-cell">2000</td>
        </tr>
        <tr>
            <td class="filterable-cell">Ford</td>
            <td class="filterable-cell">Escort</td>
            <td class="filterable-cell">Blue</td>
            <td class="filterable-cell">2000</td>
        </tr>
        <tr>
            <td class="filterable-cell">Ford</td>
            <td class="filterable-cell">Escort</td>
            <td class="filterable-cell">Blue</td>
            <td class="filterable-cell">2000</td>
        </tr>
    </tbody>
</table>

链接到jsfiddle


34
在IE9(可能也在其他浏览器中)无法工作;需要固定宽度的列(如果您要使用固定宽度的列,有更简单、更跨浏览器的解决方案);如果内容超出固定宽度,会出现严重错误;实际上没有将标题和列对齐(可以在上面链接的jsfiddle中看到,而这个版本则更清晰地显示了简单边框),尽管这可能可以修复;滚动条与表格主体不完全对齐... - T.J. Crowder
为td和th添加可见边框,您会发现td的宽度与th的宽度不匹配。 - Apolo
5
这个解决方案的问题在于它需要一个固定的高度。这不适用于响应式设计。我一直在研究一种解决方案,它使用position: fixed,解决了滚动问题,但是会破坏行宽。 - Jeffrey A. Gochin
您可以手动解决td宽度与th宽度不匹配的问题,方法是分离出td和th的宽度%声明,并将th设置为稍微更窄。您每次都需要自定义完成,但这不是经常使用的功能。 - Ben Hoffman
在标题行中遇到了问题:标题行只能有不超过4列,如果您有超过4列,则会出现新行。需要将宽度%调整为相应的行号 - Jason Lim Ji Chen

63

更新

如果需要更新且仍在维护的库,请尝试jquery.floatThead(Bob Jordan在评论中提到).

旧回答

这是一个非常古老的回答,下面提到的库已不再得到维护。

我正在使用StickyTableHeaders,它运行得很好!

但我必须添加此CSS样式使标题栏不透明。

table#stickyHeader thead {
  border-top: none;
  border-bottom: none;
  background-color: #FFF;
}

9
请注意stickyTableHeaders插件只会找到页面初始加载时存在的HTML内容,无法捕捉动态生成的内容。 - Rob Sedgwick
3
@RobSedgwick,我没有试过这个,但它应该仍然有效。只要在生成表格后初始化stickyTableHeadres即可。这意味着您不能在头部初始化它,而是需要在动态生成的表格完成后立即初始化它。 - Rosdi Kasim
1
太棒了,非常感谢这个解决方案。如果有人在使背景不透明时遇到问题,我不得不使用.tableFloatingHeaderOriginal { //css } - Matt Inamdar
这个插件对我来说不起作用,因为有时候表头会浮出表格,然后又浮回去,这让它看起来像是坏了。 - Mati
我们如何调整垂直滚动条的间距?我们能否使用CSS来实现? - M2012

40

不需要将其包在一个div中...

CSS:

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

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

Bootply : https://www.codeply.com/p/nkaQFc713a


6
当你意识到列数据必须具有相同的宽度时,这看起来很不错。如果不是这样,那么数据就无法适合在标题下方显示。 - Tmac
当单个单元格溢出时,此解决方案无效。 - John Zabroski
如果我不想要滚动条怎么办? - Dejell
tbody after setting width: 100% filled page width. Was fixed by position: relative to table - Dmitry
1
这在Chrome中不起作用,无论是Bootply还是解决方案。 - kevinc
2
仅供参考,它在Chrome中对我有效,在页面内容的中间顶部有表格...你能提供更多信息吗?(我在Linux上) - BENARD Patrick

40

迄今为止,我看到的最好的解决方案是只使用CSS,在多个浏览器上支持良好,且没有对齐问题,来自 codingrabbithole 的这个解决方案。

table {
  width: 100%;
}
thead, tbody tr {
  display: table;
  width: 100%;
  table-layout: fixed;
}
tbody {
  display: block;
  overflow-y: auto;
  table-layout: fixed;
  max-height: 200px;
}

3
我本来跳过了下面 @Roko C. Buljan 的更高评价的答案,结果发现当表头有多行时那个答案并不起作用。这个解决方案非常好,非常感谢!!! - shao.lo
1
不确定是因为我的表格在嵌套网格布局中还是因为表格真的很宽,但这会在我的表格上产生最奇怪的渲染 - 就像每列都向右偏移了几个像素,因此每列几乎覆盖了前一列,导致外观非常失灵。 - Michael
如果您想使用<colgroup><col style="width: 10px;"><col style="width: 100px;"></colgroup>来管理表格列的宽度,则此方法无效 - 所有设置都将被忽略。 - izogfif
通过使用`.table-scroll th:nth-child(1), .table-scroll td:nth-child(1) { width: 3%;}`解决了colgroup问题,适用于n个列。 - Tom Shelley
3
与顶部解决方案不同,这需要将表格高度设置为低于页面,并且还会出现额外的内部滚动条。有时这是您想要的(例如对于固定大小的内部表格),但否则不太理想。 - Steve Horvath
这个解决方案忽略了使用粘性页眉。 - undefined

32

使用 CSS 可以更加简单。

table tbody { display:block; max-height:450px; overflow-y:scroll; }
table thead, table tbody tr { display:table; width:100%; table-layout:fixed; }

1
你可以通过使用JavaScript添加CSS类来克服table-layout:fixed - user2182349
3
th does not align with td - deanwilliammills

25

table {
    overflow-y: auto;
    height: 50vh;     /* !!!  HEIGHT MUST BE IN [ vh ] !!! */
}

thead th {
    background-color: white;
    position: sticky;
    top: 0;
}
   <table>
      <thead>
        <tr><th>TH 1</th><th>TH 2</th></tr>
      </thead>
      <tbody>
        <tr><td>A1</td><td>A2</td></tr>
        <tr><td>B1</td><td>B2</td></tr>
        <tr><td>C1</td><td>C2</td></tr>
        <tr><td>D1</td><td>D2</td></tr>
        <tr><td>E1</td><td>E2</td></tr>
        <tr><td>F1</td><td>F2</td></tr>
        <tr><td>G1</td><td>G2</td></tr>
        <tr><td>H1</td><td>H2</td></tr>
        <tr><td>I1</td><td>I2</td></tr>
        <tr><td>J1</td><td>J2</td></tr>
        <tr><td>K1</td><td>K2</td></tr>
        <tr><td>L1</td><td>L2</td></tr>
        <tr><td>M1</td><td>M2</td></tr>
        <tr><td>N1</td><td>N2</td></tr>
        <tr><td>O1</td><td>O2</td></tr>
        <tr><td>P1</td><td>P2</td></tr>
        <tr><td>Q1</td><td>Q2</td></tr>
        <tr><td>R1</td><td>R2</td></tr>
        <tr><td>S1</td><td>S2</td></tr>
        <tr><td>T1</td><td>T2</td></tr>
        <tr><td>U1</td><td>U2</td></tr>
        <tr><td>V1</td><td>V2</td></tr>
        <tr><td>W1</td><td>W2</td></tr>
        <tr><td>X1</td><td>X2</td></tr>
        <tr><td>Y1</td><td>Y2</td></tr>
        <tr><td>Z1</td><td>Z2</td></tr>
      </tbody>
    </table>

您不需要使用JS。重要的是在 [vh] 中设置表格高度。


1
运行得非常好!谢谢。当标题离开屏幕时,它开始浮动。正是我所需要的。我设置了背景颜色,以便它不会与表格内容混合,并且设置了z-index:2,因此它在前面。 - Mark
不错。最后为TH元素添加样式:th { background-color: white; opacity: 1; z-index: 100; } - dat
我在页眉中添加了一个背景。我不确定这样做是否正确,因为现在你无法看到行在页眉下滚动。 - TomDom

16

我来晚了(这就是我的人生),但由于这是谷歌的第一个结果,并且上面的所有内容都没有让我运作起来,所以这是我的代码

/*Set a min width where your table start to look like crap*/
table { min-width: 600px; }

/*The next 3 sections make the magic happen*/
thead, tbody tr {
    display: table;
    width: 100%;
    table-layout: fixed;
}

tbody {
    display: block;
    max-height: 200px;
    overflow-x: hidden;
    overflow-y: scroll;
}

td {
    overflow: hidden;
    text-overflow: ellipsis;
}

/*Use the following to make sure cols align correctly*/
table, tr, th, td {
    border: 1px solid black;
    border-collapse: collapse;
}


/*Set your columns to where you want them to be, skip the one that you can have resize to any width*/
    th:nth-child(1), td:nth-child(1) {
    width: 85px;
}
th:nth-child(2), td:nth-child(2) {
    width: 150px;
}
th:nth-child(4), td:nth-child(4) {
    width: 125px;
}
th:nth-child(5) {
    width: 102px;
}
td:nth-child(5) {
    width: 85px;
}

4
它对我有用,但现在所有的单元格宽度都相同,而我不想要这样。 - shubhamkes
真是太棒了。顺便提一下:th:nth-child(2), td:nth-child(2) 等同于 tr > :nth-child(2) - Washington Guedes

12

在我看来,jQuery中最好的插件之一是DataTables

它还有一个固定表头的扩展,非常容易实现。

引用自他们的网站:

HTML:

<table id="example" class="display" cellspacing="0" width="100%">
    <thead>
        <tr>
            <th>Name</th>
            <th>Position</th>
            <th>Office</th>
            <th>Age</th>
            <th>Start date</th>
            <th>Salary</th>
        </tr>
    </thead>

    <tfoot>
        <tr>
            <th>Name</th>
            <th>Position</th>
            <th>Office</th>
            <th>Age</th>
            <th>Start date</th>
            <th>Salary</th>
        </tr>
    </tfoot>

    <tbody>
        <tr>
            <td>Tiger Nixon</td>
            <td>System Architect</td>
            <td>Edinburgh</td>
            <td>61</td>
            <td>2011/04/25</td>
            <td>$320,800</td>
        </tr>
        <tr>
            <td>Garrett Winters</td>
            <td>Accountant</td>
            <td>Tokyo</td>
            <td>63</td>
            <td>2011/07/25</td>
            <td>$170,750</td>
        </tr>
        <tr>
            <td>Ashton Cox</td>
            <td>Junior Technical Author</td>
            <td>San Francisco</td>
            <td>66</td>
            <td>2009/01/12</td>
            <td>$86,000</td>
        </tr>
  </tbody>
</table>

JavaScript:

$(document).ready(function() {
    var table = $('#example').DataTable();

    new $.fn.dataTable.FixedHeader( table );
} );

但是最简单的方法只需要使<tbody>可以滚动:

//configure table with fixed header and scrolling rows
$('#example').DataTable({
    scrollY: 400,
    scrollCollapse: true,
    paging: false,
    searching: false,
    ordering: false,
    info: false
});

我想要第一列和表头都固定,但根据数据表兼容性图表,这是不可能的。 - PirateApp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接