web-dev-qa-db-fra.com

En-têtes de tableau HTML toujours visibles en haut de la fenêtre lors de l'affichage d'un grand tableau

J'aimerais pouvoir "ajuster" la présentation d'un tableau HTML pour ajouter une seule fonctionnalité: lors du défilement vers le bas de la page de sorte que le tableau soit affiché à l'écran mais que les lignes d'en-tête ne soient pas à l'écran, j'aimerais que les en-têtes restent visible en haut de la zone de visualisation.

Ce serait conceptuellement comme la fonctionnalité "figer les volets" dans Excel. Cependant, une page HTML peut contenir plusieurs tables et je voudrais seulement que cela se produise pour la table actuellement affichée, uniquement pendant qu'elle est affichée.

Remarque: j'ai vu une solution dans laquelle la zone de données de la table est mise en défilement tandis que les en-têtes ne défilent pas. Ce n'est pas la solution que je cherche.

166
Craig McQueen

Check out jQuery.floatThead ( démos disponibles ) qui est très cool, peut travailler avec DataTables aussi, et peut même travailler à l'intérieur d'un conteneur overflow: auto.

65
Hendy Irawan

J'ai créé une solution de preuve de concept en utilisant jQuery .

Voir un exemple ici.

J'ai maintenant ce code dans un référentiel Mercurial bitbucket . Le fichier principal est tables.html .

Je suis conscient d'un problème avec ceci: si la table contient des ancres et si vous ouvrez l'URL avec l'ancre spécifiée dans un navigateur, lorsque la page se charge, la ligne avec l'ancre sera probablement masquée par l'en-tête flottant.

Mise à jour 2017-12-11: Je vois que cela ne fonctionne pas avec les versions actuelles de Firefox (57) et Chrome (63). Vous ne savez pas quand et pourquoi cela a cessé de fonctionner, ni comment le réparer. Mais maintenant, je pense que le réponse acceptée par Hendy Irawan est supérieur.

135
Craig McQueen

Craig, j'ai un peu raffiné votre code (entre autres choses, il utilise maintenant position: fixed) et je l'ai enveloppé dans un plugin jQuery.

Essayez-le ici: http://jsfiddle.net/jmosbech/stFcx/

Et obtenez le source ici: https://github.com/jmosbech/StickyTableHeaders

68
jmosbech

Si vous ciblez des navigateurs modernes compatibles css3 ( Prise en charge du navigateur: https://caniuse.com/#feat=css-sticky ) vous pouvez utiliser position:sticky, qui ne nécessite pas JS et ne rompt pas la présentation du tableau en alignant mal les th et td de la même colonne. Il ne nécessite pas non plus de largeur de colonne fixe pour fonctionner correctement.

Exemple pour une seule ligne d'en-tête:

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

Pour les theads avec 1 ou 2 lignes, vous pouvez utiliser quelque chose comme ceci:

thead > :last-child th
{
    position: sticky;
    top: 30px; /* This is for all the the "th" elements in the second row, (in this casa is the last child element into the thead) */
}

thead > :first-child th
{
    position: sticky;
    top: 0px; /* This is for all the the "th" elements in the first child row */
}

Vous devrez peut-être jouer un peu avec la propriété top du dernier enfant, en modifiant le nombre de pixels pour correspondre à la hauteur de la première ligne (+ la marge). + la bordure + le rembourrage, le cas échéant), de sorte que la deuxième ligne colle juste en dessous de la première.

Les deux solutions fonctionnent également même si vous avez plusieurs tableaux dans la même page: l’élément th de chacun d’entre eux commence à être collant lorsque sa position la plus haute est celle indiquée dans la définition css et disparaît lorsque tous les tableaux défile vers le bas. Donc, s’il ya plus de tables, elles fonctionnent toutes de la même manière.

Pourquoi utiliser last-child avant et first-child après dans le css?

Parce que les règles css sont rendues par le navigateur dans le même ordre que vous les écrivez dans le fichier css, c'est pourquoi, si vous n'avez qu'une ligne dans l'élément thead, la première ligne est simultanément la dernière ligne et la première règle enfant pour remplacer le dernier-enfant. Sinon, vous aurez un décalage de la ligne 30 px par rapport à la marge supérieure, ce que je suppose que vous ne voulez pas.

Un problème de position connu: le problème persistant est que cela ne fonctionne pas avec les éléments de tête ni les rangées de tableaux: vous devez cibler les éléments. Ce problème sera résolu avec les futures versions de navigateur.

8
willy wonka

Alternatives possibles

js-floating-table-headers

en-têtes js-floating-table (code Google)

Dans Drupal

J'ai un Drupal 6 site. J'étais sur la page "modules" de l'administrateur, et j'ai remarqué que les tables avaient cette fonctionnalité exacte!

En regardant le code, il semble être implémenté par un fichier appelé tableheader.js . Il applique la fonctionnalité sur toutes les tables avec la classe _sticky-enabled_.

