vertical thead tbody tabla horizontal hacer fijo encabezado dinamico con bootstrap javascript css html-table

javascript - thead - ¿Tabla HTML con encabezados fijos?



tabla encabezado fijo html (27)

¿Existe una técnica de CSS / JavaScript en todos los navegadores para mostrar una tabla HTML larga de manera que los encabezados de las columnas permanezcan fijos en la pantalla y no se desplacen con el cuerpo de la tabla? Piense en el efecto de "congelar paneles" en Microsoft Excel.

Quiero poder desplazarme por el contenido de la tabla, pero siempre poder ver los encabezados de las columnas en la parte superior.


TL; DR

Si te diriges a los navegadores modernos y no tienes necesidades de estilo extravagantes: http://jsfiddle.net/dPixie/byB9d/3/ ... Aunque la versión de los cuatro grandes es bastante buena, esta versión maneja el ancho del fluido mucho mejor.

¡Buenas noticias para todos!

Con los avances de HTML5 y CSS3 esto ahora es posible, al menos para los navegadores modernos. La implementación ligeramente intrincada que se me ocurrió se puede encontrar aquí: http://jsfiddle.net/dPixie/byB9d/3/ . Lo he probado en FX 25, Chrome 31 e IE 10 ...

HTML relevante (inserte un doctype HTML5 en la parte superior de su documento):

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

Con este CSS:

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

¡¿Pero cómo?!

En pocas palabras, tiene un encabezado de tabla que oculta visualmente haciéndolo de 0px de altura, que también contiene divs utilizados como encabezado fijo. El contenedor de la mesa deja suficiente espacio en la parte superior para permitir el encabezado absolutamente posicionado, y la tabla con barras de desplazamiento aparece como cabría esperar.

El código anterior utiliza la clase posicionada para posicionar la tabla en forma absoluta (lo estoy usando en un cuadro de diálogo de estilo emergente) pero también puede usarlo en el flujo del documento al eliminar la clase positioned del contenedor.

Pero ...

No es perfecto Firefox se niega a hacer que la fila del encabezado sea 0px (al menos no encontré ninguna manera) pero obstinadamente lo mantiene a un mínimo de 4px ... No es un gran problema, pero dependiendo de su estilo, tendrá problemas con sus bordes, etc.

La tabla también utiliza un enfoque de columna falsa donde el color de fondo del contenedor se utiliza como fondo para los divs de encabezado, que son transparentes.

Resumen

En general, puede haber problemas de estilo según sus requisitos, especialmente los bordes o fondos complicados. También podría haber problemas con la computabilidad, no lo he comprobado en una gran variedad de navegadores todavía (por favor, comente con sus experiencias si lo prueba), pero no encontré nada parecido, así que pensé que valía la pena publicar de todas formas ...


Un simple plugin de jQuery.

Esta es una variación de la solución de Mahes. Puedes llamarlo como $(''table#foo'').scrollableTable();

La idea es:

  • Dividir el thead y el thead en elementos de table separados
  • Haz que sus anchos de celda coincidan de nuevo
  • Envuelve la segunda table en un div.scrollable
  • Usa CSS para hacer que div.scrollable realmente se desplace

El CSS podría ser:

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

Advertencias

  • Obviamente, dividir estas tablas hace que el marcado sea menos semántico. No estoy seguro de qué efecto tiene esto en la accesibilidad.
  • Este complemento no trata con pies de página, encabezados múltiples, etc.
  • Sólo lo he probado en Chrome versión 20.

Dicho esto, funciona para mis propósitos y usted es libre de tomarlo y modificarlo.

Aquí está el 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); };


Esto se puede resolver limpiamente en cuatro líneas de código.

Si solo te interesan los navegadores modernos, un encabezado fijo se puede lograr mucho más fácilmente mediante el uso de transformaciones CSS. Suena extraño, pero funciona muy bien:

  • HTML y CSS permanecen como están.
  • No hay dependencias externas de JavaScript.
  • Cuatro líneas de código.
  • Funciona para todas las configuraciones (table-layout: fix, etc.).

document.getElementById("wrap").addEventListener("scroll", function(){ var translate = "translate(0,"+this.scrollTop+"px)"; this.querySelector("thead").style.transform = translate; });

El soporte para transformaciones CSS está ampliamente disponible, excepto para Internet Explorer 8-.

Aquí está el ejemplo completo para referencia:

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>


Soporte para pie fijo

Extendí la función de Nathan para que también soporte un pie fijo y una altura máxima. Además, la función establecerá el CSS en sí, y solo tendrá que admitir un ancho.

Uso:

Altura fija:

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

Altura máxima (si el navegador admite la opción ''max-height'' de CSS):

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

Guión:

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(); };


Una tabla de desplazamiento de CSS puro más refinado.

