Tabla HTML con encabezados fijos?


Existe una técnica CSS/JavaScript entre navegadores para mostrar una tabla HTML larga de modo 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" congelar paneles " en Microsoft Excel.

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

Author: Termininja, 2009-03-23

27 answers

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 copia clonada encima del original.
  2. Retire el cuerpo de la tabla de la tabla superior.
  3. Elimine el encabezado de la tabla de la tabla inferior.
  4. Ajuste los anchos de columna. (Hacemos un seguimiento de la ancho de columna original)

A continuación se muestra el código de una demo 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. Dado que se basa en jQuery, esto también debería funcionar en otros navegadores compatibles con jQuery.

 82
Author: Mahes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-21 14:43:02

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

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

  • HTML y CSS permanecen como están.
  • No hay dependencias JS externas.
  • 4 líneas de código.
  • Funciona para todas las configuraciones (diseño de tabla: fijo, 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 a excepción de IE8-. 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>
 159
Author: Maximilian Hils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-14 15:38:06

Acabo de completar la creación de un plugin de jQuery que tomará una tabla única válida utilizando HTML válido (tiene que tener un thead y tbody) y dará salida a una tabla que tiene encabezados fijos, pie de página fijo opcional que puede ser un encabezado clonado o cualquier contenido que elija (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 añadida es poder desplazarse de lado si las columnas de la tabla no caben todas vista.

Http://fixedheadertable.com/

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

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

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

 57
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-12 15:24:29

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

No requiere estilos externos y no espera que su tabla tenga un estilo en particular. Es compatible con IE8 + y FF / Chrome.

Actualmente (5/2018) tiene:

405 commits y 998 estrellas en github


Muchas (no todas) de las respuestas aquí son hacks rápidos que pueden haber resuelto el problema una persona estaba teniendo, pero no va a trabajar para cada mesa.

Algunos de los otros plugins son antiguos y probablemente funcionan muy bien con IE, pero se romperán en FF y chrome.

 22
Author: mkoryak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-23 18:58:53

TL; DR

Si te diriges a navegadores modernos y no tienes necesidades de estilo extravagantes: http://jsfiddle.net/dPixie/byB9d/3 / ... Aunque el big four versión es bastante dulce, así que esta versión maneja ancho fluido mucho mejor.

¡Buenas noticias a todos!

Con los avances de HTML5 y CSS3 esto es ahora posible, al menos para los navegadores modernos. La implementación ligeramente hackish 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 (sin embargo, 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?!

Simplemente tiene un encabezado de tabla, que oculta visualmente haciéndolo 0px alto, 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 el tabla con barras de desplazamiento aparecen como era de esperar.

El código anterior usa la clase positioned para posicionar la tabla absolutamente (la estoy usando en un diálogo de estilo emergente), pero también puede usarla en el flujo del documento eliminando la clase positioned del contenedor.

Pero ...

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

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

Resumen

En general, puede haber problemas de estilo dependiendo de sus requisitos, especialmente bordes o fondos complicados. También puede haber problemas con la computabilidad, todavía no lo he comprobado en una amplia variedad de navegadores (por favor, comente con sus experiencias si lo prueba), pero no encontré nada igual, así que pensé que valía la pena publicarlo de todos modos ...

 20
Author: Jonas Schubert Erlandsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-18 09:37:30

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

Este problema de frozen-headers-for-a-table ha sido una herida abierta en HTML/CSS durante mucho tiempo.

En un mundo perfecto, habría una solución pure-css para este problema. Desafortunadamente no parece haber una buena en su lugar.

Estándares relevantes-las discusiones sobre este tema incluyen:

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

 19
Author: djsadinoff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-03 04:56:10

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

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

Hace no desplazarse solo por el contenido de la tabla. Mira a otras herramientas para eso, como una de estas otras respuestas. Tú decides lo que mejor se adapta a tu caso.

 14
Author: oma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-24 21:52:15

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

Está alojado en Github aquí: https://github.com/mmurph211/Grid

No solo admite encabezados fijos, también admite columnas y pies de página izquierdos fijos, entre otras cosas.

 9
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-27 18:37:58

Una Tabla de Desplazamiento CSS Pura Más Refinada

Todas las soluciones CSS puras que he visto hasta ahora, aunque sean inteligentes, carecen de cierto nivel de pulido, o simplemente no funcionan bien en algunas situaciones. Así que decidí crear la mía...

Características:

  • es CSS puro, por lo que no se requiere jQuery (o ningún javascript en absoluto, para eso matter)
  • puede establecer el ancho de la tabla en un porcentaje (también conocido como "fluido") o un valor fijo, o dejar que el contenido determine su ancho (también conocido como "fluido"). "auto")
  • 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, incluyendo Internet Explorer volver a la versión 8
  • aspecto limpio y pulido; sin huecos de 1 píxel de aspecto descuidado o bordes desalineados; se ve igual en todos navegadores

Aquí hay un par de violines que muestran las opciones fluid y auto width:

  • Ancho y alto del fluido (se adapta al tamaño de la pantalla): jsFiddle (Tenga en cuenta que la barra de desplazamiento solo aparece cuando es necesario en esta configuración, por lo que puede que tenga que reducir el marco para verlo)

  • Auto Anchura, Altura Fija (más fácil de integrar con otros contenidos): jsFiddle

El Auto Ancho, Configuración de altura fija probablemente tiene más casos de uso, así que voy a 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 2 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 de encabezado es similar al de d-Pixie, así que consulte su publicación para obtener una explicación. Había una gran cantidad de errores y limitaciones con esa técnica que solo se podían arreglar con montones de CSS adicionales y un contenedor div adicional o dos.

 6
Author: DoctorDestructo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-15 12:59:04

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:

  • Divida los thead y {[4] } en elementos table separados
  • Hacer que sus anchos de celda coincidan de nuevo
  • Envuelva el segundo table en un div.scrollable
  • Use 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ántica. No estoy seguro de qué efecto tiene esto en la accesibilidad.
  • Este plugin no trata con pies de página, múltiples encabezados, etc.
  • Solo lo he probado en Chrome versión 20.

Dicho esto, funciona para mis propósitos y eres 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);
};
 5
