简体   繁体   English

带有固定标题的 HTML 表?

[英]HTML table with fixed headers?

Is there a cross-browser CSS/JavaScript technique to display a long HTML table such that the column headers stay fixed on-screen and do not scroll with the table body.是否有跨浏览器的 CSS/JavaScript 技术来显示长 HTML 表格,以便列标题在屏幕上保持固定并且不随表格主体滚动。 Think of the "freeze panes" effect in Microsoft Excel.想一想 Microsoft Excel 中的“冻结窗格”效果。

I want to be able to scroll through the contents of the table, but to always be able to see the column headers at the top.我希望能够滚动浏览表格的内容,但始终能够看到顶部的列标题。

This can be cleanly solved in four lines of code.这可以用四行代码干净地解决。

If you only care about modern browsers, a fixed header can be achieved much easier by using CSS transforms.如果您只关心现代浏览器,则可以通过使用 CSS 转换更轻松地实现固定的 header。 Sounds odd, but works great:听起来很奇怪,但效果很好:

  • HTML and CSS stay as-is. HTML 和 CSS 保持原样。
  • No external JavaScript dependencies.没有外部 JavaScript 依赖项。
  • Four lines of code.四行代码。
  • Works for all configurations (table-layout: fixed, etc.).适用于所有配置(表格布局:固定等)。
document.getElementById("wrap").addEventListener("scroll", function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});

Support for CSS transforms is widely available except for Internet Explorer 8-.对 CSS 转换的支持广泛可用,除了 Internet Explorer 8-。

Here is the full example for reference:这是供参考的完整示例:

 document.getElementById("wrap").addEventListener("scroll",function(){ var translate = "translate(0,"+this.scrollTop+"px)"; this.querySelector("thead").style.transform = translate; });
 /* Your existing container */ #wrap { overflow: auto; height: 400px; } /* CSS for demo */ td { background-color: green; width: 200px; height: 100px; }
 <div id="wrap"> <table> <thead> <tr> <th>Foo</th> <th>Bar</th> </tr> </thead> <tbody> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> <tr><td></td><td></td></tr> </tbody> </table> </div>

I was looking for a solution for this for a while and found most of the answers are not working or not suitable for my situation, so I wrote a simple solution with jQuery.我一直在寻找解决方案,发现大多数答案都不起作用或不适合我的情况,所以我用 jQuery 编写了一个简单的解决方案。

This is the solution outline:这是解决方案大纲:

  1. Clone the table that needs to have a fixed header, and place the cloned copy on top of the original.克隆需要固定 header 的表,并将克隆的副本放在原始的顶部。
  2. Remove the table body from top table.从顶部桌子上取下桌子主体。
  3. Remove the table header from bottom table.从底部表格中删除表格 header。
  4. Adjust the column widths.调整列宽。 (We keep track of the original column widths) (我们跟踪原始列宽)