Todas las soluciones CSS puras que he visto hasta ahora, aunque pueden ser inteligentes, carecen de un cierto nivel de pulido, o simplemente no funcionan bien en algunas situaciones. Entonces, decidí crear mi propia ...

caracteristicas:

  • Es CSS puro, por lo que no se requiere jQuery (o cualquier código JavaScript, para el caso)
  • Puede establecer el ancho de la tabla en un porcentaje (también conocido como "fluido") o en un valor fijo, o dejar que el contenido determine su ancho (también conocido como "automático")
  • Los anchos de columna también pueden ser fluidos, fijos o automáticos.
  • Las columnas nunca se desalinearán con los encabezados debido al desplazamiento horizontal (un problema que ocurre en todas las demás soluciones basadas en CSS que he visto que no requieren anchos fijos).
  • Compatible con todos los navegadores de escritorio populares, incluido Internet Explorer a la versión 8
  • Aspecto limpio, pulido; sin espacios vacíos de 1 píxel ni bordes desalineados; se ve igual en todos los navegadores

Aquí hay un par de violines que muestran las opciones de ancho fluido y automático:

  • Ancho y altura fluidos (se adapta al tamaño de la pantalla): jsFiddle ( jsFiddle cuenta que la barra de desplazamiento solo se muestra cuando es necesario en esta configuración, por lo que es posible que tenga que reducir el marco para verla)

  • Ancho automático, Altura fija (más fácil de integrar con otro contenido): jsFiddle

La configuración de Ancho automático, Altura fija probablemente tenga más casos de uso, por lo que publicaré el código a continuación.

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

El método que utilicé para congelar la fila del encabezado es similar al de d-Pixie, así que consulta su publicación para obtener una explicación. Hubo una gran cantidad de errores y limitaciones con esa técnica que solo se podía arreglar con montones de CSS adicional y un contenedor de div adicional o dos.


:)

No tan limpia, pero pura solución HTML / CSS.

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

Actualizado para IE8 + JSFiddle ejemplo


Acabo de completar un complemento de jQuery que tomará una tabla única válida con HTML válido (debe tener un thead y tbody) y generará una tabla con encabezados fijos, pie de página fijo opcional que puede ser un encabezado clonado o cualquier otro. Contenido que eligió (paginación, etc.). Si desea aprovechar los monitores más grandes, también cambiará el tamaño de la tabla cuando se cambie el tamaño del navegador. Otra característica adicional es poder desplazarse lateralmente si no se pueden ver todas las columnas de la tabla.

http://fixedheadertable.com/

en github: http://markmalek.github.com/Fixed-Header-Table/

Es extremadamente fácil de configurar y puedes crear tus propios estilos personalizados para él. También utiliza esquinas redondeadas en todos los navegadores. Tenga en cuenta que acabo de lanzarlo, así que todavía es técnicamente beta y hay muy pocos problemas menores que estoy solucionando.

Funciona en Internet Explorer 7, Internet Explorer 8, Safari, Firefox y Chrome.


Aquí hay un plugin de jQuery para encabezados de tabla fijos. Permite que toda la página se desplace, congelando el encabezado cuando llega a la parte superior. Funciona bien con las tablas de Twitter Bootstrap .

Repositorio de GitHub: https://github.com/oma/table-fixed-header

No desplaza solo el contenido de la tabla. Busque otras herramientas para eso, como una de estas otras respuestas. Tú decides lo que mejor se adapte a tu caso.


Estuve buscando una solución para esto por un tiempo y encontré que la mayoría de las respuestas no funcionan o no son adecuadas para mi situación, así que escribí una solución simple con jQuery.

Este es el esquema de la solución:

  1. Clone la tabla que necesita tener un encabezado fijo y coloque la copia clonada sobre el original.
  2. Retire el cuerpo de la mesa de la mesa superior.
  3. Eliminar el encabezado de la tabla de la tabla inferior.
  4. Ajustar los anchos de columna. (Realizamos un seguimiento de los anchos de columna originales)

A continuación se muestra el código en una demostración ejecutable.

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>

Esta solución funciona en Chrome e IE. Como se basa en jQuery, esto también debería funcionar en otros navegadores compatibles con jQuery.


La position: sticky propiedad CSS position: sticky tiene un gran soporte en la mayoría de los navegadores modernos (tuve problemas con Edge, ver más abajo).

Esto nos permite resolver el problema de los encabezados fijos con bastante facilidad:

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

Safari necesita un prefijo de proveedor: -webkit-sticky .

Para Firefox, tuve que agregar min-height: 0 a uno de los elementos principales. Me olvido exactamente por qué esto era necesario.

Desafortunadamente, la implementación de Microsoft Edge parece ser solo semi-funcional. Al menos, tenía algunas celdas de la mesa parpadeando y desalineada en mis pruebas. La mesa todavía era utilizable, pero tenía problemas estéticos significativos.


