La cabecera de la tabla debe permanecer fija en la parte superior cuando el usuario la desplaza fuera de la vista con jQuery

Estoy tratando de diseñar una tabla HTML en la que el encabezado se mantenga en la parte superior de la página cuando Y SÓLO cuando el usuario se desplaza fuera de la vista. Por ejemplo, la tabla puede estar a 500 píxeles de la página, ¿cómo puedo hacer que si el usuario se desplaza la cabecera fuera de la vista (el navegador detecta que ya no está en la vista de las ventanas de alguna manera), se mantendrá en la parte superior? ¿Alguien puede darme una solución Javascript para esto?

<table>
  <thead>
    <tr>
      <th>Col1</th>
      <th>Col2</th>
      <th>Col3</th>
    </tr>
  </thead>
  <tbody>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
     <tr>
       <td>info</td>
       <td>info</td>
       <td>info</td>
     </tr>
  </tbody>
</table>

Así que en el ejemplo anterior, quiero que el <thead> se desplace con la página si se sale de la vista.

IMPORTANTE: Estoy NO buscando una solución donde el <tbody> tenga una barra de desplazamiento (overflow:auto).

Solución

Se haría algo así aprovechando el manejador de eventos scroll de window, y utilizando otra table con una posición fija para mostrar la cabecera en la parte superior de la página.

HTML:

<table id="header-fixed"></table>

CSS:

#header-fixed {
    position: fixed;
    top: 0px; display:none;
    background-color:white;
}

JavaScript:

var tableOffset = $("#table-1").offset().top;
var $header = $("#table-1 > thead").clone();
var $fixedHeader = $("#header-fixed").append($header);

$(window).bind("scroll", function() {
    var offset = $(this).scrollTop();

    if (offset >= tableOffset && $fixedHeader.is(":hidden")) {
        $fixedHeader.show();
    }
    else if (offset < tableOffset) {
        $fixedHeader.hide();
    }
});

Esto mostrará la cabeza de la tabla cuando el usuario se desplace hacia abajo lo suficiente como para ocultar la cabeza de la tabla original. Se ocultará de nuevo cuando el usuario se haya desplazado de nuevo hacia arriba.

Ejemplo práctico: http://jsfiddle.net/andrewwhitaker/fj8wM/

Comentarios (21)

Pues bien, yo intentaba obtener el mismo efecto sin recurrir a columnas de tamaño fijo ni tener una altura fija para toda la tabla.

La solución que se me ocurrió es un hack. Consiste en duplicar toda la tabla y luego ocultar todo menos la cabecera, y hacer que ésta tenga una posición fija.

HTML

<div id="table-container">
<table id="maintable">
    <thead>
        <tr>
            <th>Col1</th>
            <th>Col2</th>
            <th>Col3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>some really long line here instead</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
                <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
        <tr>
            <td>info</td>
            <td>info</td>
            <td>info</td>
        </tr>
    </tbody>
</table>
<div id="bottom_anchor"></div>
</div>
body { height: 1000px; }
thead{
    background-color:white;
}

javascript


function moveScroll(){
    var scroll = $(window).scrollTop();
    var anchor_top = $("#maintable").offset().top;
    var anchor_bottom = $("#bottom_anchor").offset().top;
    if (scroll>anchor_top && scroll
Comentarios (15)

Pude arreglar el problema con el cambio de los anchos de las columnas. Empecé con la solución de Andrew's arriba (¡muchas gracias!) y luego añadí un pequeño bucle para establecer los anchos de los td's clonados:

$("#header-fixed td").each(function(index){
    var index2 = index;
    $(this).width(function(index2){
        return $("#table-1 td").eq(index).width();
    });
});

Esto resuelve el problema sin tener que clonar toda la tabla y ocultar el cuerpo. Soy completamente nuevo en JavaScript y jQuery (y en stack overflow), así que cualquier comentario se agradece.

Comentarios (1)