Pour un site Drupal, j'aimerais pouvoir utiliser ce module tableheader.js pour le contenu utilisateur. tableheader.js ne semble pas être présent sur les pages de contenu utilisateur de Drupal. J'ai posté un message du forum pour demander comment modifier le thème Drupal afin qu'il soit disponible. Selon une réponse, _tableheader.js_ peut être ajouté à un thème Drupal à l'aide de drupal_add_js() dans le thème _template.php_ comme suit:

_drupal_add_js('misc/tableheader.js', 'core');
_
5
Craig McQueen

J'ai rencontré ce problème très récemment. Malheureusement, j'ai dû faire 2 tables, une pour l'en-tête et une pour le corps. Ce n'est probablement pas la meilleure approche à ce jour, mais voici:

<html>
<head>
    <title>oh hai</title>
</head>
<body>
    <table id="tableHeader">
        <tr>
            <th style="width:100px; background-color:#CCCCCC">col header</th>
            <th style="width:100px; background-color:#CCCCCC">col header</th>
        </tr>
    </table>
    <div style="height:50px; overflow:auto; width:250px">
        <table>
            <tr>
                <td style="height:50px; width:100px; background-color:#DDDDDD">data1</td>
                <td style="height:50px; width:100px; background-color:#DDDDDD">data1</td>
            </tr>
            <tr>
                <td style="height:50px; width:100px; background-color:#DDDDDD">data2</td>
                <td style="height:50px; width:100px; background-color:#DDDDDD">data2</td>
            </tr>
        </table>
    </div>
</body>
</html>

Cela a fonctionné pour moi, ce n'est probablement pas la manière élégante mais cela fonctionne. Je vais voir si je peux faire quelque chose de mieux, mais cela permet d'avoir plusieurs tables.

Allez lire sur le débordement propriété pour voir si cela correspond à votre besoin

4
Gab Royer

C'est vraiment une chose délicate d'avoir une tête collante sur votre table. J'avais les mêmes exigences mais avec asp: GridView , puis je l'ai trouvé vraiment pensé pour avoir un en-tête collant sur gridview. Il existe de nombreuses solutions disponibles et il m'a fallu 3 jours pour essayer toute la solution mais aucune d'entre elles ne pouvait satisfaire.

Le problème principal que j'ai rencontré avec la plupart de ces solutions était le problème de l'alignement. Lorsque vous essayez de rendre l'en-tête flottant, l'alignement des cellules d'en-tête et des cellules de corps s'écarte quelque peu de la piste.

Avec certaines solutions, j'ai également eu le problème d'obtenir un chevauchement d'en-tête sur les premières lignes du corps, ce qui a pour effet de masquer les lignes du corps derrière l'en-tête flottant.

Alors maintenant, je devais mettre en œuvre ma propre logique pour y parvenir, bien que je ne considère pas non plus cela comme une solution parfaite, mais cela pourrait aussi être utile pour quelqu'un,

Vous trouverez ci-dessous un exemple de tableau.

<div class="table-holder">
        <table id="MyTable" cellpadding="4" cellspacing="0" border="1px" class="customerTable">
            <thead>
                <tr><th>ID</th><th>First Name</th><th>Last Name</th><th>DOB</th><th>Place</th></tr>
            </thead>
            <tbody>
                <tr><td>1</td><td>Customer1</td><td>LastName</td><td>1-1-1</td><td>Sun</td></tr>
                <tr><td>2</td><td>Customer2</td><td>LastName</td><td>2-2-2</td><td>Earth</td></tr>
                <tr><td>3</td><td>Customer3</td><td>LastName</td><td>3-3-3</td><td>Mars</td></tr>
                <tr><td>4</td><td>Customer4</td><td>LastName</td><td>4-4-4</td><td>Venus</td></tr>
                <tr><td>5</td><td>Customer5</td><td>LastName</td><td>5-5-5</td><td>Saturn</td></tr>
                <tr><td>6</td><td>Customer6</td><td>LastName</td><td>6-6-6</td><td>Jupitor</td></tr>
                <tr><td>7</td><td>Customer7</td><td>LastName</td><td>7-7-7</td><td>Mercury</td></tr>
                <tr><td>8</td><td>Customer8</td><td>LastName</td><td>8-8-8</td><td>Moon</td></tr>
                <tr><td>9</td><td>Customer9</td><td>LastName</td><td>9-9-9</td><td>Uranus</td></tr>
                <tr><td>10</td><td>Customer10</td><td>LastName</td><td>10-10-10</td><td>Neptune</td></tr>
            </tbody>
        </table>
    </div>

Remarque : la table est encapsulée dans une DIV avec un attribut de classe égal à 'titulaire de la table'.

Vous trouverez ci-dessous le script JQuery que j'ai ajouté dans l'en-tête de page HTML.