La mayoría de las soluciones publicadas aquí requieren jQuery. Si está buscando una solución independiente de marco, pruebe Grid: http://www.matts411.com/post/grid/

Se encuentra alojado en Github aquí: https://github.com/mmurph211/Grid

No solo admite encabezados fijos, sino que también admite columnas y pies de página fijos a la izquierda, entre otras cosas.


También he creado un complemento que aborda este problema. Mi proyecto - jQuery.floatThead ha existido durante más de 4 años y es muy maduro.

No requiere estilos externos y no espera que su mesa tenga un estilo particular. Es compatible con Internet Explorer9 + y Firefox / Chrome.

Actualmente (2018-05) tiene:

405 commit y 998 estrellas en GitHub

Muchas (no todas) de las respuestas aquí son trucos rápidos que pueden haber resuelto el problema que tenía una persona, pero no funcionarán para todas las mesas.

Algunos de los otros complementos son antiguos y probablemente funcionen bien con Internet Explorer, pero se interrumpirán en Firefox y Chrome.


Todos los intentos de resolver esto desde fuera de la especificación de CSS son sombras pálidas de lo que realmente queremos: entrega en la promesa implícita de THEAD.

Este problema de encabezados congelados para una mesa ha sido una herida abierta en HTML / CSS durante mucho tiempo.

En un mundo perfecto, habría una solución de CSS puro para este problema. Desafortunadamente, no parece haber uno bueno en su lugar.

Estándares relevantes-discusiones sobre este tema incluyen:

ACTUALIZACIÓN : Firefox envió la position:sticky en la versión 32. ¡Todos ganan!


Desarrollé un sencillo complemento ligero de jQuery para convertir una tabla HTML bien formateada en una tabla desplazable con encabezado de tabla fija y columnas.

El complemento funciona bien para hacer coincidir píxel a píxel colocando la sección fija con la sección desplazable. Además, también puede congelar la cantidad de columnas que estarán siempre a la vista cuando se desplace horizontalmente.

Demostración y documentación: http://meetselva.github.io/fixed-table-rows-cols/

Repositorio de GitHub: https://github.com/meetselva/fixed-table-rows-cols

A continuación se muestra el uso de una tabla simple con un encabezado fijo,

$(<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''} ] });


Desearía haber encontrado la solución de @ Mark antes, pero fui y escribí la mía antes de ver esta pregunta SO ...

El mío es un complemento jQuery muy ligero que admite encabezado fijo, pie de página, expansión de columna (colspan), cambio de tamaño, desplazamiento horizontal y un número opcional de filas para mostrar antes de que comience el desplazamiento.

jQuery.scrollTableBody (GitHub)

Mientras usted tiene una tabla con adecuado <thead>, <tbody>y (opcional) <tfoot>, todo lo que tiene que hacer es la siguiente:

$(''table'').scrollTableBody();


Encontré esta solución: mueva la fila del encabezado en una tabla sobre la tabla con datos:

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


Esta no es una solución exacta para la fila de encabezado fija, pero he creado un método bastante ingenioso para repetir la fila de encabezado en toda la tabla larga, pero aún así tengo la capacidad de ordenar.

Esta opción pequeña y ordenada requiere el complemento jQuerytablesorter . Así es como funciona:

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>

Obviamente, mi tabla tiene muchas más filas que esta. 193 para ser exactos, pero puede ver dónde se repite la fila del encabezado. La fila de encabezado de repetición está configurada por esta función:

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


Para aquellos que probaron la buena solución dada por Maximilian Hils, y no lograron que funcionara con Internet Explorer, tuve el mismo problema (Internet Explorer 11) y descubrí cuál era el problema.

En Internet Explorer 11, la transformación de estilo (al menos con traducir) no funciona <THEAD>. Resolví esto aplicando el estilo a todos <TH>en un bucle. Eso funciono. Mi código de JavaScript se ve así:

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

En mi caso la tabla era un GridView en ASP.NET. Primero pensé que era porque no tenía <THEAD>, pero incluso cuando lo forcé a tener uno, no funcionó. Entonces me enteré de lo que escribí arriba.

Es una solución muy bonita y sencilla. En Chrome es perfecto, en Firefox un poco brusco, y en Internet Explorer aún más brusco. Pero en general una buena solución.


Use la última versión de jQuery e incluya el siguiente código JavaScript.

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


Al aplicar el StickyTableHeaders jQuery de StickyTableHeaders a la tabla, los encabezados de las columnas se pegarán a la parte superior de la ventana gráfica mientras se desplaza hacia abajo.

Ejemplo:

$(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>A.P. 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>A.P. 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>


Aquí hay una respuesta mejorada a la publicada por .

Éste funciona en Internet Explorer 11 sin parpadeo en absoluto:

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


De alguna manera terminé Position:Stickytrabajando bien en mi caso:

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>


Dos divs, uno para el encabezado, uno para los datos. Haga que la división de datos se pueda desplazar y use JavaScript para establecer el ancho de las columnas en el encabezado para que sea el mismo que el ancho de los datos. Creo que los anchos de las columnas de datos deben ser fijos en lugar de dinámicos.


Esta es una solución con la que terminamos trabajando (para solucionar algunos casos de borde y versiones anteriores de Internet Explorer, al final también se desvaneció la barra de título en el desplazamiento y luego se desvaneció cuando finaliza el desplazamiento, pero en los navegadores Firefox y WebKit esta solución simplemente funciona . Supone que se contrae el colapso: colapso.

La clave de esta solución es que una vez que aplica el colapso de bordes , las transformaciones de CSS funcionan en el encabezado, por lo que es solo una cuestión de interceptar los eventos de desplazamiento y configurar la transformación correctamente. No necesitas duplicar nada. Si no se implementa correctamente este comportamiento en el navegador, es difícil imaginar una solución más liviana.

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

Se implementa como un simple complemento de jQuery. Simplemente haga que su thead esté pegajoso con una llamada como $ (''thead''). Sticky (), y se quedarán. Funciona para varias tablas en una página y secciones de cabecera a mitad de camino en tablas grandes.

$.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();


Me doy cuenta de que la pregunta permite JavaScript, pero aquí hay una solución CSS pura que elaboré y que también permite que la tabla se expanda horizontalmente. Fue probado con Internet Explorer 10 y los últimos navegadores Chrome y Firefox. Un enlace a jsFiddle está en la parte inferior.

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

Y el 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/


Me gusta la respuesta de Maximillian Hils pero tuve algunos problemas:

  1. la transformación no funciona en Edge o IE a menos que se aplique a la th
  2. el encabezado parpadea durante el desplazamiento en Edge e IE
  3. mi tabla se carga utilizando ajax, así que quise adjuntar al evento de desplazamiento de la ventana en lugar del evento de desplazamiento del contenedor

Para deshacerme del parpadeo, uso un tiempo de espera para esperar hasta que el usuario haya terminado de desplazarse, luego aplico la transformación, por lo que el encabezado no está visible durante el desplazamiento.

También escribí esto usando jQuery, una ventaja de eso es que jQuery debería manejar prefijos de proveedores para usted

var isScrolling, lastTop, lastLeft, isLeftHidden, isTopHidden; //Scroll events don''t bubble https://.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);

La mesa está envuelta en un div con la clase table-container-fixed.

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

Configuro el colapso del borde para que se separe porque, de lo contrario, perderemos los bordes durante la traducción, y elimino el borde de la tabla para evitar que el contenido aparezca justo encima de la celda donde estaba el borde durante el desplazamiento.

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

El thfondo se vuelve blanco para cubrir las celdas de abajo, y agrego un borde que coincide con el borde de la tabla, que tiene un estilo usando Bootstrap y se desplaza fuera de la vista.

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


Mucha gente parece estar buscando esta respuesta. Lo encontré enterrado en una respuesta a otra pregunta aquí: Sincronizar el ancho de columna de tablas en dos marcos diferentes, etc.

De las docenas de métodos que he probado, este es el único método que encontré que funciona de manera confiable para permitirle tener una tabla inferior con la tabla de encabezado con los mismos anchos.

Así es como lo hice, primero mejoré el jsfiddle de arriba para crear esta función, que funciona en ambos tdy th(en caso de que tropiece con otros que usan thpara el estilo de sus filas de encabezado).

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()); }); }

A continuación, debe crear dos tablas. NOTA: la tabla de encabezado debe tener un extra TDpara dejar espacio en la tabla superior para la barra de desplazamiento, como esto:

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

Entonces haz algo como:

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

Esta es la única solución que encontré en que funciona a partir de muchas preguntas similares que se han publicado, que funciona en todos mis casos.

Por ejemplo, probé el complemento jQuery stickytables que no funciona con durandal, y el proyecto Google Code aquí https://code.google.com/p/js-scroll-table-header/issues/detail?id=2

Otras soluciones que implican la clonación de tablas, tienen un rendimiento deficiente o no funcionan en todos los casos.

No hay necesidad de estas soluciones demasiado complejas. Simplemente haga dos tablas como los ejemplos a continuación y llame a la función setHeaderTableWidth como se describe aquí y boom, listo .

Si esto no funciona para usted, probablemente estaba jugando con su propiedad de tamaño de caja de CSS y necesita configurarlo correctamente. Es fácil arruinar tu contenido CSS por accidente. Hay muchas cosas que pueden salir mal, así que ten cuidado / cuidado. Este enfoque funciona para mí .