Below is the code in a runnable demo.以下是可运行演示中的代码。

 function scrolify(tblAsJQueryObject, height) { var oTbl = tblAsJQueryObject; // for very large tables you can remove the four lines below // and wrap the table with <div> in the mark-up and assign // height and overflow property var oTblDiv = $("<div/>"); oTblDiv.css('height', height); oTblDiv.css('overflow', 'scroll'); oTbl.wrap(oTblDiv); // save original width oTbl.attr("data-item-original-width", oTbl.width()); oTbl.find('thead tr td').each(function() { $(this).attr("data-item-original-width", $(this).width()); }); oTbl.find('tbody tr:eq(0) td').each(function() { $(this).attr("data-item-original-width", $(this).width()); }); // clone the original table var newTbl = oTbl.clone(); // remove table header from original table oTbl.find('thead tr').remove(); // remove table body from new table newTbl.find('tbody tr').remove(); oTbl.parent().parent().prepend(newTbl); newTbl.wrap("<div/>"); // replace ORIGINAL COLUMN width newTbl.width(newTbl.attr('data-item-original-width')); newTbl.find('thead tr td').each(function() { $(this).width($(this).attr("data-item-original-width")); }); oTbl.width(oTbl.attr('data-item-original-width')); oTbl.find('tbody tr:eq(0) td').each(function() { $(this).width($(this).attr("data-item-original-width")); }); } $(document).ready(function() { scrolify($('#tblNeedsScrolling'), 160); // 160 is height });
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <div style="width:300px;border:6px green solid;"> <table border="1" width="100%" id="tblNeedsScrolling"> <thead> <tr><th>Header 1</th><th>Header 2</th></tr> </thead> <tbody> <tr><td>row 1, cell 1</td><td>row 1, cell 2</td></tr> <tr><td>row 2, cell 1</td><td>row 2, cell 2</td></tr> <tr><td>row 3, cell 1</td><td>row 3, cell 2</td></tr> <tr><td>row 4, cell 1</td><td>row 4, cell 2</td></tr> <tr><td>row 5, cell 1</td><td>row 5, cell 2</td></tr> <tr><td>row 6, cell 1</td><td>row 6, cell 2</td></tr> <tr><td>row 7, cell 1</td><td>row 7, cell 2</td></tr> <tr><td>row 8, cell 1</td><td>row 8, cell 2</td></tr> </tbody> </table> </div>

This solution works in Chrome and IE.此解决方案适用于 Chrome 和 IE。 Since it is based on jQuery, this should work in other jQuery supported browsers as well.由于它基于 jQuery,因此它也应该适用于其他 jQuery 支持的浏览器。

I've just completed putting together a jQuery plugin that will take valid single table using valid HTML (have to have a thead and tbody) and will output a table that has fixed headers, optional fixed footer that can either be a cloned header or any content you chose (pagination, etc.). I've just completed putting together a jQuery plugin that will take valid single table using valid HTML (have to have a thead and tbody) and will output a table that has fixed headers, optional fixed footer that can either be a cloned header or any您选择的内容(分页等)。 If you want to take advantage of larger monitors it will also resize the table when the browser is resized.如果您想利用更大的显示器,它也会在调整浏览器大小时调整表格大小。 Another added feature is being able to side scroll if the table columns can not all fit in view.如果表格列不能全部放在视图中,另一个附加功能是能够侧滚动。

http://fixedheadertable.com/ http://fixedheadertable.com/

on github: http://markmalek.github.com/Fixed-Header-Table/在 github: http://markmalek.github.com/Fixed-Header-Table/

It's extremely easy to setup and you can create your own custom styles for it.它非常容易设置,您可以为其创建自己的自定义 styles。 It also uses rounded corners in all browsers.它还在所有浏览器中使用圆角。 Keep in mind I just released it, so it's still technically beta and there are very few minor issues I'm ironing out.请记住,我刚刚发布了它,所以它在技术上仍然是测试版,我正在解决的小问题很少。

It works in Internet Explorer 7, Internet Explorer 8, Safari, Firefox and Chrome.它适用于 Internet Explorer 7、Internet Explorer 8、Safari、Firefox 和 Chrome。

I also created a plugin that addresses this issue.我还创建了一个插件来解决这个问题。 My project - jQuery.floatThead has been around for over 4 years now and is very mature.我的项目 - jQuery.floatThead已经存在 4 年多了,并且非常成熟。

It requires no external styles and does not expect your table to be styled in any particular way.它不需要外部 styles 并且不希望您的表格以任何特定方式设置样式。 It supports Internet Explorer9+ and Firefox/Chrome.它支持 Internet Explorer9+ 和 Firefox/Chrome。

Currently (2018-05) it has:目前(2018-05)它有:

405 commits and 998 stars on GitHub GitHub 上的 405 次提交和 998 颗星


Many (not all) of the answers here are quick hacks that may have solved the problem one person was having, but will work not for every table.这里的许多(不是全部)答案都是快速破解,可能已经解决了一个人遇到的问题,但不适用于每张桌子。

Some of the other plugins are old and probably work great with Internet Explorer, but will break on Firefox and Chrome.其他一些插件很旧,可能在 Internet Explorer 上运行良好,但会在 Firefox 和 Chrome 上中断。

TL;DR TL;博士

If you target modern browsers and don't have extravagant styling needs: http://jsfiddle.net/dPixie/byB9d/3/ ... Although the big four version is pretty sweet as well this version handles fluid width a lot better.如果您针对现代浏览器并且没有奢侈的样式需求: http://jsfiddle.net/dPixie/byB9d/3/ ...虽然四大版本也很可爱,但这个版本处理流体宽度要好得多。

Good news everyone!大家好消息!

With the advances of HTML5 and CSS3 this is now possible, at least for modern browsers.随着 HTML5 和 CSS3 的进步,这现在是可能的,至少对于现代浏览器来说是这样。 The slightly hackish implementation I came up with can be found here: http://jsfiddle.net/dPixie/byB9d/3/ .我想出的稍微有点骇人听闻的实现可以在这里找到: http://jsfiddle.net/dPixie/byB9d/3/ I have tested it in FX 25, Chrome 31 and IE 10...我已经在 FX 25、Chrome 31 和 IE 10 中对其进行了测试...

Relevant HTML (insert a HTML5 doctype at the top of your document though):相关的 HTML (尽管在文档顶部插入 HTML5 文档类型):

 html, body { margin: 0; padding: 0; height: 100%; } section { position: relative; border: 1px solid #000; padding-top: 37px; background: #500; } section.positioned { position: absolute; top: 100px; left: 100px; width: 800px; box-shadow: 0 0 15px #333; }.container { overflow-y: auto; height: 200px; } table { border-spacing: 0; width: 100%; } td+td { border-left: 1px solid #eee; } td, th { border-bottom: 1px solid #eee; background: #ddd; color: #000; padding: 10px 25px; } th { height: 0; line-height: 0; padding-top: 0; padding-bottom: 0; color: transparent; border: none; white-space: nowrap; } th div { position: absolute; background: transparent; color: #fff; padding: 9px 25px; top: 0; margin-left: -25px; line-height: normal; border-left: 1px solid #800; } th:first-child div { border: none; }
 <section class="positioned"> <div class="container"> <table> <thead> <tr class="header"> <th> Table attribute name <div>Table attribute name</div> </th> <th> Value <div>Value</div> </th> <th> Description <div>Description</div> </th> </tr> </thead> <tbody> <tr> <td>align</td> <td>left, center, right</td> <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the alignment of a table according to surrounding text</td> </tr> <tr> <td>bgcolor</td> <td>rgb(x,x,x), #xxxxxx, colorname</td> <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the background color for a table</td> </tr> <tr> <td>border</td> <td>1,""</td> <td>Specifies whether the table cells should have borders or not</td> </tr> <tr> <td>cellpadding</td> <td>pixels</td> <td>Not supported in HTML5. Specifies the space between the cell wall and the cell content</td> </tr> <tr> <td>cellspacing</td> <td>pixels</td> <td>Not supported in HTML5. Specifies the space between cells</td> </tr> <tr> <td>frame</td> <td>void, above, below, hsides, lhs, rhs, vsides, box, border</td> <td>Not supported in HTML5. Specifies which parts of the outside borders that should be visible</td> </tr> <tr> <td>rules</td> <td>none, groups, rows, cols, all</td> <td>Not supported in HTML5. Specifies which parts of the inside borders that should be visible</td> </tr> <tr> <td>summary</td> <td>text</td> <td>Not supported in HTML5. Specifies a summary of the content of a table</td> </tr> <tr> <td>width</td> <td>pixels, %</td> <td>Not supported in HTML5. Specifies the width of a table</td> </tr> </tbody> </table> </div> </section>

But how?!但是怎么办?!

Simply put you have a table header, that you visually hide by making it 0px high, that also contains divs used as the fixed header.简单地说,您有一个表 header,您可以通过将其设置为 0px 高来在视觉上隐藏它,其中还包含用作固定 header 的 div。 The table's container leaves enough room at the top to allow for the absolutely positioned header, and the table with scrollbars appear as you would expect.表格的容器在顶部留有足够的空间以允许绝对定位的 header,并且带有滚动条的表格会按您的预期显示。

The code above uses the positioned class to position the table absolutely (I'm using it in a popup style dialog) but you can use it in the flow of the document as well by removing the positioned class from the container.上面的代码绝对使用定位的 class 到 position 表格(我在弹出式对话框中使用它)但是您也可以通过从容器中删除positioned的 ZA2F2ED4F8EBC2CBB4C21A29DC40AB61 来在文档流中使用它。

But...但...

It's not perfect.这并不完美。 Firefox refuses to make the header row 0px (at least I did not find any way) but stubbornly keeps it at minimum 4px... It's not a huge problem, but depending on your styling it will mess with your borders etc. Firefox 拒绝使 header 行 0px (至少我没有找到任何方法),但顽固地将其保持在至少 4px ......这不是一个大问题,但根据您的样式,它会弄乱您的边框等。

The table is also using a faux column approach where the background color of the container itself is used as the background for the header divs, that are transparent.该表还使用了一种假列方法,其中容器本身的背景颜色用作 header div 的背景,它们是透明的。

Summary概括

All in all there might be styling issues depending on your requirements, especially borders or complicated backgrounds.总而言之,根据您的要求,可能存在样式问题,尤其是边框或复杂背景。 There might also be problems with computability, I haven't checked it in a wide variety of browsers yet (please comment with your experiences if you try it out), but I didn't find anything like it so I thought it was worth posting anyway...可计算性也可能存在问题,我还没有在各种浏览器中检查过(如果你尝试过,请评论你的经验),但我没有找到类似的东西,所以我认为值得发布反正...

All of the attempts to solve this from outside the CSS specification are pale shadows of what we really want: Delivery on the implied promise of THEAD.从 CSS 规范之外解决这个问题的所有尝试都是我们真正想要的苍白阴影:在 THEAD 的隐含 promise 上交付。

This frozen-headers-for-a-table issue has been an open wound in HTML/CSS for a long time.很长一段时间以来,HTML/CSS 中的冻结标题问题一直是一个开放的伤口。

In a perfect world, there would be a pure-CSS solution for this problem.在一个完美的世界里,这个问题会有一个纯 CSS 的解决方案。 Unfortunately, there doesn't seem to be a good one in place.不幸的是,似乎没有一个好的地方。

Relevant standards-discussions on this topic include:关于该主题的相关标准讨论包括:

UPDATE : Firefox shipped position:sticky in version 32. Everyone wins!更新:Firefox 发货position:sticky 。每个人都赢了!

Here is a jQuery plugin for fixed table headers.这是一个用于固定表头的 jQuery 插件。 It allows the entire page to scroll, freezing the header when it reaches the top.它允许整个页面滚动,当 header 到达顶部时冻结。 It works well with Twitter Bootstrap tables.它适用于 Twitter 引导表。

GitHub repository: https://github.com/oma/table-fixed-header GitHub 存储库: https://github.com/oma/table-fixed-header

It does not scroll only table content.不仅仅滚动表格内容。 Look to other tools for that, as one of these other answers.寻找其他工具,作为其他答案之一。 You decide what fits your case the best.你决定什么最适合你的情况。

The CSS property position: sticky has great support in most modern browsers (I had issues with Edge, see below). CSS 属性position: sticky在大多数现代浏览器中都有很好的支持(我遇到了 Edge 的问题,见下文)。

This lets us solve the problem of fixed headers quite easily:这让我们很容易解决固定标题的问题:

thead th { position: sticky; top: 0; }

Safari needs a vendor prefix: -webkit-sticky . Safari 需要供应商前缀: -webkit-sticky

For Firefox, I had to add min-height: 0 to one the parent elements.对于 Firefox,我必须将min-height: 0添加到父元素之一。 I forget exactly why this was needed.我完全忘记了为什么需要这样做。

Most unfortunately, the Microsoft Edge implementation seems to be only semi-working.最不幸的是,Microsoft Edge 的实现似乎只是半工作。 At least, I had some flickering and misaligned table cells in my testing.至少,我的测试中有一些闪烁和未对齐的表格单元格。 The table was still usable, but had significant aesthetic issues.这张桌子仍然可以使用,但存在严重的美学问题。

Most of the solutions posted here require jQuery.此处发布的大多数解决方案都需要 jQuery。 If you are looking for a framework independent solution try Grid: http://www.matts411.com/post/grid/如果您正在寻找独立于框架的解决方案,请尝试使用 Grid: http://www.matts411.com/post/grid/

It's hosted on Github here: https://github.com/mmurph211/Grid它托管在 Github 上: https://github.com/mmurph211/Grid

Not only does it support fixed headers, it also supports fixed left columns and footers, among other things.它不仅支持固定页眉,还支持固定左栏和页脚等。

A more refined pure CSS scrolling table更精致的纯CSS滚动表

All of the pure CSS solutions I've seen so far-- clever though they may be-- lack a certain level of polish, or just don't work right in some situations.到目前为止,我所看到的所有纯 CSS 解决方案(尽管它们可能很聪明)都缺乏一定程度的修饰,或者在某些情况下无法正常工作。 So, I decided to create my own...所以,我决定创建自己的...

Features:特征:

  • It's pure CSS, so no jQuery required (or any JavaScript code at all, for that matter)它是纯 CSS,因此不需要 jQuery(或任何 JavaScript 代码,就此而言)
  • You can set the table width to a percent (aka "fluid") or a fixed value, or let the content determine its width (aka "auto")您可以将表格宽度设置为百分比(又名“流体”)或固定值,或者让内容确定其宽度(又名“自动”)
  • Column widths can also be fluid, fixed, or auto.列宽也可以是流动的、固定的或自动的。
  • Columns will never become misaligned with headers due to horizontal scrolling (a problem that occurs in every other CSS-based solution I've seen that doesn't require fixed widths).由于水平滚动,列永远不会与标题不对齐(我见过的所有其他基于 CSS 的解决方案中都会出现这个问题,不需要固定宽度)。
  • Compatible with all of the popular desktop browsers, including Internet Explorer back to version 8与所有流行的桌面浏览器兼容,包括 Internet Explorer 回到第 8 版
  • Clean, polished appearance;干净、抛光的外观; no sloppy-looking 1-pixel gaps or misaligned borders;没有看起来邋遢的 1 像素间隙或未对齐的边框; looks the same in all browsers在所有浏览器中看起来都一样

Here are a couple of fiddles that show the fluid and auto width options:这里有几个显示流体和自动宽度选项的小提琴:

  • Fluid Width and Height (adapts to screen size): jsFiddle (Note that the scrollbar only shows up when needed in this configuration, so you may have to shrink the frame to see it) Fluid Width and Height (适应屏幕大小): jsFiddle (注意滚动条只在此配置中需要时才会显示,因此您可能需要缩小框架才能看到它)

  • Auto Width, Fixed Height (easier to integrate with other content): jsFiddle自动宽度,固定高度(更容易与其他内容集成): jsFiddle

The Auto Width, Fixed Height configuration probably has more use cases, so I'll post the code below. Auto Width, Fixed Height 配置可能有更多的用例,所以我将在下面发布代码。

 /* The following 'html' and 'body' rule sets are required only if using a % width or height*/ /*html { width: 100%; height: 100%; }*/ body { box-sizing: border-box; width: 100%; height: 100%; margin: 0; padding: 0 20px 0 20px; text-align: center; }.scrollingtable { box-sizing: border-box; display: inline-block; vertical-align: middle; overflow: hidden; width: auto; /* If you want a fixed width, set it here, else set to auto */ min-width: 0/*100%*/; /* If you want a % width, set it here, else set to 0 */ height: 188px/*100%*/; /* Set table height here; can be fixed value or % */ min-height: 0/*104px*/; /* If using % height, make this large enough to fit scrollbar arrows + caption + thead */ font-family: Verdana, Tahoma, sans-serif; font-size: 16px; line-height: 20px; padding: 20px 0 20px 0; /* Need enough padding to make room for caption */ text-align: left; color: black; }.scrollingtable * {box-sizing: border-box;}.scrollingtable > div { position: relative; border-top: 1px solid black; height: 100%; padding-top: 20px; /* This determines column header height */ }.scrollingtable > div:before { top: 0; background: cornflowerblue; /* Header row background color */ }.scrollingtable > div:before, .scrollingtable > div > div:after { content: ""; position: absolute; z-index: -1; width: 100%; height: 100%; left: 0; }.scrollingtable > div > div { min-height: 0/*43px*/; /* If using % height, make this large enough to fit scrollbar arrows */ max-height: 100%; overflow: scroll/*auto*/; /* Set to auto if using fixed or % width; else scroll */ overflow-x: hidden; border: 1px solid black; /* Border around table body */ }.scrollingtable > div > div:after {background: white;} /* Match page background color */.scrollingtable > div > div > table { width: 100%; border-spacing: 0; margin-top: -20px; /* Inverse of column header height */ /*margin-right: 17px;*/ /* Uncomment if using % width */ }.scrollingtable > div > div > table > caption { position: absolute; top: -20px; /*inverse of caption height*/ margin-top: -1px; /*inverse of border-width*/ width: 100%; font-weight: bold; text-align: center; }.scrollingtable > div > div > table > * > tr > * {padding: 0;}.scrollingtable > div > div > table > thead { vertical-align: bottom; white-space: nowrap; text-align: center; }.scrollingtable > div > div > table > thead > tr > * > div { display: inline-block; padding: 0 6px 0 6px; /*header cell padding*/ }.scrollingtable > div > div > table > thead > tr >:first-child:before { content: ""; position: absolute; top: 0; left: 0; height: 20px; /*match column header height*/ border-left: 1px solid black; /*leftmost header border*/ }.scrollingtable > div > div > table > thead > tr > * > div[label]:before, .scrollingtable > div > div > table > thead > tr > * > div > div:first-child, .scrollingtable > div > div > table > thead > tr > * +:before { position: absolute; top: 0; white-space: pre-wrap; color: white; /*header row font color*/ }.scrollingtable > div > div > table > thead > tr > * > div[label]:before, .scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);}.scrollingtable > div > div > table > thead > tr > * +:before { content: ""; display: block; min-height: 20px; /* Match column header height */ padding-top: 1px; border-left: 1px solid black; /* Borders between header cells */ }.scrollingtable.scrollbarhead {float: right;}.scrollingtable.scrollbarhead:before { position: absolute; width: 100px; top: -1px; /* Inverse border-width */ background: white; /* Match page background color */ }.scrollingtable > div > div > table > tbody > tr:after { content: ""; display: table-cell; position: relative; padding: 0; border-top: 1px solid black; top: -1px; /* Inverse of border width */ }.scrollingtable > div > div > table > tbody {vertical-align: top;}.scrollingtable > div > div > table > tbody > tr {background: white;}.scrollingtable > div > div > table > tbody > tr > * { border-bottom: 1px solid black; padding: 0 6px 0 6px; height: 20px; /* Match column header height */ }.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;}.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /* Alternate row color */.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /* Borders between body cells */
 <div class="scrollingtable"> <div> <div> <table> <caption>Top Caption</caption> <thead> <tr> <th><div label="Column 1"/></th> <th><div label="Column 2"/></th> <th><div label="Column 3"/></th> <th> <;-- More versatile way of doing column label. requires two identical copies of label --> <div><div>Column 4</div><div>Column 4</div></div> </th> <th class="scrollbarhead"/> <:-- ALWAYS ADD THIS EXTRA CELL AT END OF HEADER ROW --> </tr> </thead> <tbody> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr> </tbody> </table> </div> Faux bottom caption </div> </div> <;--[if lte IE 9]><style>.scrollingtable > div > div > table {margin-right: 17px;}</style><![endif]-->