<script src="../Scripts/jquery-1.7.2.min.js" type="text/javascript"></script>
<script src="../Scripts/jquery-ui.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(document).ready(function () {
        //create var for table holder
        var originalTableHolder = $(".table-holder");
        // set the table holder's with
        originalTableHolder.width($('table', originalTableHolder).width() + 17);
        // Create a clone of table holder DIV
        var clonedtableHolder = originalTableHolder.clone();

        // Calculate height of all header rows.
        var headerHeight = 0;
        $('thead', originalTableHolder).each(function (index, element) {
            headerHeight = headerHeight + $(element).height();
        });

        // Set the position of cloned table so that cloned table overlapped the original
        clonedtableHolder.css('position', 'relative');
        clonedtableHolder.css('top', headerHeight + 'px');

        // Set the height of cloned header equal to header height only so that body is not visible of cloned header
        clonedtableHolder.height(headerHeight);
        clonedtableHolder.css('overflow', 'hidden');

        // reset the ID attribute of each element in cloned table
        $('*', clonedtableHolder).each(function (index, element) {
            if ($(element).attr('id')) {
                $(element).attr('id', $(element).attr('id') + '_Cloned');
            }
        });

        originalTableHolder.css('border-bottom', '1px solid #aaa');

        // Place the cloned table holder before original one
        originalTableHolder.before(clonedtableHolder);
    });
</script>

et enfin, ci-dessous, la classe CSS à des fins de coloration.

.table-holder
{
    height:200px;
    overflow:auto;
    border-width:0px;    
}

.customerTable thead
{
    background: #4b6c9e;        
    color:White;
}

Donc, l’idée de cette logique est de placer la table dans un div de titulaire de table et de créer un clone de ce titulaire côté client lors du chargement de la page. Cachez maintenant le corps de la table à l’intérieur du support de clone et positionnez l’en-tête restant sur l’en-tête d’origine.

La même solution fonctionne également pour asp: gridview, vous devez ajouter deux étapes supplémentaires pour y parvenir dans gridview,

  1. Dans l'événement OnPrerender de l'objet gridview de votre page Web, définissez la section de table de la ligne d'en-tête égale à TableHeader.

    if (this.HeaderRow != null)
    {
        this.HeaderRow.TableSection = TableRowSection.TableHeader;
    }
    
  2. Et envelopper votre grille dans <div class="table-holder"></div>.

Remarque : si votre en-tête comporte des commandes cliquables, vous devrez peut-être ajouter un script jQuery supplémentaire pour transmettre les événements générés dans l'en-tête cloné à l'en-tête d'origine. Ce code est déjà disponible dans le plugin jQuery sticky-header créé par jmosbech

3
Prasoon

Si vous utilisez un tableau plein écran, vous voudrez peut-être définir: et en haut: 0; ou essayez une approche très similaire via css.

Mise à jour

Il suffit de construire rapidement une solution de travail avec iframes (html4.0). Cet exemple IS NON conforme au standard, cependant vous pourrez facilement le corriger:

outer.html

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">   
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">     
    <head>      
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />   
        <title>Outer</title>
  <body>
    <iframe src="test.html" width="200" height="100"></iframe>
    </body>
</html> 

test.html

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">   
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">     
    <head>      
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />   
        <title>Floating</title>
    <style type="text/css">
      .content{
        position:relative; 
      }

      thead{
        background-color:red;
        position:fixed; 
        top:0;
      }
    </style>
  <body>
    <div class="content">      
      <table>
        <thead>
          <tr class="top"><td>Title</td></tr>
        </head>
        <tbody>
          <tr><td>a</td></tr>
          <tr><td>b</td></tr>
          <tr><td>c</td></tr>
          <tr><td>d</td></tr>
          <tr><td>e</td></tr>
          <tr><td>e</td></tr>
          <tr><td>e</td></tr>
          <tr><td>e</td></tr>
          <tr><td>e</td></tr>
          <tr><td>e</td></tr>
        </tbody>
      </table>
    </div>
    </body>
</html> 
2
merkuro

L'utilisation de display: fixed sur la section thead devrait fonctionner, mais pour que cela fonctionne uniquement sur la table en cours d'affichage, vous aurez besoin de l'aide de JavaScript. Et ce sera délicat, car il faudra déterminer les emplacements de défilement et l'emplacement des éléments par rapport à la fenêtre d'affichage, qui est l'une des principales zones d'incompatibilité des navigateurs.

Jetez un coup d'œil aux frameworks JavaScript populaires (jQuery, MooTools, YUI, etc.) pour voir s'ils peuvent faire ce que vous voulez ou vous faciliter la tâche.

2
staticsan

Il est frustrant de constater que ce qui fonctionne bien dans un navigateur ne fonctionne pas dans les autres. Ce qui suit fonctionne dans Firefox, mais pas dans Chrome ou IE:

<table width="80%">

 <thead>

 <tr>
  <th>Column 1</th>
  <th>Column 2</th>
  <th>Column 3</th>
 </tr>

 </thead>

 <tbody style="height:50px; overflow:auto">

  <tr>
    <td>Cell A1</td>
    <td>Cell B1</td>
    <td>Cell C1</td>
  </tr>

  <tr>
    <td>Cell A2</td>
    <td>Cell B2</td>
    <td>Cell C2</td>
  </tr>

  <tr>
    <td>Cell A3</td>
    <td>Cell B3</td>
    <td>Cell C3</td>
  </tr>

 </tbody>

</table>
0
Peter Dow

Cela preuve de concept vous avez fait était super! Cependant, j’ai aussi trouvé ce plugin jQuery qui semble fonctionner très bien. J'espère que ça aide!

0
Marco