是否有跨浏览器的CSS/JavaScript技术来显示一个长HTML表,使列标题保持固定在屏幕上,而不随表体滚动。想想微软Excel中的“冻结窗格”效果。
我希望能够滚动表的内容,但总是能够在顶部看到列标题。
是否有跨浏览器的CSS/JavaScript技术来显示一个长HTML表,使列标题保持固定在屏幕上,而不随表体滚动。想想微软Excel中的“冻结窗格”效果。
我希望能够滚动表的内容,但总是能够在顶部看到列标题。
当前回答
一个更精致的纯CSS滚动表
到目前为止,我所见过的所有纯CSS解决方案——尽管它们可能很聪明——都缺乏一定程度的优化,或者在某些情况下不能正常工作。所以,我决定创建我自己的…
特点:
It's pure CSS, so no jQuery required (or any JavaScript code at all, for that matter) You can set the table width to a percent (a.k.a. "fluid") or a fixed value, or let the content determine its width (a.k.a. "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). Compatible with all of the popular desktop browsers, including Internet Explorer back to version 8 Clean, polished appearance; no sloppy-looking 1-pixel gaps or misaligned borders; looks the same in all browsers
下面是一些显示流体和自动宽度选项的控件:
流体宽度和高度(适应屏幕大小):jsFiddle(注意,在此配置中,滚动条只在需要时显示,因此您可能必须缩小帧才能看到它) 自动宽度,固定高度(更容易与其他内容集成):jsFiddle
自动宽度,固定高度配置可能有更多的用例,所以我将在下面发布代码。
/* 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]-->
我用来冻结标题行的方法与d-Pixie的方法类似,因此请参阅他的帖子以获得解释。该技术存在大量错误和限制,只能通过大量额外的CSS和一两个额外的div容器来修复。
其他回答
我一直在寻找一个解决方案,发现大多数答案都不工作或不适合我的情况,所以我用jQuery编写了一个简单的解决方案。
这是解决方案大纲:
克隆需要固定表头的表,并放置 在原拷贝之上的克隆拷贝。 从主表上拆下表体。 从底部表中删除表头。 调整列的宽度。(我们跟踪原始列的宽度)
下面是一个可运行的演示代码。
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>
该解决方案支持Chrome和IE浏览器。因为它是基于jQuery的,所以在其他jQuery支持的浏览器中也能正常工作。
补充@Daniel Waltrip的回答。表需附以div位置:相对,以便与工作位置:粘。所以我想在这里发布我的示例代码。
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>
<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
博士TL;
如果您的目标是现代浏览器,并且没有奢侈的样式需求:http://jsfiddle.net/dPixie/byB9d/3/…虽然四大版本是相当甜蜜的以及这个版本处理流体宽度好得多。
大家好!
随着HTML5和CSS3的进步,至少对于现代浏览器来说,这是可能的。我提出的稍微有点粗糙的实现可以在这里找到:http://jsfiddle.net/dPixie/byB9d/3/。我已经在FX 25, Chrome 31和IE 10上测试过了…
相关的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>
但是如何? !
简单地说,你有一个表头,你通过使它高0px来隐藏它,它还包含了用作固定表头的div。表的容器在顶部留下了足够的空间来允许绝对定位的标题,并且带有滚动条的表如您所期望的那样出现。
上面的代码使用了定位类来确定表的位置(我在弹出式对话框中使用它),但是您也可以通过从容器中删除定位类来在文档流中使用它。
但是…
它并不完美。Firefox拒绝使标题行为0px(至少我没有找到任何方法),但固执地保持它至少为4px…这不是一个大问题,但取决于你的样式,它会混淆你的边界等。
该表还使用了一种伪列方法,其中容器本身的背景色被用作标题div的背景,这是透明的。
总结
总而言之,根据您的需求,可能会有样式问题,特别是边界或复杂的背景。可计算性可能也有问题,我还没有在各种各样的浏览器中检查它(如果你尝试过,请评论你的经验),但我没有发现任何类似的东西,所以我认为无论如何都值得发布…
不知怎的,我以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>
这是我们最终使用的解决方案(为了处理一些边缘情况和旧版本的Internet Explorer,我们最终也会在滚动时淡出标题栏,然后在滚动结束时重新淡出,但在Firefox和WebKit浏览器中,这个解决方案是有效的。它假设边界崩溃:崩溃。
此解决方案的关键在于,一旦应用了边界折叠,CSS转换就可以在头部上工作,因此只需拦截滚动事件并正确设置转换即可。你不需要复制任何东西。除非在浏览器中正确地实现这种行为,否则很难想象还有更轻量级的解决方案。
JSFiddle: http://jsfiddle.net/podperson/tH9VU/2/
它被实现为一个简单的jQuery插件。你只需要调用像$('thead').sticky()这样的调用来让你的head's sticky,它们就会一直存在。它适用于一个页面上的多个表和大表中间的头部部分。
$.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();