The method I used to freeze the header row is similar to d-Pixie's, so refer to his post for an explanation.我用来冻结 header 行的方法与 d-Pixie 的类似,所以请参阅他的帖子以获得解释。 There were a slew of bugs and limitations with that technique that could only be fixed with heaps of additional CSS and an extra div container or two.该技术存在大量错误和限制,只能通过大量额外的 CSS 和额外的一两个 div 容器来修复。

A simple jQuery plugin一个简单的 jQuery 插件

This is a variation on Mahes' solution.这是 Mahes 解决方案的一个变体。 You can call it like $('table#foo').scrollableTable();您可以将其称为$('table#foo').scrollableTable();

The idea is:这个想法是:

  • Split the thead and tbody into separate table elementstheadtbody拆分为单独的table元素
  • Make their cell widths match again使它们的单元格宽度再次匹配
  • Wrap the second table in a div.scrollable将第二个table包装在div.scrollable
  • Use CSS to make div.scrollable actually scroll使用 CSS 使div.scrollable实际滚动

The CSS could be: CSS 可以是:

div.scrollable { height: 300px; overflow-y: scroll;}

Caveats注意事项

  • Obviously, splitting up these tables makes the markup less semantic.显然,拆分这些表会降低标记的语义。 I'm not sure what effect this has on accessibility.我不确定这对可访问性有什么影响。
  • This plugin does not deal with footers, multiple headers, etc.这个插件不处理页脚、多个页眉等。
  • I've only tested it in Chrome version 20.我只在 Chrome 版本 20 中测试过它。

That said, it works for my purposes and you're free to take and modify it.也就是说,它适用于我的目的,您可以自由使用和修改它。

Here's the plugin:这是插件:

jQuery.fn.scrollableTable = function () {
  var $newTable, $oldTable, $scrollableDiv, originalWidths;
  $oldTable = $(this);

  // Once the tables are split, their cell widths may change. 
  // Grab these so we can make the two tables match again.
  originalWidths = $oldTable.find('tr:first td').map(function() {
    return $(this).width();
  });

  $newTable = $oldTable.clone();
  $oldTable.find('tbody').remove();
  $newTable.find('thead').remove();

  $.each([$oldTable, $newTable], function(index, $table) {
    $table.find('tr:first td').each(function(i) {
      $(this).width(originalWidths[i]);
    });
  });

  $scrollableDiv = $('<div/>').addClass('scrollable');
  $newTable.insertAfter($oldTable).wrap($scrollableDiv);
};

:) :)

Not-so-clean, but pure HTML/CSS solution.不那么干净,但纯粹的 HTML/CSS 解决方案。

table {
    overflow-x:scroll;
}

tbody {
    max-height: /*your desired max height*/
    overflow-y:scroll;
    display:block;
}

Updated for IE8+ JSFiddle example针对 IE8+ JSFiddle 示例进行了更新

Somehow I ended up with Position:Sticky working fine on my case:不知何故,我最终得到了Position:Sticky在我的情况下工作正常:

 table{ width: 100%; border: collapse; } th{ position: sticky; top: 0px; border: 1px solid black; background: #ff5722; color: #f5f5f5; font-weight: 600; } td{ background: #d3d3d3; border: 1px solid black; color: #f5f5f5; font-weight: 600; } div{ height: 150px overflow: auto; width: 100% }
 <div> <table> <thead> <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> <th>header 7</th> </tr> </thead> <tbody> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> <tr> <td>data 1</td> <td>data 2</td> <td>data 3</td> <td>data 4</td> <td>data 5</td> <td>data 6</td> <td>data 7</td> </tr> </tbody> </table> </div>

Support for fixed footer支持固定页脚

I extended Nathan's function to also support a fixed footer and maximum height.我扩展了 Nathan 的 function 以支持固定页脚和最大高度。 Also, the function will set the CSS itself, and you only have to support a width.此外,function 将设置 CSS 本身,您只需要支持一个宽度。

Usage:用法:

Fixed height:固定高度:

$('table').scrollableTable({ height: 100 });

Maximum height (if the browser supports the CSS 'max-height' option):最大高度(如果浏览器支持 CSS 'max-height' 选项):

$('table').scrollableTable({ maxHeight: 100 });

Script:脚本:

jQuery.fn.scrollableTable = function(options) {

    var $originalTable, $headTable, $bodyTable, $footTable, $scrollableDiv, originalWidths;

    // Prepare the separate parts of the table
    $originalTable = $(this);
    $headTable = $originalTable.clone();

    $headTable.find('tbody').remove();
    $headTable.find('tfoot').remove();

    $bodyTable = $originalTable.clone();
    $bodyTable.find('thead').remove();
    $bodyTable.find('tfoot').remove();

    $footTable = $originalTable.clone();
    $footTable.find('thead').remove();
    $footTable.find('tbody').remove();

    // Grab the original column widths and set them in the separate tables
    originalWidths = $originalTable.find('tr:first td').map(function() {
        return $(this).width();
    });

    $.each([$headTable, $bodyTable, $footTable], function(index, $table) {
        $table.find('tr:first td').each(function(i) {
            $(this).width(originalWidths[i]);
        });
    });

    // The div that makes the body table scroll
    $scrollableDiv = $('<div/>').css({
        'overflow-y': 'scroll'
    });

    if(options.height) {
        $scrollableDiv.css({'height': options.height});
    }
    else if(options.maxHeight) {
        $scrollableDiv.css({'max-height': options.maxHeight});
    }

    // Add the new separate tables and remove the original one
    $headTable.insertAfter($originalTable);
    $bodyTable.insertAfter($headTable);
    $footTable.insertAfter($bodyTable);
    $bodyTable.wrap($scrollableDiv);
    $originalTable.remove();
};

Two divs, one for header, one for data.两个 div,一个用于 header,一个用于数据。 Make the data div scrollable, and use JavaScript to set the width of the columns in the header to be the same as the widths in the data.使数据 div 可滚动,并使用 JavaScript 将 header 中的列宽设置为与数据中的宽度相同。 I think the data columns widths need to be fixed rather than dynamic.我认为数据列的宽度需要固定而不是动态的。

I realize the question allows JavaScript, but here is a pure CSS solution I worked up that also allows for the table to expand horizontally.我意识到这个问题允许 JavaScript,但这是一个纯粹的 CSS 解决方案,我研究它还允许表格水平扩展。 It was tested with Internet Explorer 10 and the latest Chrome and Firefox browsers.它使用 Internet Explorer 10 以及最新的 Chrome 和 Firefox 浏览器进行了测试。 A link to jsFiddle is at the bottom. jsFiddle 的链接在底部。

The HTML: HTML:

Putting some text here to differentiate between the header
aligning with the top of the screen and the header aligning
with the top of one of its ancestor containers.

<div id="positioning-container">
<div id="scroll-container">
    <table>
        <colgroup>
            <col class="col1"></col>
            <col class="col2"></col>
        </colgroup>
        <thead>
            <th class="header-col1"><div>Header 1</div></th>
            <th class="header-col2"><div>Header 2</div></th>
        </thead>
        <tbody>
            <tr><td>Cell 1.1</td><td>Cell 1.2</td></tr>
            <tr><td>Cell 2.1</td><td>Cell 2.2</td></tr>
            <tr><td>Cell 3.1</td><td>Cell 3.2</td></tr>
            <tr><td>Cell 4.1</td><td>Cell 4.2</td></tr>
            <tr><td>Cell 5.1</td><td>Cell 5.2</td></tr>
            <tr><td>Cell 6.1</td><td>Cell 6.2</td></tr>
            <tr><td>Cell 7.1</td><td>Cell 7.2</td></tr>

        </tbody>
    </table>
</div>
</div>

And the CSS: CSS:

table{
    border-collapse: collapse;
    table-layout: fixed;
    width: 100%;
}
/* Not required, just helps with alignment for this example */
td, th{
    padding: 0;
    margin: 0;
}

tbody{
    background-color: #ddf;
}

thead {
    /* Keeps the header in place. Don't forget top: 0 */
    position: absolute;
    top: 0;
    background-color: #ddd;

    /* The 17px is to adjust for the scrollbar width.
     * This is a new css value that makes this pure
     * css example possible */
    width: calc(100% - 17px);
    height: 20px;
}

/* Positioning container. Required to position the
 * header since the header uses position:absolute
 * (otherwise it would position at the top of the screen) */
#positioning-container{
    position: relative;
}