Author: Nathan Long,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-05 15:23:45

:)

Solución HTML/CSS no tan limpia, sino pura.

table {
    overflow-x:scroll;
}

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

Actualizado para IE8+ JSFiddle ejemplo

 4
Author: Anton Matyulkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-19 19:40:24

Soporte para pie de página fijo

Extendí la función de Nathan para soportar también un pie de página fijo y una altura máxima. Además, la función establecerá el css en sí, solo tiene que soportar un ancho.

Uso:

Altura fija:

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

Altura máxima (si el navegador soporta la opción css 'altura máxima'):

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

    // grap 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();

};
 3
Author: gitaarik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-27 15:34:37

Dos divs, uno para encabezado, uno para datos. Haga que el div de datos sea desplazable y use JavaScript para establecer que el ancho de las columnas en el encabezado 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.

 2
Author: cjk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-20 18:57:13

Me doy cuenta de que la pregunta permite JavaScript, pero aquí hay una solución CSS pura que trabajé que también permite que la tabla se expanda horizontalmente. Probado con IE10 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 it's 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 /

 1
Author: bzuillsmith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 18:00:28

Para aquellos que probaron la buena solución dada por Maximilian Hils, y no tuvieron éxito en conseguir que funcione con Internet Explorer, tuve el mismo problema (ES decir, 11) y descubrí cuál era el problema.

En IE 11 la transformación de estilo (al menos con translate) no funciona en <THEAD>. Resolví esto aplicando el estilo a todos los <TH> en un bucle. Eso funcionó. Mi 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 obligué a tener uno, no funcionó. Entonces descubrí lo que escribí arriba.

Es una solución muy agradable y simple. En Chrome es perfecto, en Firefox un poco desigual, y en IE aún más desigual. Pero en general una buena solución.

 1
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 16:02:50

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

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 padre. Olvidé exactamente por qué era necesario.

Desafortunadamente, la implementación de Microsoft Edge parece estar solo semi-trabajando. Al menos, tenía una mesa parpadeante y desalineada células en mis pruebas. La mesa todavía era utilizable, pero tenía problemas estéticos significativos.

 1
Author: Daniel Waltrip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-04 01:37:11

Utilice 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()});         
}); 
 0
Author: Prabhavith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 19:00:01

Esta no es una solución exacta a la fila de encabezado fijo, pero he creado un método bastante ingenioso de repetir la fila de encabezado a lo largo de la tabla larga, pero aún manteniendo la capacidad de ordenar. Esta pequeña opción requiere el plugin jQuery tablesorter . 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, nut puede ver dónde se repite la fila de encabezado. La fila de encabezado repetido se configura de esta manera 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);
});
 0
Author: DevlshOne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 18:25:41

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

Mine es un plugin de jQuery muy ligero que admite encabezado fijo, pie de página, expansión de columnas (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)

Siempre y cuando tengas una mesa con <thead>, <tbody>, y (opcional) <tfoot>, todo lo que necesita hacer es esto:

$('table').scrollTableBody();
 0
Author: Noah Heldman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-01 14:39:15

Desarrollé un simple plug-in ligero de jQuery para convertir una tabla HTML bien a una tabla desplazable con encabezado de tabla fijo y columnas.

El plugin funciona bien para hacer coincidir el posicionamiento píxel a píxel de la sección fija con la sección desplazable. Además, también puede congelar el número 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/

[1] Github Repo: https://github.com/meetselva/fixed-table-rows-cols

A continuación se muestra el uso de una tabla simple con 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'}
              ]
});
 0
Author: Selvakumar Arumugam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-12 17:07:35

Mucha gente parece estar buscando esta respuesta, la encontré enterrada en una respuesta a otra pregunta aquí: Sincronizar el ancho de la columna entre 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 de desplazamiento inferior con la tabla de encabezado que tiene los mismos anchos.

Así es como lo hice, primero mejoré el jsfiddle anterior para crear esta función, que funciona en td y th (en caso de que se tropiece con otros que usan th para 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, TENGA en cuenta que la tabla de encabezado debe tener un TD adicional para dejar espacio en la tabla superior para la barra de desplazamiento, así:

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

Luego haz algo como:

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

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

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

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

NO HAY NECESIDAD DE ESTAS SOLUCIONES DEMASIADO COMPLEJAS, SOLO HAGA DOS TABLAS COMO LOS EJEMPLOS A CONTINUACIÓN Y LLAME A LA función setHeaderTableWidth como se describe aquí y BOOM, YA ESTÁ.

Si esto no funciona para usted, probablemente estaba jugando con su propiedad css box-sizing y necesita configurarlo correctamente. Es fácil arruinar su css por accidente hay muchas cosas que pueden salir mal, así que tenga cuidado de eso. ESTE ENFOQUE FUNCIONA PARA MÍ. Tengo curiosidad si funciona para otros, házmelo saber. ¡Buena suerte!

 0
Author: pilavdzice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:28

Aquí hay una solución con la que terminamos trabajando (con el fin de hacer frente a algunos casos de borde y versiones anteriores de IE que finalmente también se desvaneció la barra de título en desplazamiento y luego se desvanecen de nuevo en cuando el desplazamiento termina, pero en Firefox y Webkit navegadores esta solución solo funciona. Asume frontera-colapso: colapso.

La clave de esta solución es que una vez que aplique border-collapse, Las transformaciones CSS funcionan en el encabezado, por lo que es solo cuestión de interceptar el desplazamiento eventos y configuración de la transformación correctamente. No necesitas duplicar nada. A menos que este comportamiento se implemente correctamente 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 plugin de jQuery. Sólo tiene que hacer su thead pegajoso con una llamada como $('thead').sticky () y se quedarán por ahí. Funciona para varias tablas en una página y secciones de cabeza a mitad de camino hacia abajo grande tabla.

        $.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();
 0
Author: podperson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-07 16:45:40

Encontré esta solución alternativa: mover la fila del encabezado en una tabla anterior a 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>
 0
Author: Leonid Alzhin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-21 18:19:09

Al aplicar el complemento StickyTableHeaders jQuery a la tabla, los encabezados de columna se pegarán a la parte superior de la ventana 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>
 0
Author: shilovk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-05 15:47:23

Aquí hay una respuesta mejorada a la publicada por Maximilian Hils.

Este funciona en IE11 sin parpadeo alguno:

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) {
        // reseting 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;
    }
});
 0
Author: user4617883,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-29 15:23:39

Me gusta La respuesta de Maximillian Hils pero tenía 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 usando ajax, por lo que quería adjuntar al evento de desplazamiento de la ventana en lugar del evento de desplazamiento del envoltorio

Para deshacerme del parpadeo, utilizo un tiempo de espera para esperar hasta que el usuario haya terminado de desplazarse, luego aplico la transform-so el encabezado no es visible durante el desplazamiento.

También he escrito esto usando jQuery, una ventaja de eso es que jQuery debe manejar prefijos de proveedor para usted

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

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

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

Establezco border-collapse para separar porque de lo contrario perdemos bordes durante la traducción, y elimino el borde de la tabla para detener el contenido que aparece justo encima de la celda donde estaba el borde durante el desplazamiento.

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

Hago el fondo th blanco para cubrir las celdas debajo, y agrego un borde que coincide con el borde de la tabla, que se estiliza 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;
}
 0
Author: Colin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-27 13:38:49

De alguna manera, terminé Position:Sticky funciona 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>
 0
Author: Munkhdelger Tumenbayar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-10 09:42:45