/* A container to set the scroll-bar and
 * includes padding to move the table contents
 * down below the header (padding = header height) */
#scroll-container{
    overflow-y: auto;
    padding-top: 20px;
    height: 100px;
}
.header-col1{
    background-color: red;
}

/* Fixed-width header columns need a div to set their width */
.header-col1 div{
    width: 100px;
}

/* Expandable columns need a width set on the th tag */
.header-col2{
    width: 100%;
}
.col1 {
    width: 100px;
}
.col2{
    width: 100%;
}

http://jsfiddle.net/HNHRv/3/ http://jsfiddle.net/HNHRv/3/

I found this workaround - move header row in a table above table with data:我找到了这个解决方法 - 在表格上方的表格中移动 header 行,其中包含数据:

 <html> <head> <title>Fixed header</title> <style> table td {width:75px;} </style> </head> <body> <div style="height:auto; width:350px; overflow:auto"> <table border="1"> <tr> <td>header 1</td> <td>header 2</td> <td>header 3</td> </tr> </table> </div> <div style="height:50px; width:350px; overflow:auto"> <table border="1"> <tr> <td>row 1 col 1</td> <td>row 1 col 2</td> <td>row 1 col 3</td> </tr> <tr> <td>row 2 col 1</td> <td>row 2 col 2</td> <td>row 2 col 3</td> </tr> <tr> <td>row 3 col 1</td> <td>row 3 col 2</td> <td>row 3 col 3</td> </tr> <tr> <td>row 4 col 1</td> <td>row 4 col 2</td> <td>row 4 col 3</td> </tr> <tr> <td>row 5 col 1</td> <td>row 5 col 2</td> <td>row 5 col 3</td> </tr> <tr> <td>row 6 col 1</td> <td>row 6 col 2</td> <td>row 6 col 3</td> </tr> </table> </div> </body> </html>

For those who tried the nice solution given by Maximilian Hils, and did not succeed to get it to work with Internet Explorer, I had the same problem (Internet Explorer 11) and found out what was the problem.对于那些尝试了 Maximilian Hils 给出的很好的解决方案,但没有成功让它与 Internet Explorer 一起工作的人,我遇到了同样的问题(Internet Explorer 11)并找出了问题所在。

In Internet Explorer 11 the style transform (at least with translate) does not work on <THEAD> .在 Internet Explorer 11 中,样式转换(至少使用 translate)在<THEAD>上不起作用。 I solved this by instead applying the style to all the <TH> in a loop.我通过将样式应用于循环中的所有<TH>来解决此问题。 That worked.那行得通。 My JavaScript code looks like this:我的 JavaScript 代码如下所示:

document.getElementById('pnlGridWrap').addEventListener("scroll", function () {
  var translate = "translate(0," + this.scrollTop + "px)";
  var myElements = this.querySelectorAll("th");
  for (var i = 0; i < myElements.length; i++) {
    myElements[i].style.transform=translate;
  }
});

In my case the table was a GridView in ASP.NET.在我的情况下,该表是 ASP.NET 中的 GridView。 First I thought it was because it had no <THEAD> , but even when I forced it to have one, it did not work.首先我认为这是因为它没有<THEAD> ,但即使我强迫它有一个,它也不起作用。 Then I found out what I wrote above.然后我发现了我上面写的东西。

It is a very nice and simple solution.这是一个非常好的和简单的解决方案。 On Chrome it is perfect, on Firefox a bit jerky, and on Internet Explorer even more jerky.在 Chrome 上它是完美的,在 Firefox 上有点生涩,在 Internet Explorer 上更生涩。 But all in all a good solution.但总的来说是一个很好的解决方案。

Very late to the party, but as it's still a party, here's my two cents using tailwindcss:派对已经很晚了,但由于它仍然是派对,这是我使用 tailwindcss 的两分钱:

<div class="h-screen overflow-hidden flex flex-col">
  <div class="overflow-y-scroll flex-1">
    <table>
      <thead class="sticky top-0">
        <tr>
          <th>Timestamp</th>
          <th>Species</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>2022-02-09T08:20:39.967Z</td>
          <td>willow</td>
        </tr>
        <tr>
          <td>2022-02-09T08:21:29.453Z</td>
          <td>red osier dogwood</td>
        </tr>
        <tr>
          <td>2022-02-09T08:22:18.984Z</td>
          <td>buttonbush</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>

Here's a full working example on tailwind's playgroud .这是 tailwind 的playgroud上的完整工作示例。

This is not an exact solution to the fixed header row, but I have created a rather ingenious method of repeating the header row throughout the long table, yet still keeping the ability to sort.这不是固定 header 行的精确解决方案,但我创建了一种相当巧妙的方法,在整个长表中重复 header 行,但仍保持排序能力。

This neat little option requires the jQuery tablesorter plugin .这个简洁的小选项需要jQuery tablesorter插件 Here's how it works:以下是它的工作原理:

HTML HTML

<table class="tablesorter boxlist" id="pmtable">
    <thead class="fixedheader">
        <tr class="boxheadrow">
            <th width="70px" class="header">Job Number</th>
            <th width="10px" class="header">Pri</th>
            <th width="70px" class="header">CLLI</th>
            <th width="35px" class="header">Market</th>
            <th width="35px" class="header">Job Status</th>
            <th width="65px" class="header">Technology</th>
            <th width="95px;" class="header headerSortDown">MEI</th>
            <th width="95px" class="header">TEO Writer</th>
            <th width="75px" class="header">Quote Due</th>
            <th width="100px" class="header">Engineer</th>
            <th width="75px" class="header">ML Due</th>
            <th width="75px" class="header">ML Complete</th>
            <th width="75px" class="header">SPEC Due</th>
            <th width="75px" class="header">SPEC Complete</th>
            <th width="100px" class="header">Install Supervisor</th>
            <th width="75px" class="header">MasTec OJD</th>
            <th width="75px" class="header">Install Start</th>
            <th width="30px" class="header">Install Hours</th>
            <th width="75px" class="header">Revised CRCD</th>
            <th width="75px" class="header">Latest Ship-To-Site</th>
            <th width="30px" class="header">Total Parts</th>
            <th width="30px" class="header">OEM Rcvd</th>
            <th width="30px" class="header">Minor Rcvd</th>
            <th width="30px" class="header">Total Received</th>
            <th width="30px" class="header">% On Site</th>
            <th width="60px" class="header">Actions</th>
        </tr>
    </thead>
        <tbody class="scrollable">
            <tr data-job_id="3548" data-ml_id="" class="odd">
                <td class="c black">FL-8-RG9UP</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">FTLDFLOV</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">Chris Byrd</td>
                <td class="c">Apr 13, 2013</td>
                <td class="c">Kris Hall</td>
                <td class="c">May 20, 2013</td>
                <td class="c">May 20, 2013</td>
                <td class="c">Jun 5, 2013</td>
                <td class="c">Jun 7, 2013</td>
                <td class="c">Joseph Fitz</td>
                <td class="c">Jun 10, 2013</td>
                <td class="c">TBD</td>
                <td class="c">123</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Jul 26, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058616"></td>
                <td class="c">TBD</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="c">N/A</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span></td>
            </tr>
            <tr data-job_id="4264" data-ml_id="2959" class="even">
                <td class="c black">MTS13009SF</td>
                <td data-pri="2" class="priority c yellow">M</td>
                <td class="c">OJUSFLTL</td>
                <td class="c">SFL</td>
                <td class="c">NOI</td>
                <td class="c">TRANSPORT</td>
                <td class="c"></td>
                <td class="c">DeMarcus Stewart</td>
                <td class="c">May 22, 2013</td>
                <td class="c">Ryan Alsobrook</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jun 27, 2013</td>
                <td class="c">Jun 19, 2013</td>
                <td class="c">Jul 4, 2013</td>
                <td class="c">Randy Williams</td>
                <td class="c">Jun 21, 2013</td>
                <td class="c">TBD</td>
                <td class="c">95</td>
                <td class="c revised_crcd"><input readonly="true" name="revised_crcd" value="Aug 9, 2013" type="text" size="12" class="smInput r_crcd c hasDatepicker" id="dp1377194058632"></td><td class="c">TBD</td>
                <td class="c">0</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="c">0.00%</td>
                <td class="actions"><span style="float:left;" class="ui-icon ui-icon-folder-open editJob" title="View this job" s="" details'=""></span><input style="float:left;" type="hidden" name="req_ship" class="reqShip hasDatepicker" id="dp1377194058464"><span style="float:left;" class="ui-icon ui-icon-calendar requestShip" title="Schedule this job for shipping"></span><span class="ui-icon ui-icon-info viewOrderInfo" style="float:left;" title="Show material details for this order"></span></td>
            </tr>
            .
            .
            .
            .
            <tr class="boxheadrow repeated-header">
                <th width="70px" class="header">Job Number</th>
                <th width="10px" class="header">Pri</th>
                <th width="70px" class="header">CLLI</th>
                <th width="35px" class="header">Market</th>
                <th width="35px" class="header">Job Status</th>
                <th width="65px" class="header">Technology</th>
                <th width="95px;" class="header">MEI</th>
                <th width="95px" class="header">TEO Writer</th>
                <th width="75px" class="header">Quote Due</th>
                <th width="100px" class="header">Engineer</th>
                <th width="75px" class="header">ML Due</th>
                <th width="75px" class="header">ML Complete</th>
                <th width="75px" class="header">SPEC Due</th>
                <th width="75px" class="header">SPEC Complete</th>
                <th width="100px" class="header">Install Supervisor</th>
                <th width="75px" class="header">MasTec OJD</th>
                <th width="75px" class="header">Install Start</th>
                <th width="30px" class="header">Install Hours</th>
                <th width="75px" class="header">Revised CRCD</th>
                <th width="75px" class="header">Latest Ship-To-Site</th>
                <th width="30px" class="header">Total Parts</th>
                <th width="30px" class="header">OEM Rcvd</th>
                <th width="30px" class="header">Minor Rcvd</th>
                <th width="30px" class="header">Total Received</th>
                <th width="30px" class="header">% On Site</th>
                <th width="60px" class="header">Actions</th>
            </tr>

Obviously, my table has many more rows than this.显然,我的表的行数比这多得多。 193 to be exact, but you can see where the header row repeats.准确地说是 193,但您可以看到 header 行重复的位置。 The repeating header row is set up by this function:重复的 header 行由此 function 设置:

jQuery jQuery

// Clone the original header row and add the "repeated-header" class
var tblHeader = $('tr.boxheadrow').clone().addClass('repeated-header');

// Add the cloned header with the new class every 34th row (or as you see fit)
$('tbody tr:odd:nth-of-type(17n)').after(tblHeader);

// On the 'sortStart' routine, remove all the inserted header rows
$('#pmtable').bind('sortStart', function() {
    $('.repeated-header').remove();
    // On the 'sortEnd' routine, add back all the header row lines.
}).bind('sortEnd', function() {
    $('tbody tr:odd:nth-of-type(17n)').after(tblHeader);
});

I wish I had found @Mark's solution earlier, but I went and wrote my own before I saw this SO question...我希望我早点找到@Mark 的解决方案,但是在我看到这个 SO 问题之前,我去写了自己的解决方案......

Mine is a very lightweight jQuery plugin that supports fixed header, footer, column spanning (colspan), resizing, horizontal scrolling, and an optional number of rows to display before scrolling starts.我的是一个非常轻量级的 jQuery 插件,它支持固定的 header、页脚、列跨度 (colspan)、调整大小、水平滚动以及在滚动开始前显示的可选行数。

jQuery.scrollTableBody (GitHub) jQuery.scrollTableBody (GitHub)

As long as you have a table with proper <thead> , <tbody> , and (optional) <tfoot> , all you need to do is this:只要您有一张带有正确<thead><tbody>和(可选) <tfoot>的表格,您需要做的就是:

$('table').scrollTableBody();

I developed a simple light-weight jQuery plug-in for converting a well formatted HTML table to a scrollable table with fixed table header and columns.我开发了一个简单的轻量级 jQuery 插件,用于将格式良好的 HTML 表转换为具有固定表 header 和列的可滚动表。

The plugin works well to match pixel-to-pixel positioning the fixed section with the scrollable section.该插件可以很好地匹配固定部分与可滚动部分的像素到像素定位。 Additionally, you could also freeze the number of columns that will be always in view when scrolling horizontally.此外,您还可以冻结水平滚动时始终在视图中的列数。

Demo & Documentation: http://meetselva.github.io/fixed-table-rows-cols/演示和文档: http://meetselva.github.io/fixed-table-rows-cols/

GitHub repository: https://github.com/meetselva/fixed-table-rows-cols GitHub 存储库: https://github.com/meetselva/fixed-table-rows-cols

Below is the usage for a simple table with a fixed header,下面是一个固定header的简单表的用法,

$(<table selector>).fxdHdrCol({
    width:     "100%",
    height:    200,
    colModal: [{width: 30, align: 'center'},
               {width: 70, align: 'center'}, 
               {width: 200, align: 'left'}, 
               {width: 100, align: 'center'}, 
               {width: 70, align: 'center'}, 
               {width: 250, align: 'center'}
              ]
});

A lot of people seem to be looking for this answer.很多人似乎都在寻找这个答案。 I found it buried in an answer to another question here: Syncing column width of between tables in two different frames, etc我发现它隐藏在另一个问题的答案中: Syncing column width of between tables in two different frames, etc

Of the dozens of methods I have tried this is the only method I found that works reliably to allow you to have a scrolling bottom table with the header table having the same widths.在我尝试过的几十种方法中,这是我发现的唯一一种可靠的方法,可以让您拥有一个滚动底部表格,其中 header 表格具有相同的宽度。

Here is how I did it, first I improved upon the jsfiddle above to create this function, which works on both td and th (in case that trips up others who use th for styling of their header rows).我是这样做的,首先我改进了上面的 jsfiddle 以创建这个 function,它适用于tdth (以防其他人使用th来设置其 header 行的样式)。

var setHeaderTableWidth= function (headertableid,basetableid) {
            $("#"+headertableid).width($("#"+basetableid).width());
            $("#"+headertableid+" tr th").each(function (i) {
                $(this).width($($("#"+basetableid+" tr:first td")[i]).width());
            });
            $("#" + headertableid + " tr td").each(function (i) {
                $(this).width($($("#" + basetableid + " tr:first td")[i]).width());
            });
        }

Next, you need to create two tables, NOTE the header table should have an extra TD to leave room in the top table for the scrollbar, like this:接下来,您需要创建两个表,注意 header 表应该有一个额外的TD ,以便在顶部表中为滚动条留出空间,如下所示:

 <table id="headertable1" class="input-cells table-striped">
        <thead>
            <tr style="background-color:darkgray;color:white;"><th>header1</th><th>header2</th><th>header3</th><th>header4</th><th>header5</th><th>header6</th><th></th></tr>
        </thead>
     </table>
    <div id="resizeToBottom" style="overflow-y:scroll;overflow-x:hidden;">
        <table id="basetable1" class="input-cells table-striped">
            <tbody >
                <tr>
                    <td>testdata</td>
                    <td>2</td>
                    <td>3</td>
                    <td>4</span></td>
                    <td>55555555555555</td>
                    <td>test</td></tr>
            </tbody>
        </table>
    </div>

Then do something like:然后执行以下操作:

        setHeaderTableWidth('headertable1', 'basetable1');
        $(window).resize(function () {
            setHeaderTableWidth('headertable1', 'basetable1');
        });

This is the only solution that I found on Stack Overflow that works out of many similar questions that have been posted, that works in all my cases.这是我在 Stack Overflow 上找到的唯一解决方案,该解决方案适用于已发布的许多类似问题,适用于我的所有情况。

For example, I tried the jQuery stickytables plugin which does not work with durandal, and the Google Code project here https://code.google.com/p/js-scroll-table-header/issues/detail?id=2例如,我尝试了不适用于 durandal 的 jQuery 粘性表插件,以及此处的 Google 代码项目https://code.google.com/p/js-scroll-table-header/issues/detail?id=2

Other solutions involving cloning the tables, have poor performance, or suck and don't work in all cases.其他涉及克隆表的解决方案,性能很差,或者很糟糕并且在所有情况下都不起作用。

There is no need for these overly complex solutions.不需要这些过于复杂的解决方案。 Just make two tables like the examples below and call setHeaderTableWidth function like described here and boom, you are done .只需像下面的示例一样制作两个表格,然后像这里描述的那样调用setHeaderTableWidth function 和繁荣,你就完成了

If this does not work for you, you probably were playing with your CSS box-sizing property and you need to set it correctly.如果这对您不起作用,您可能正在使用 CSS box-sizing 属性,您需要正确设置它。 It is easy to screw up your CSS content by accident.很容易意外地搞砸您的 CSS 内容。 There are many things that can go wrong, so just be aware/careful of that. go 有很多事情会出错,所以请注意/小心。 This approach works for me .这种方法对我有用

Here's a solution that we ended up working with (in order to deal with some edge cases and older versions of Internet Explorer, we eventually also faded out the title bar on scroll then faded it back in when scrolling ends, but in Firefox and WebKit browsers this solution just works . It assumes border-collapse: collapse.这是我们最终使用的解决方案(为了处理一些边缘情况和旧版本的 Internet Explorer,我们最终还在滚动时淡出标题栏,然后在滚动结束时将其淡入,但在 Firefox 和 WebKit 浏览器中这个解决方案很有效。它假设边界崩溃:崩溃。

The key to this solution is that once you apply border-collapse , CSS transforms work on the header, so it's just a matter of intercepting scroll events and setting the transform correctly.这个解决方案的关键是,一旦你应用了border-collapseCSS 转换就可以在 header 上工作,因此只需拦截滚动事件并正确设置转换即可。 You don't need to duplicate anything.你不需要复制任何东西。 Short of this behavior being implemented properly in the browser, it's hard to imagine a more light-weight solution.如果没有在浏览器中正确实现这种行为,很难想象一个更轻量级的解决方案。

JSFiddle: http://jsfiddle.net/podperson/tH9VU/2/ JSFiddle: http://jsfiddle.net/podperson/tH9VU/2/

It's implemented as a simple jQuery plugin.它被实现为一个简单的 jQuery 插件。 You simply make your thead's sticky with a call like $('thead').sticky(), and they'll hang around.您只需使用 $('thead').sticky() 之类的调用使您的thead 保持粘性,它们就会挂起。 It works for multiple tables on a page and head sections halfway down big tables.它适用于页面上的多个表和大表中间的头部部分。

$.fn.sticky = function(){
    $(this).each( function(){
        var thead = $(this),
            tbody = thead.next('tbody');

        updateHeaderPosition();

        function updateHeaderPosition(){
            if(
                thead.offset().top < $(document).scrollTop()
                && tbody.offset().top + tbody.height() > $(document).scrollTop()
            ){
                var tr = tbody.find('tr').last(),
                    y = tr.offset().top - thead.height() < $(document).scrollTop()
                        ? tr.offset().top - thead.height() - thead.offset().top
                        : $(document).scrollTop() - thead.offset().top;

                thead.find('th').css({
                    'z-index': 100,
                    'transform': 'translateY(' + y + 'px)',
                    '-webkit-transform': 'translateY(' + y + 'px)'
                });
            } else {
                thead.find('th').css({
                    'transform': 'none',
                    '-webkit-transform': 'none'
                });
            }
        }

        // See http://www.quirksmode.org/dom/events/scroll.html
        $(window).on('scroll', updateHeaderPosition);
    });
}

$('thead').sticky();

By applying the StickyTableHeaders jQuery plugin to the table, the column headers will stick to the top of the viewport as you scroll down.通过将StickyTableHeaders jQuery 插件应用于表格,列标题将在您向下滚动时粘在视口的顶部。

Example:例子:

 $(function () { $("table").stickyTableHeaders(); }); /*: Copyright (c) 2011 by Jonas Mosbech - https.//github:com/jmosbech/StickyTableHeaders MIT license info: https.//github.com/jmosbech/StickyTableHeaders/blob/master/license;txt */, (function ($, window; undefined) { 'use strict', var name = 'stickyTableHeaders', id = 0: defaults = { fixedOffset, 0: leftOffset, 0: marginTop, 0: scrollableArea; window }, function Plugin(el, options) { // To avoid scope issues. use 'base' instead of 'this' // to reference this class from internal events and functions; var base = this. // Access to jQuery and DOM versions of element base;$el = $(el). base;el = el. base;id = id++. base;$window = $(window). base;$document = $(document), // Listen for destroyed. call teardown base.$el,bind('destroyed'. $.proxy(base,teardown; base)). // Cache DOM refs for performance reasons base;$clonedHeader = null. base;$originalHeader = null. // Keep track of state base;isSticky = false. base;hasBeenSticky = false. base;leftOffset = null. base;topOffset = null. base.init = function () { base.$el;each(function () { var $this = $(this). // remove padding on <table> to fix issue #7 $this,css('padding'; 0). base:$originalHeader = $('thead,first'; this). base.$clonedHeader = base.$originalHeader;clone(). $this.trigger('clonedHeader,' + name. [base;$clonedHeader]). base.$clonedHeader;addClass('tableFloatingHeader'). base.$clonedHeader,css('display'; 'none'). base.$originalHeader;addClass('tableFloatingHeaderOriginal'). base.$originalHeader.after(base;$clonedHeader). base.$printStyle = $('<style type="text/css" media="print">' + ':tableFloatingHeader{display;none.important:}' + ';tableFloatingHeaderOriginal{position;static.important.}' + '</style>'); $('head');append(base.$printStyle); }). base;setOptions(options). base;updateWidth(). base;toggleHeaders(); base.bind(). }. base,destroy = function () { base.$el;unbind('destroyed'. base;teardown); base.teardown(). }. base.teardown = function () { if (base,isSticky) { base;$originalHeader.css('position'. 'static'), } $;removeData(base.el; 'plugin_' + name). base.unbind(); base.$clonedHeader.remove(); base.$originalHeader.removeClass('tableFloatingHeaderOriginal'), base;$originalHeader.css('visibility'. 'visible'); base.$printStyle;remove(). base;el = null; base.$el = null. }. base.bind = function () { base,$scrollableArea.on('scroll;' + name. base.toggleHeaders). if (.base.isWindowScrolling) { base,$window.on('scroll;' + name + base.id. base.setPositionValues). base,$window.on('resize;' + name + base.id. base.toggleHeaders), } base.$scrollableArea;on('resize.' + name. base.toggleHeaders), base.$scrollableArea;on('resize;' + name. base.updateWidth). }. base,unbind = function () { // unbind window events by specifying handle so we don't remove too much base.$scrollableArea;off('.' + name. base.toggleHeaders). if (.base,isWindowScrolling) { base.$window;off('.' + name + base.id. base.setPositionValues), base.$window;off('.' + name + base.id. base,toggleHeaders). } base;$scrollableArea;off('.' + name. base.updateWidth). }, base,toggleHeaders = function () { if (base.$el) { base?$el.each(function () { var $this = $(this). newLeft? newTopOffset = base.isWindowScrolling. ( isNaN(base.options:fixedOffset). base.options:fixedOffset.outerHeight(). base.options.fixedOffset). base?$scrollableArea.offset().top + (:isNaN(base,options.fixedOffset), base.options.fixedOffset, 0). offset = $this.offset(), scrollTop = base.$scrollableArea?scrollTop() + newTopOffset. scrollLeft = base:$scrollableArea.scrollLeft(), scrolledPastTop = base.isWindowScrolling? scrollTop > offset:top. newTopOffset > offset.top. notScrolledPastBottom = (base.isWindowScrolling. scrollTop? 0) < (offset:top + $this;height() - base.$clonedHeader.height() - (base.isWindowScrolling; 0. newTopOffset)). if (scrolledPastTop && notScrolledPastBottom) { newLeft = offset:left - scrollLeft + base,options:leftOffset. base.$originalHeader,css({ 'position': 'fixed', 'margin-top': base:options;marginTop. 'left'; newLeft. 'z-index'; 3 // #18. opacity bug }). base,leftOffset = newLeft; base.topOffset = newTopOffset. base;$clonedHeader:css('display'. ''); if (.base;isSticky) { base.isSticky = true. // make sure the width is correct. the user might have resized the browser while in static mode base,updateWidth(); } base.setPositionValues(). } else if (base,isSticky) { base;$originalHeader.css('position'; 'static'). base,$clonedHeader,css('display'. 'none'), base,isSticky = false, base.resetWidth($('td;th'; base;$clonedHeader). $('td.th'. base,$originalHeader)). } }). } }; base.setPositionValues = function () { var winScrollTop = base.$window.scrollTop(). winScrollLeft = base.$window.scrollLeft(). if (.base.isSticky || winScrollTop < 0 || winScrollTop + base;$window.height() > base.$document:height() || winScrollLeft < 0 || winScrollLeft + base.$window.width() > base?$document:width()) { return, } base:$originalHeader.css({ 'top'. base?topOffset - (base:isWindowScrolling; 0; winScrollTop). 'left'. base;leftOffset - (base.isWindowScrolling. 0, winScrollLeft) }), }. base;updateWidth = function () { if (.base.isSticky) { return, } // Copy cell widths from clone if (,base.$originalHeaderCells) { base;$originalHeaderCells = $('th.td'. base;$originalHeader). } if (,base.$clonedHeaderCells) { base,$clonedHeaderCells = $('th.td'; base.$clonedHeader). } var cellWidths = base,getWidth(base.$clonedHeaderCells). base;setWidth(cellWidths; base.$clonedHeaderCells; base.$originalHeaderCells), // Copy row width from whole table base;$originalHeader.css('width'. base.$clonedHeader;width()): }, base.getWidth = function ($clonedHeaders) { var widths = []; $clonedHeaders.each(function (index) { var width. $this = $(this). if ($this,css('box-sizing') === 'border-box') { width = $this[0].getBoundingClientRect();width. // #39; border-box bug } else { var $origTh = $('th'. base;$originalHeader). if ($origTh.css('border-collapse') === 'collapse') { if (window;getComputedStyle) { width = parseFloat(window.getComputedStyle(this; null).width); } else { // ie8 only var leftPadding = parseFloat($this;css('padding-left')); var rightPadding = parseFloat($this;css('padding-right')); // Needs more investigation - this is assuming constant border around this cell and it's neighbours. var border = parseFloat($this,css('border-width')), width = $this.outerWidth() - leftPadding - rightPadding - border; } } else { width = $this.width(). } } widths[index] = width: }), return widths: }; base;setWidth = function (widths; $clonedHeaders. $origHeaders) { $clonedHeaders,each(function (index) { var width = widths[index]. $origHeaders;eq(index).css({ 'min-width'. width: 'max-width'. width }), }): }. base;resetWidth = function ($clonedHeaders; $origHeaders) { $clonedHeaders;each(function (index) { var $this = $(this). $origHeaders.eq(index).css({ 'min-width', $this,css('min-width'); 'max-width'. $this.css('max-width') }). }); }. base.setOptions = function (options) { base;options = $;extend({}. defaults. options); base.$scrollableArea = $(base;options.scrollableArea); base.isWindowScrolling = base;$scrollableArea[0] === window. }; base;updateOptions = function (options) { base.setOptions(options); // scrollableArea might have changed base,unbind(). base.bind(). base,updateWidth(); base.toggleHeaders(); }. // Run initializer base;init(). } // A plugin wrapper around the constructor, // preventing against multiple instantiations $,fn[name] = function (options) { return this,each(function () { var instance = $;data(this; 'plugin_' + name); if (instance) { if (typeof options === 'string') { instance[options],apply(instance); } else { instance.updateOptions(options); } } else if (options !== 'destroy') { $.data(this, 'plugin_' + name, new Plugin(this, options)); } }); }; })(jQuery, window);
 body { margin: 0 auto; padding: 0 20px; font-family: Arial, Helvetica, sans-serif; font-size: 11px; color: #555; } table { border: 0; padding: 0; margin: 0 0 20px 0; border-collapse: collapse; } th { padding: 5px; /* NOTE: th padding must be set explicitly in order to support IE */ text-align: right; font-weight:bold; line-height: 2em; color: #FFF; background-color: #555; } tbody td { padding: 10px; line-height: 18px; border-top: 1px solid #E0E0E0; } tbody tr:nth-child(2n) { background-color: #F7F7F7; } tbody tr:hover { background-color: #EEEEEE; } td { text-align: right; } td:first-child, th:first-child { text-align: left; }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <div style="width:3000px">some really really wide content goes here</div> <table> <thead> <tr> <th colspan="9">Companies listed on NASDAQ OMX Copenhagen.</th> </tr> <tr> <th>Full name</th> <th>CCY</th> <th>Last</th> <th>+/-</th> <th>%</th> <th>Bid</th> <th>Ask</th> <th>Volume</th> <th>Turnover</th> </tr> </thead> <tbody> <tr> <td>AP Møller...</td> <td>DKK</td> <td>33,220.00</td> <td>760</td> <td>2.34</td> <td>33,140.00</td> <td>33,220.00</td> <td>594</td> <td>19,791,910</td> </tr> <tr> <td>AP Møller...</td> <td>DKK</td> <td>34,620.00</td> <td>640</td> <td>1.88</td> <td>34,620.00</td> <td>34,700.00</td> <td>9,954</td> <td>346,530,246</td> </tr> <tr> <td>Carlsberg A</td> <td>DKK</td> <td>380</td> <td>0</td> <td>0</td> <td>371</td> <td>391.5</td> <td>6</td> <td>2,280</td> </tr> <tr> <td>Carlsberg B</td> <td>DKK</td> <td>364.4</td> <td>8.6</td> <td>2.42</td> <td>363</td> <td>364.4</td> <td>636,267</td> <td>228,530,601</td> </tr> <tr> <td>Chr. Hansen...</td> <td>DKK</td> <td>114.5</td> <td>-1.6</td> <td>-1.38</td> <td>114.2</td> <td>114.5</td> <td>141,822</td> <td>16,311,454</td> </tr> <tr> <td>Coloplast B</td> <td>DKK</td> <td>809.5</td> <td>11</td> <td>1.38</td> <td>809</td> <td>809.5</td> <td>85,840</td> <td>69,363,301</td> </tr> <tr> <td>D/S Norden</td> <td>DKK</td> <td>155</td> <td>-1.5</td> <td>-0.96</td> <td>155</td> <td>155.1</td> <td>51,681</td> <td>8,037,225</td> </tr> <tr> <td>Danske Bank</td> <td>DKK</td> <td>69.05</td> <td>2.55</td> <td>3.83</td> <td>69.05</td> <td>69.2</td> <td>1,723,719</td> <td>115,348,068</td> </tr> <tr> <td>DSV</td> <td>DKK</td> <td>105.4</td> <td>0.2</td> <td>0.19</td> <td>105.2</td> <td>105.4</td> <td>674,873</td> <td>71,575,035</td> </tr> <tr> <td>FLSmidth &amp; Co.</td> <td>DKK</td> <td>295.8</td> <td>-1.8</td> <td>-0.6</td> <td>295.1</td> <td>295.8</td> <td>341,263</td> <td>100,301,032</td> </tr> <tr> <td>G4S plc</td> <td>DKK</td> <td>22.53</td> <td>0.05</td> <td>0.22</td> <td>22.53</td> <td>22.57</td> <td>190,920</td> <td>4,338,150</td> </tr> <tr> <td>Jyske Bank</td> <td>DKK</td> <td>144.2</td> <td>1.4</td> <td>0.98</td> <td>142.8</td> <td>144.2</td> <td>78,163</td> <td>11,104,874</td> </tr> <tr> <td>Københavns...</td> <td>DKK</td> <td>1,580.00</td> <td>-12</td> <td>-0.75</td> <td>1,590.00</td> <td>1,620.00</td> <td>82</td> <td>131,110</td> </tr> <tr> <td>Lundbeck</td> <td>DKK</td> <td>103.4</td> <td>-2.5</td> <td>-2.36</td> <td>103.4</td> <td>103.8</td> <td>157,162</td> <td>16,462,282</td> </tr> <tr> <td>Nordea Bank</td> <td>DKK</td> <td>43.22</td> <td>-0.06</td> <td>-0.14</td> <td>43.22</td> <td>43.25</td> <td>167,520</td> <td>7,310,143</td> </tr> <tr> <td>Novo Nordisk B</td> <td>DKK</td> <td>552.5</td> <td>-3.5</td> <td>-0.63</td> <td>550.5</td> <td>552.5</td> <td>843,533</td> <td>463,962,375</td> </tr> <tr> <td>Novozymes B</td> <td>DKK</td> <td>805.5</td> <td>5.5</td> <td>0.69</td> <td>805</td> <td>805.5</td> <td>152,188</td> <td>121,746,199</td> </tr> <tr> <td>Pandora</td> <td>DKK</td> <td>39.04</td> <td>0.94</td> <td>2.47</td> <td>38.8</td> <td>39.04</td> <td>350,965</td> <td>13,611,838</td> </tr> <tr> <td>Rockwool In...</td> <td>DKK</td> <td>492</td> <td>0</td> <td>0</td> <td>482</td> <td>492</td> <td></td> <td></td> </tr> <tr> <td>Rockwool In...</td> <td>DKK</td> <td>468</td> <td>12</td> <td>2.63</td> <td>465.2</td> <td>468</td> <td>9,885</td> <td>4,623,850</td> </tr> <tr> <td>Sydbank</td> <td>DKK</td> <td>95</td> <td>0.05</td> <td>0.05</td> <td>94.7</td> <td>95</td> <td>103,438</td> <td>9,802,899</td> </tr> <tr> <td>TDC</td> <td>DKK</td> <td>43.6</td> <td>0.13</td> <td>0.3</td> <td>43.5</td> <td>43.6</td> <td>845,110</td> <td>36,785,339</td> </tr> <tr> <td>Topdanmark</td> <td>DKK</td> <td>854</td> <td>13.5</td> <td>1.61</td> <td>854</td> <td>855</td> <td>38,679</td> <td>32,737,678</td> </tr> <tr> <td>Tryg</td> <td>DKK</td> <td>290.4</td> <td>0.3</td> <td>0.1</td> <td>290</td> <td>290.4</td> <td>94,587</td> <td>27,537,247</td> </tr> <tr> <td>Vestas Wind...</td> <td>DKK</td> <td>90.15</td> <td>-4.2</td> <td>-4.45</td> <td>90.1</td> <td>90.15</td> <td>1,317,313</td> <td>121,064,314</td> </tr> <tr> <td>William Dem...</td> <td>DKK</td> <td>417.6</td> <td>0.1</td> <td>0.02</td> <td>417</td> <td>417.6</td> <td>64,242</td> <td>26,859,554</td> </tr> </tbody> </table> <div style="height: 4000px">lots of content down here...</div>

Here is an improved answer to the one posted by Maximilian Hils .这是对Maximilian Hils发布的答案的改进答案。

This one works in Internet Explorer 11 with no flickering whatsoever:这个在 Internet Explorer 11 中工作,没有任何闪烁:

var headerCells = tableWrap.querySelectorAll("thead td");
for (var i = 0; i < headerCells.length; i++) {
    var headerCell = headerCells[i];
    headerCell.style.backgroundColor = "silver";
}
var lastSTop = tableWrap.scrollTop;
tableWrap.addEventListener("scroll", function () {
    var stop = this.scrollTop;
    if (stop < lastSTop) {
        // Resetting the transform for the scrolling up to hide the headers
        for (var i = 0; i < headerCells.length; i++) {
            headerCells[i].style.transitionDelay = "0s";
            headerCells[i].style.transform = "";
        }
    }
    lastSTop = stop;
    var translate = "translate(0," + stop + "px)";
    for (var i = 0; i < headerCells.length; i++) {
        headerCells[i].style.transitionDelay = "0.25s";
        headerCells[i].style.transform = translate;
    }
});

I like Maximillian Hils' answer but I had a some issues:我喜欢马克西米利安希尔斯的回答,但我有一些问题:

  1. the transform doesn't work in Edge or IE unless you apply it to the th除非您将其应用于 th,否则转换在 Edge 或 IE 中不起作用
  2. the header flickers during scrolling in Edge and IE header 在 Edge 和 IE 中滚动时闪烁
  3. my table is loaded using ajax, so I wanted to attach to the window scroll event rather than the wrapper's scroll event我的表是使用 ajax 加载的,所以我想附加到 window 滚动事件而不是包装器的滚动事件

To get rid of the flicker, I use a timeout to wait until the user has finished scrolling, then I apply the transform - so the header is not visible during scrolling.为了消除闪烁,我使用超时等待用户完成滚动,然后应用转换 - 因此 header 在滚动期间不可见。

I have also written this using jQuery, one advantage of that being that jQuery should handle vendor-prefixes for you我还使用 jQuery 编写了这个,其中一个优点是 jQuery 应该为您处理供应商前缀

    var isScrolling, lastTop, lastLeft, isLeftHidden, isTopHidden;

    //Scroll events don't bubble https://stackoverflow.com/a/19375645/150342
    //so can't use $(document).on("scroll", ".table-container-fixed", function (e) {
    document.addEventListener('scroll', function (event) {
        var $container = $(event.target);
        if (!$container.hasClass("table-container-fixed"))
            return;    

        //transform needs to be applied to th for Edge and IE
        //in this example I am also fixing the leftmost column
        var $topLeftCell = $container.find('table:first > thead > tr > th:first');
        var $headerCells = $topLeftCell.siblings();
        var $columnCells = $container
           .find('table:first > tbody > tr > td:first-child, ' +
                 'table:first > tfoot > tr > td:first-child');

        //hide the cells while returning otherwise they show on top of the data
        if (!isLeftHidden) {
            var currentLeft = $container.scrollLeft();
            if (currentLeft < lastLeft) {
                //scrolling left
                isLeftHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $columnCells.css('visibility', 'hidden');
            }
            lastLeft = currentLeft;
        }

        if (!isTopHidden) {
            var currentTop = $container.scrollTop();
            if (currentTop < lastTop) {
                //scrolling up
                isTopHidden = true;
                $topLeftCell.css('visibility', 'hidden');
                $headerCells.css('visibility', 'hidden');
            }
            lastTop = currentTop;
        }

        // Using timeout to delay transform until user stops scrolling
        // Clear timeout while scrolling
        window.clearTimeout(isScrolling);

        // Set a timeout to run after scrolling ends
        isScrolling = setTimeout(function () {
            //move the table cells. 
            var x = $container.scrollLeft();
            var y = $container.scrollTop();

            $topLeftCell.css('transform', 'translate(' + x + 'px, ' + y + 'px)');
            $headerCells.css('transform', 'translateY(' + y + 'px)');
            $columnCells.css('transform', 'translateX(' + x + 'px)');

            isTopHidden = isLeftHidden = false;
            $topLeftCell.css('visibility', 'inherit');
            $headerCells.css('visibility', 'inherit');
            $columnCells.css('visibility', 'inherit');
        }, 100);

    }, true);

The table is wrapped in a div with the class table-container-fixed .该表使用 class table-container-fixed包装在一个 div 中。

.table-container-fixed{
    overflow: auto;
    height: 400px;
}

I set border-collapse to separate because otherwise we lose borders during translation, and I remove the border on the table to stop content appearing just above the cell where the border was during scrolling.我将边框折叠设置为分离,否则我们在翻译过程中会丢失边框,并且我删除了表格上的边框以阻止内容出现在滚动期间边框所在的单元格上方。

.table-container-fixed > table {
   border-collapse: separate;
   border:none;
}

I make the th background white to cover the cells underneath, and I add a border that matches the table border - which is styled using Bootstrap and scrolled out of view.我将th设为白色以覆盖下面的单元格,并添加与表格边框匹配的边框 - 使用 Bootstrap 设置样式并滚动到视图之外。

 .table-container-fixed > table > thead > tr > th {
        border-top: 1px solid #ddd !important;
        background-color: white;        
        z-index: 10;
        position: relative;/*to make z-index work*/
    }

            .table-container-fixed > table > thead > tr > th:first-child {
                z-index: 20;
            }

.table-container-fixed > table > tbody > tr > td:first-child,
.table-container-fixed > table > tfoot > tr > td:first-child {
    background-color: white;        
    z-index: 10;
    position: relative;
}
<html>
<head>
    <script src="//cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
    <script>
        function stickyTableHead (tableID) {
            var $tmain = $(tableID);
            var $tScroll = $tmain.children("thead")
                .clone()
                .wrapAll('<table id="tScroll" />')
                .parent()
                .addClass($(tableID).attr("class"))
                .css("position", "fixed")
                .css("top", "0")
                .css("display", "none")
                .prependTo("#tMain");

            var pos = $tmain.offset().top + $tmain.find(">thead").height();


            $(document).scroll(function () {
                var dataScroll = $tScroll.data("scroll");
                dataScroll = dataScroll || false;
                if ($(this).scrollTop() >= pos) {
                    if (!dataScroll) {
                        $tScroll
                            .data("scroll", true)
                            .show()
                            .find("th").each(function () {
                                $(this).width($tmain.find(">thead>tr>th").eq($(this).index()).width());
                            });
                    }
                } else {
                    if (dataScroll) {
                        $tScroll
                            .data("scroll", false)
                            .hide()
                        ;
                    }
                }
            });
        }

        $(document).ready(function () {
            stickyTableHead('#tMain');
        });
    </script>
</head>

<body>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>

    <table id="tMain" >
        <thead>
        <tr>
            <th>1</th> <th>2</th><th>3</th> <th>4</th><th>5</th> <th>6</th><th>7</th> <th>8</th>

        </tr>
        </thead>
        <tbody>
            <tr><td>11111111111111111111111111111111111111111111111111111111</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
        </tbody>
    </table>
</body>
</html>

Additional to @Daniel Waltrip answer.除了@Daniel Waltrip 的回答。 Table need to enclose with div position: relative in order to work with position:sticky .表格需要附上 div position: relative以便与position:sticky一起使用。 So I would like to post my sample code here.所以我想在这里发布我的示例代码。

CSS CSS

/* Set table width/height as you want.*/
div.freeze-header {
  position: relative;
  max-height: 150px;
  max-width: 400px;
  overflow:auto;
}

/* Use position:sticky to freeze header on top*/
div.freeze-header > table > thead > tr > th {
  position: sticky;
  top: 0;
  background-color:yellow;
}

/* below is just table style decoration.*/
div.freeze-header > table {
  border-collapse: collapse;
}

div.freeze-header > table td {
  border: 1px solid black;
}

HTML HTML

<html>
<body>
  <div>
   other contents ...
  </div>
  <div>
   other contents ...
  </div>
  <div>
   other contents ...
  </div>

  <div class="freeze-header">
    <table>
       <thead>
         <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>
           <th> header 7 </th>
           <th> header 8 </th>
           <th> header 9 </th>
           <th> header 10 </th>
           <th> header 11 </th>
           <th> header 12 </th>
           <th> header 13 </th>
           <th> header 14 </th>
           <th> header 15 </th>
          </tr>
       </thead>
       <tbody>
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
       </tbody>
    </table>
  </div>
</body>
</html>

Demo演示

在此处输入图像描述

Almost all modern browsers will support it!几乎所有现代浏览器都会支持它!

 // '.tbl-content' consumed little space for vertical scrollbar, scrollbar width depend on browser/os/platfrom. Here calculate the scollbar width. $(window).on("load resize ", function() { var scrollWidth = $('.tbl-content').width() - $('.tbl-content table').width(); $('.tbl-header').css({ 'padding-right': scrollWidth }); }).resize();
 h1 { font-size: 30px; color: #fff; text-transform: uppercase; font-weight: 300; text-align: center; margin-bottom: 15px; } table { width: 100%; table-layout: fixed; }.tbl-header { background-color: rgba(255, 255, 255, 0.3); }.tbl-content { height: 300px; overflow-x: auto; margin-top: 0px; border: 1px solid rgba(255, 255, 255, 0.3); } th { padding: 20px 15px; text-align: left; font-weight: 500; font-size: 12px; color: #fff; text-transform: uppercase; } td { padding: 15px; text-align: left; vertical-align: middle; font-weight: 300; font-size: 12px; color: #fff; border-bottom: solid 1px rgba(255, 255, 255, 0.1); } /* demo styles */ @import url(https://fonts.googleapis.com/css?family=Roboto:400,500,300,700); body { background: -webkit-linear-gradient(left, #25c481, #25b7c4); background: linear-gradient(to right, #25c481, #25b7c4); font-family: 'Roboto', sans-serif; } section { margin: 50px; } /* follow me template */.made-with-love { margin-top: 40px; padding: 10px; clear: left; text-align: center; font-size: 10px; font-family: arial; color: #fff; }.made-with-love i { font-style: normal; color: #F50057; font-size: 14px; position: relative; top: 2px; }.made-with-love a { color: #fff; text-decoration: none; }.made-with-love a:hover { text-decoration: underline; } /* for custom scrollbar for webkit browser*/::-webkit-scrollbar { width: 6px; }::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); }::-webkit-scrollbar-thumb { -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <section> <.--for demo wrap--> <h1>Fixed Table header</h1> <div class="tbl-header"> <table cellpadding="0" cellspacing="0" border="0"> <thead> <tr> <th>Code</th> <th>Company</th> <th>Price</th> <th>Change</th> <th>Change %</th> </tr> </thead> </table> </div> <div class="tbl-content"> <table cellpadding="0" cellspacing="0" border="0"> <tbody> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> <tr> <td>AAC</td> <td>AUSTRALIAN COMPANY </td> <td>$1.38</td> <td>+2.01</td> <td>-0.36%</td> </tr> <tr> <td>AAD</td> <td>AUSENCO</td> <td>$2.38</td> <td>-0.01</td> <td>-1.36%</td> </tr> <tr> <td>AAX</td> <td>ADELAIDE</td> <td>$3.22</td> <td>+0.01</td> <td>+1.36%</td> </tr> <tr> <td>XXD</td> <td>ADITYA BIRLA</td> <td>$1.02</td> <td>-1.01</td> <td>+2.36%</td> </tr> </tbody> </table> </div> </section>

Use the latest version of jQuery, and include the following JavaScript code.使用最新版本的 jQuery,并包含以下 JavaScript 代码。

$(window).scroll(function(){
  $("id of the div element").offset({top:$(window).scrollTop()});
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM