Tableau HTML avec en-têtes fixes?


231

Existe-t-il une technique CSS / JavaScript multi-navigateur pour afficher un long tableau HTML de sorte que les en-têtes de colonne restent fixes à l'écran et ne défilent pas avec le corps du tableau. Pensez à l'effet «figer les volets» dans Microsoft Excel.

Je veux pouvoir faire défiler le contenu du tableau, mais toujours voir les en-têtes de colonne en haut.


3
Essayez ceci: Pure CSS Scrollable Table with Fixed Header EDIT : Celui-ci devrait fonctionner dans Internet Explorer 7 comme le montre l' exemple : Scrolling HTML Table with Fixed Header EDIT 2: J'ai trouvé quelques liens supplémentaires qui pourraient être utiles: - Stupide en-tête fixe - Un plugin jQuery avec quelques limitations. - [En-têtes de table fixes] ( cross-browser.com/x/examp
gcores

J'ai rencontré de nombreuses solutions qui fonctionnent généralement, mais aucune d'entre elles n'a fonctionné en faisant défiler les div. Je veux dire, votre table est à l'intérieur d'une div défilable et vous voulez toujours que votre en-tête de table soit toujours à l'intérieur de cette div. J'ai résolu cela et partage la solution ici .
Yogee

9
En 2018, tous les navigateurs peuvent utiliser la solution simple suivante: thead th { position: sticky; top: 0; }. Safari a besoin d'un préfixe de fournisseur:-webkit-sticky
Daniel Waltrip

1
@DanielWaltrip, vous devez ajouter cette réponse afin qu'elle puisse être votée au premier rang - toutes les autres réponses sont redondantes avec la position: collant étant un meilleur support de nos jours
Peter Kerr

Réponses:


88

Je cherchais une solution pour cela depuis un certain temps et j'ai trouvé que la plupart des réponses ne fonctionnaient pas ou ne convenaient pas à ma situation, j'ai donc écrit une solution simple avec jQuery.

Voici le contour de la solution:

  1. Clonez la table qui doit avoir un en-tête fixe et placez la copie clonée au-dessus de l'original.
  2. Retirez le corps de la table de la table supérieure.
  3. Retirez l'en-tête du tableau du tableau inférieur.
  4. Ajustez la largeur des colonnes. (Nous gardons une trace des largeurs de colonne d'origine)

Vous trouverez ci-dessous le code d'une démonstration exécutable.

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>

Cette solution fonctionne dans Chrome et IE. Comme il est basé sur jQuery, cela devrait également fonctionner dans d'autres navigateurs pris en charge par jQuery.


4
et comment résoudre le problème lorsque le contenu est plus grand que la largeur?
Maertz

1
@tetra td {max-width: 30px; } cela vous permettra au développeur de contrôler la façon dont les lignes sont affichées.
Lyuben Todorov

Mais que se passe-t-il si le contenu de certaines cellules d'en-tête est plus long que celui des cellules td? J'ai essayé cela dans IE7, et width () casse tout. IE8 et IE9 fonctionnent bien, cependant ...
JustAMartin

4
Malheureusement, si vous avez besoin d'un alignement parfait des pixels des colonnes, cela ne fonctionne pas: jsbin.com/elekiq/1 ( code source ). Vous pouvez voir que certains en-têtes sont décalés de leur emplacement, juste légèrement. L'effet est plus évident si vous utilisez des arrière-plans: jsbin.com/elekiq/2 ( code source ). (Je travaillais dans le même sens, je suis tombé sur cela dans mon code, j'ai trouvé le vôtre et j'ai pensé "Oh, je me demande s'il a résolu cela pour moi!" Malheureusement non. :-)) Les navigateurs sont TELLEMENT mal à vouloir contrôler le largeurs de cellules ...
TJ Crowder

Cela ne semble pas fonctionner avec le défilement horizontal - il crée l'en-tête, mais il s'étend au-delà de la zone de défilement (visiblement) et ne défile pas avec le contenu.
Crash

183

Cela peut être résolu proprement en quatre lignes de code.

Si vous vous souciez uniquement des navigateurs modernes, un en-tête fixe peut être obtenu beaucoup plus facilement en utilisant des transformations CSS. Cela semble étrange, mais fonctionne très bien:

  • HTML et CSS restent tels quels.
  • Pas de dépendances JavaScript externes.
  • Quatre lignes de code.
  • Fonctionne pour toutes les configurations (disposition de table: fixe, etc.).
document.getElementById("wrap").addEventListener("scroll", function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});

La prise en charge des transformations CSS est largement disponible, à l' exception d'Internet Explorer 8-.

Voici l'exemple complet pour référence:


8
Je dois dire que, malgré mon commentaire précédent, c'est la chose la plus proche d'une solution parfaite que j'ai vue. Même le défilement horizontal est parfait (mieux que ma propre solution). Voici un exemple avec des bordures (vous ne pouvez pas utiliser border-collapse) et une barre de défilement qui colle à la table au lieu du conteneur: jsfiddle
DoctorDestructo

11
Découvert, cela fonctionne mais il faut appliquer la transformation à th / td, pas à thead.
rousse

5
@AlexAlexeev, votre solution est incroyable. Je vous remercie. J'ai remarqué que l'en-tête fixe résultant n'a pas les lignes de frontière qui distinguent les colonnes. Le style CSS par défaut est perdu. Même lorsque j'inclus ceci ... $(this).addClass('border')change le reste du tableau présente les polices, la taille, la couleur que je passe dans la classe de bordure. Mais, n'ajoute pas de lignes à l'en-tête fixe. Appréciez, toute entrée sur la façon de résoudre ce problème
user5249203

5
@ user5249203 Je sais que vous l'avez demandé il y a quelques mois mais j'ai eu le même problème et c'était dû à l'effondrement de la frontière: voyez ceci: stackoverflow.com/questions/33777751/… .
archz

6
Cela ne fonctionne dans aucune version d'IE ou dans Edge. Voici une version basée sur le commentaire de @ redhead jsfiddle.net/n6o8ocwb/2
rob

58

Je viens de terminer la création d'un plugin jQuery qui prendra une table unique valide en utilisant du HTML valide (doit avoir une tête et un corps) et produira une table qui a des en-têtes fixes, un pied de page fixe facultatif qui peut être soit un en-tête cloné, soit le contenu que vous avez choisi (pagination, etc.). Si vous souhaitez profiter de moniteurs plus grands, il redimensionnera également le tableau lorsque le navigateur sera redimensionné. Une autre fonctionnalité ajoutée est la possibilité de faire défiler le côté si les colonnes du tableau ne peuvent pas toutes tenir en vue.

http://fixedheadertable.com/

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

Il est extrêmement facile à configurer et vous pouvez créer vos propres styles personnalisés pour cela. Il utilise également des coins arrondis dans tous les navigateurs. Gardez à l'esprit que je viens de le publier, donc c'est toujours techniquement bêta et il y a très peu de problèmes mineurs que je résous.

Il fonctionne dans Internet Explorer 7, Internet Explorer 8, Safari, Firefox et Chrome.


Merci! J'ajoute une nouvelle version plus tard dans la journée quand je rentre du travail. Voici un lien vers mon entrée de blog avec ce que j'ajoute
Mark

Merci pour ça. Je sais que cette question remonte à plus d'un an, mais même au risque de remuer le limon décanté, je voudrais vous dire que votre travail est apprécié
sova

Dans votre démo, les largeurs sont désactivées dans ie6 :-( l'en-tête et le corps du tableau ne sont pas alignés.
Cheekysoft

4
La dernière version ne fonctionne pas dans IE6. Je ne supporte plus IE6.
Mark

grand travail Mark - malheureusement, il y a des problèmes avec le défilement de l'en-tête et de la colonne fixes dans les appareils mobiles (iPad, tablette Android) - lorsque je fais défiler le contenu, ces parties fixes ne défilent pas - lorsque j'arrête de faire défiler et appuyez une fois sur le tableau , les parties fixes "sautent" aux positions appropriées - existe-t-il un moyen simple de résoudre ce problème?
Okizb

23

J'ai également créé un plugin qui résout ce problème. Mon projet - jQuery.floatThead existe depuis plus de 4 ans maintenant et est très mature.

Il ne nécessite aucun style externe et ne s'attend pas à ce que votre table soit stylisée d'une manière particulière. Il prend en charge Internet Explorer9 + et Firefox / Chrome.

Actuellement (2018-05), il a:

405 commits et 998 étoiles sur GitHub


Beaucoup (pas toutes) des réponses ici sont des hacks rapides qui peuvent avoir résolu le problème d'une personne, mais ne fonctionneront pas pour chaque table.

Certains des autres plugins sont anciens et fonctionnent probablement très bien avec Internet Explorer, mais ils tomberont en panne sur Firefox et Chrome.


1
Excellent plugin, prend en charge les tables et les décalages imbriqués.
Mihai Alex du

2
Génial. Merci beaucoup. Le plugin fonctionnait bien dans Firefox 45.2, Chromium 51 et IE 11. De plus, il n'interfère pas avec beaucoup de code JS et jQuery construit sur la même page.
Aldo Paradiso

Je vous remercie. Je suis heureux d'annoncer que le projet reçoit environ 1 rapport de bogue tous les 4 mois à ce stade. Je n'apporte pas beaucoup de changements de rupture. Il est assez solide et fonctionne.
mkoryak

20

TL; DR

Si vous ciblez des navigateurs modernes et n'avez pas de besoins de style extravagants: http://jsfiddle.net/dPixie/byB9d/3/ ... Bien que la grande version quatre soit assez douce, cette version gère beaucoup mieux la largeur du fluide.

Bonnes nouvelles tout le monde!

Avec les avancées de HTML5 et CSS3, cela est désormais possible, du moins pour les navigateurs modernes. L'implémentation légèrement hackeuse que j'ai trouvée peut être trouvée ici: http://jsfiddle.net/dPixie/byB9d/3/ . Je l'ai testé dans FX 25, Chrome 31 et IE 10 ...

HTML pertinent (insérez un doctype HTML5 en haut de votre document):

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

section {
  position: relative;
  border: 1px solid #000;
  padding-top: 37px;
  background: #500;
}

section.positioned {
  position: absolute;
  top: 100px;
  left: 100px;
  width: 800px;
  box-shadow: 0 0 15px #333;
}

.container {
  overflow-y: auto;
  height: 200px;
}

table {
  border-spacing: 0;
  width: 100%;
}

td+td {
  border-left: 1px solid #eee;
}

td,
th {
  border-bottom: 1px solid #eee;
  background: #ddd;
  color: #000;
  padding: 10px 25px;
}

th {
  height: 0;
  line-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  color: transparent;
  border: none;
  white-space: nowrap;
}

th div {
  position: absolute;
  background: transparent;
  color: #fff;
  padding: 9px 25px;
  top: 0;
  margin-left: -25px;
  line-height: normal;
  border-left: 1px solid #800;
}

th:first-child div {
  border: none;
}
<section class="positioned">
  <div class="container">
    <table>
      <thead>
        <tr class="header">
          <th>
            Table attribute name
            <div>Table attribute name</div>
          </th>
          <th>
            Value
            <div>Value</div>
          </th>
          <th>
            Description
            <div>Description</div>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>align</td>
          <td>left, center, right</td>
          <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the alignment of a table according to surrounding text</td>
        </tr>
        <tr>
          <td>bgcolor</td>
          <td>rgb(x,x,x), #xxxxxx, colorname</td>
          <td>Not supported in HTML5. Deprecated in HTML 4.01. Specifies the background color for a table</td>
        </tr>
        <tr>
          <td>border</td>
          <td>1,""</td>
          <td>Specifies whether the table cells should have borders or not</td>
        </tr>
        <tr>
          <td>cellpadding</td>
          <td>pixels</td>
          <td>Not supported in HTML5. Specifies the space between the cell wall and the cell content</td>
        </tr>
        <tr>
          <td>cellspacing</td>
          <td>pixels</td>
          <td>Not supported in HTML5. Specifies the space between cells</td>
        </tr>
        <tr>
          <td>frame</td>
          <td>void, above, below, hsides, lhs, rhs, vsides, box, border</td>
          <td>Not supported in HTML5. Specifies which parts of the outside borders that should be visible</td>
        </tr>
        <tr>
          <td>rules</td>
          <td>none, groups, rows, cols, all</td>
          <td>Not supported in HTML5. Specifies which parts of the inside borders that should be visible</td>
        </tr>
        <tr>
          <td>summary</td>
          <td>text</td>
          <td>Not supported in HTML5. Specifies a summary of the content of a table</td>
        </tr>
        <tr>
          <td>width</td>
          <td>pixels, %</td>
          <td>Not supported in HTML5. Specifies the width of a table</td>
        </tr>
      </tbody>
    </table>
  </div>
</section>

Mais comment?!

Autrement dit, vous avez un en-tête de table, que vous masquez visuellement en le faisant à 0px de haut, qui contient également des divs utilisés comme en-tête fixe. Le conteneur de la table laisse suffisamment d'espace en haut pour permettre l'en-tête absolument positionné, et la table avec des barres de défilement apparaît comme vous vous y attendez.

Le code ci-dessus utilise la classe positionnée pour positionner le tableau de manière absolue (je l'utilise dans une boîte de dialogue de style contextuelle) mais vous pouvez également l'utiliser dans le flux du document en supprimant le positioned classe du conteneur.

Mais ...

Ce n'est pas parfait. Firefox refuse de faire la ligne d'en-tête 0px (au moins je n'ai trouvé aucun moyen) mais la maintient obstinément au minimum 4px ... Ce n'est pas un gros problème, mais en fonction de votre style, il gâchera vos bordures, etc.

Le tableau utilise également une approche de fausses colonnes où la couleur d'arrière-plan du conteneur lui-même est utilisée comme arrière-plan pour les divs d'en-tête, qui sont transparents.

Résumé

Dans l'ensemble, il peut y avoir des problèmes de style en fonction de vos besoins, en particulier des bordures ou des arrière-plans compliqués. Il peut également y avoir des problèmes de calculabilité, je ne l'ai pas encore vérifié dans une grande variété de navigateurs (veuillez commenter avec vos expériences si vous l'essayez), mais je n'ai rien trouvé de tel, alors j'ai pensé que cela valait la peine d'être publié en tous cas ...


Si vous réduisez la largeur de la fenêtre jusqu'à ce que le défilement horizontal se déclenche, l'en-tête ne défile pas horizontalement avec le corps. Zut.
dlaliberte

@dlaliberte - Eh bien, puisque l'en-tête et le tableau sont en fait deux éléments différents, vous pouvez, bien sûr, entrer dans l'étrange. Mais mon exemple ne permet pas le débordement sur les colonnes du tableau et les en-têtes sont généralement plus faciles à contrôler que le contenu du tableau. Cela dit, si vous faites déborder l'en-tête, il se détachera à droite de la table et semblera gravement cassé. Vous pouvez résoudre ce problème en définissant une largeur minimale sur la table, en la forçant également à déborder la page ... Mais c'est un hack donc il ne sera jamais parfait ...
Jonas Schubert Erlandsson

1
Il convient de souligner que cela nécessite une conception où une table à hauteur fixe peut être spécifiée.
Cheekysoft

1
@Cheekysoft - Non, le contenu du tableau et des lignes peut circuler librement. Le conteneur, dans mon exemple l' <section>élément, doit être limité en hauteur uniquement pour le forcer à déborder et afficher le défilement. Toute mise en page qui ferait déborder le conteneur fonctionnerait. Si vous trouvez un cas où ce n'est pas le cas, veuillez poster un lien vers un violon.
Jonas Schubert Erlandsson

La padding-topvaleur codée en dur signifie également que si le texte de l'en-tête du tableau se trouve sur plusieurs lignes, il apparaîtra au-dessus des cellules du tableau. Dommage, car cela fonctionne comme un charme la plupart du temps. Vraiment sympa truc avec l' diven thpour contourner le problème de dimensionnement de la colonne la plupart des autres solutions ont.
Bernhard Hofmann

19

Toutes les tentatives pour résoudre ce problème en dehors de la spécification CSS sont des ombres pâles de ce que nous voulons vraiment: livraison sur la promesse implicite de THEAD.

Ce problème d'en-têtes figés pour une table est une plaie ouverte en HTML / CSS depuis longtemps.

Dans un monde parfait, il y aurait une solution pure CSS pour ce problème. Malheureusement, il ne semble pas y en avoir un bon en place.

Les discussions sur les normes pertinentes sur ce sujet comprennent:

MISE À JOUR : Firefox livré position:stickyen version 32. Tout le monde y gagne!


Ce serait génial d'avoir des colonnes en renard de la même manière
Csaba Toth

4
Ré. Firefox et position: collant, cela ne fonctionne pas pour les en-têtes de table: bugzilla.mozilla.org/show_bug.cgi?id=925259#c8 ... Le correctif pour ce bogue indique explicitement: "Nous ne prenons actuellement pas en charge le positionnement relatif des éléments internes de la table, nous les excluons donc également du positionnement collant. "
Jonas Schubert Erlandsson

2
Cela fonctionne dans tous les navigateurs maintenant: thead th { position: sticky; top: 0; }. Pouvons-nous mettre à jour cette réponse pour l'énoncer clairement?
Daniel Waltrip

1
@DanielWaltrip tous les navigateurs? stackoverflow.com/a/37646284/3640407 Il y a encore plus de MSIE que d'Edges
edc65

Bon point. Il est pris en charge par 86% des utilisateurs mondiaux du Web, selon caniuse.com/#search=position%3Asticky
Daniel Waltrip

14

Voici un plugin jQuery pour les en-têtes de table fixes. Il permet à la page entière de défiler, figeant l'en-tête lorsqu'elle atteint le haut. Cela fonctionne bien avec les tables Twitter Bootstrap .

Référentiel GitHub: https://github.com/oma/table-fixed-header

Il ne fait pas défiler uniquement le contenu du tableau. Recherchez d'autres outils pour cela, comme l'une de ces autres réponses. Vous décidez ce qui convient le mieux à votre cas.


1
Bummer - l'exemple de lien est mort. "Oups! Denne siden ble ikke funnet ..." J'aimerais que le code soit collé ici.
JosephK

ouais ... désolé pour ça. supprimé le lien. Le projet n'est plus maintenu
oma

Ne vous inquiétez pas - j'ai essayé plusieurs de ces prétendues solutions pré-faites - aucune ne fonctionnait avec une table flex-col-width qui dépassait la largeur de l'écran. J'ai fini par écrire ma propre solution.
JosephK

9

La plupart des solutions publiées ici nécessitent jQuery. Si vous recherchez une solution indépendante du framework, essayez Grid: http://www.matts411.com/post/grid/

Il est hébergé sur Github ici: https://github.com/mmurph211/Grid

Non seulement il prend en charge les en-têtes fixes, il prend également en charge, entre autres, les colonnes et les pieds de page fixes.


C'est vraiment bien si cela répond à vos besoins, je viens de jouer avec aujourd'hui. Malheureusement, il s'agit plutôt d'une grille rectangulaire (comme son nom l'indique, en fait) et non d'une vraie table avec une hauteur de ligne ajustée par le contenu. Et le style des lignes individuelles semblait difficile. Je n'ai pas réussi à créer une table à rayures zébrées mais je n'ai pas essayé très fort car mes besoins étaient en fait plus complexes. Bref, beau travail.
mplwork

1
Hé je te connais! Nous semblions avoir écrit une merde très similaire ( github.com/mkoryak/floatThead ) - Misha
mkoryak

9

La propriété CSS position: stickya un grand support dans la plupart des navigateurs modernes (j'ai eu des problèmes avec Edge, voir ci-dessous).

Cela nous permet de résoudre assez facilement le problème des en-têtes fixes:

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

Safari a besoin d' un préfixe fournisseur: -webkit-sticky.

Pour Firefox, j'ai dû ajouter min-height: 0à l'un des éléments parents. J'oublie exactement pourquoi cela était nécessaire.

Malheureusement, l'implémentation de Microsoft Edge ne semble que semi-fonctionnelle. Au moins, j'avais des cellules de tableau vacillantes et mal alignées lors de mes tests. La table était toujours utilisable, mais avait des problèmes esthétiques importants.


L' utilisation position: sticky;de la table dans un div qui a overflow: scroll;, overflow-x: scroll;ou overflow-y: scroll;. semble être la solution la meilleure et la plus simple pour les en-têtes et colonnes de table fixes dans les navigateurs modernes. Cette réponse doit être votée en haut.
Aberrant

C'est simple mais efficace. C'est ce que je recherche. Merci.
Catbuilts

6

Une table de défilement CSS plus raffinée

Toutes les solutions CSS pures que j'ai vues jusqu'à présent - aussi intelligentes soient-elles - manquent d'un certain niveau de polissage ou ne fonctionnent tout simplement pas correctement dans certaines situations. J'ai donc décidé de créer le mien ...

Fonctionnalités:

  • C'est du pur CSS, donc pas de jQuery requis (ou tout code JavaScript du tout, d'ailleurs)
  • Vous pouvez définir la largeur du tableau sur un pourcentage (aka "fluide") ou une valeur fixe, ou laisser le contenu déterminer sa largeur (aka "auto")
  • Les largeurs de colonne peuvent également être fluides, fixes ou automatiques.
  • Les colonnes ne seront jamais désalignées avec les en-têtes en raison du défilement horizontal (un problème qui se produit dans toutes les autres solutions basées sur CSS que j'ai vues qui ne nécessite pas de largeurs fixes).
  • Compatible avec tous les navigateurs de bureau populaires, y compris Internet Explorer vers la version 8
  • Aspect propre et poli; pas d'espaces de 1 pixel d'aspect bâclé ou de bordures mal alignées; est identique dans tous les navigateurs

Voici quelques violons qui montrent les options de largeur fluide et automatique:

  • Largeur et hauteur du fluide (s'adapte à la taille de l'écran): jsFiddle (Notez que la barre de défilement n'apparaît que lorsque cela est nécessaire dans cette configuration, vous devrez donc peut-être rétrécir le cadre pour le voir)

  • Largeur automatique, hauteur fixe (plus facile à intégrer avec d'autres contenus): jsFiddle

La configuration Auto Width, Fixed Height a probablement plus de cas d'utilisation, donc je posterai le code ci-dessous.

/* The following 'html' and 'body' rule sets are required only
   if using a % width or height*/

/*html {
  width: 100%;
  height: 100%;
}*/

body {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 20px 0 20px;
  text-align: center;
}
.scrollingtable {
  box-sizing: border-box;
  display: inline-block;
  vertical-align: middle;
  overflow: hidden;
  width: auto; /* If you want a fixed width, set it here, else set to auto */
  min-width: 0/*100%*/; /* If you want a % width, set it here, else set to 0 */
  height: 188px/*100%*/; /* Set table height here; can be fixed value or % */
  min-height: 0/*104px*/; /* If using % height, make this large enough to fit scrollbar arrows + caption + thead */
  font-family: Verdana, Tahoma, sans-serif;
  font-size: 16px;
  line-height: 20px;
  padding: 20px 0 20px 0; /* Need enough padding to make room for caption */
  text-align: left;
  color: black;
}
.scrollingtable * {box-sizing: border-box;}
.scrollingtable > div {
  position: relative;
  border-top: 1px solid black;
  height: 100%;
  padding-top: 20px; /* This determines column header height */
}
.scrollingtable > div:before {
  top: 0;
  background: cornflowerblue; /* Header row background color */
}
.scrollingtable > div:before,
.scrollingtable > div > div:after {
  content: "";
  position: absolute;
  z-index: -1;
  width: 100%;
  height: 100%;
  left: 0;
}
.scrollingtable > div > div {
  min-height: 0/*43px*/; /* If using % height, make this large
                            enough to fit scrollbar arrows */
  max-height: 100%;
  overflow: scroll/*auto*/; /* Set to auto if using fixed
                               or % width; else scroll */
  overflow-x: hidden;
  border: 1px solid black; /* Border around table body */
}
.scrollingtable > div > div:after {background: white;} /* Match page background color */
.scrollingtable > div > div > table {
  width: 100%;
  border-spacing: 0;
  margin-top: -20px; /* Inverse of column header height */
  /*margin-right: 17px;*/ /* Uncomment if using % width */
}
.scrollingtable > div > div > table > caption {
  position: absolute;
  top: -20px; /*inverse of caption height*/
  margin-top: -1px; /*inverse of border-width*/
  width: 100%;
  font-weight: bold;
  text-align: center;
}
.scrollingtable > div > div > table > * > tr > * {padding: 0;}
.scrollingtable > div > div > table > thead {
  vertical-align: bottom;
  white-space: nowrap;
  text-align: center;
}
.scrollingtable > div > div > table > thead > tr > * > div {
  display: inline-block;
  padding: 0 6px 0 6px; /*header cell padding*/
}
.scrollingtable > div > div > table > thead > tr > :first-child:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 20px; /*match column header height*/
  border-left: 1px solid black; /*leftmost header border*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div > div:first-child,
.scrollingtable > div > div > table > thead > tr > * + :before {
  position: absolute;
  top: 0;
  white-space: pre-wrap;
  color: white; /*header row font color*/
}
.scrollingtable > div > div > table > thead > tr > * > div[label]:before,
.scrollingtable > div > div > table > thead > tr > * > div[label]:after {content: attr(label);}
.scrollingtable > div > div > table > thead > tr > * + :before {
  content: "";
  display: block;
  min-height: 20px; /* Match column header height */
  padding-top: 1px;
  border-left: 1px solid black; /* Borders between header cells */
}
.scrollingtable .scrollbarhead {float: right;}
.scrollingtable .scrollbarhead:before {
  position: absolute;
  width: 100px;
  top: -1px; /* Inverse border-width */
  background: white; /* Match page background color */
}
.scrollingtable > div > div > table > tbody > tr:after {
  content: "";
  display: table-cell;
  position: relative;
  padding: 0;
  border-top: 1px solid black;
  top: -1px; /* Inverse of border width */
}
.scrollingtable > div > div > table > tbody {vertical-align: top;}
.scrollingtable > div > div > table > tbody > tr {background: white;}
.scrollingtable > div > div > table > tbody > tr > * {
  border-bottom: 1px solid black;
  padding: 0 6px 0 6px;
  height: 20px; /* Match column header height */
}
.scrollingtable > div > div > table > tbody:last-of-type > tr:last-child > * {border-bottom: none;}
.scrollingtable > div > div > table > tbody > tr:nth-child(even) {background: gainsboro;} /* Alternate row color */
.scrollingtable > div > div > table > tbody > tr > * + * {border-left: 1px solid black;} /* Borders between body cells */
<div class="scrollingtable">
  <div>
    <div>
      <table>
        <caption>Top Caption</caption>
        <thead>
          <tr>
            <th><div label="Column 1"/></th>
            <th><div label="Column 2"/></th>
            <th><div label="Column 3"/></th>
            <th>
              <!-- More versatile way of doing column label; requires two identical copies of label -->
              <div><div>Column 4</div><div>Column 4</div></div>
            </th>
            <th class="scrollbarhead"/> <!-- ALWAYS ADD THIS EXTRA CELL AT END OF HEADER ROW -->
          </tr>
        </thead>
        <tbody>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
          <tr><td>Lorem ipsum</td><td>Dolor</td><td>Sit</td><td>Amet consectetur</td></tr>
        </tbody>
      </table>
    </div>
    Faux bottom caption
  </div>
</div>

<!--[if lte IE 9]><style>.scrollingtable > div > div > table {margin-right: 17px;}</style><![endif]-->

La méthode que j'ai utilisée pour figer la ligne d'en-tête est similaire à celle de d-Pixie, alors référez-vous à son article pour une explication. Il y avait une multitude de bogues et de limitations avec cette technique qui ne pouvaient être corrigés qu'avec des tas de CSS supplémentaires et un ou deux conteneurs de div supplémentaires.


Cette réponse est bien sous-estimée! J'ai passé des jours à essayer de trouver d'autres solutions pour mon cas particulièrement ennuyeux. Chacun d'eux n'a pas réussi à rester aligné d'une manière ou d'une autre. Cela a finalement réussi! Cela semble trop compliqué au début, mais une fois que vous avez compris, génial. Vous pouvez supprimer un certain nombre de choses dont vous n'avez pas besoin à la fin, lorsque vous n'utilisez pas de largeur de fluide, etc.
Justin Sane

1
@JustinSane Content que vous l'aimiez! Je suppose que le manque d'appréciation est dû au fait qu'il partage la page avec la solution étonnante de Maximilian Hils . Si vous n'êtes pas opposé à l'utilisation d'un tout petit peu de JS, vous devriez absolument le vérifier.
DoctorDestructo

Bon sang, c'est une solution presque parfaite en effet. J'utilisais jQuery de toute façon, j'ai essayé de le faire fonctionner avant de trouver le vôtre (via votre commentaire à une autre question). Je ne pensais pas à un écouteur de défilement et à traduire ... Eh bien, ils disent qu'il faut du génie pour trouver des solutions simples ..;) J'ai terminé le projet et cela fonctionne parfaitement sans js, mais je vais le garder dans l'esprit pour l'avenir. Encore un chapeau à vous d'être génial!
Justin Sane

Un petit problème, mais si vous utilisez différentes couleurs système, vous pouvez voir que la couleur du texte n'a pas été définie pour quoi que ce soit, sauf les en-têtes, mais l'arrière-plan du tableau a une couleur d'arrière-plan explicite. J'ai du texte jaune sur fond blanc et gris pour ce tableau.
Matt Arnold

1
@MattArnold Fixed. Thx pour la pointe!
DoctorDestructo

5

Un simple plugin jQuery

Il s'agit d'une variante de la solution de Mahes. Vous pouvez l'appeler comme$('table#foo').scrollableTable();

L'idée est:

  • Divisez le theadet tbodyen tableéléments séparés
  • Faire correspondre à nouveau la largeur de leurs cellules
  • Envelopper le second tabledans undiv.scrollable
  • Utilisez CSS pour faire div.scrollabledéfiler

Le CSS pourrait être:

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

Avertissements

  • Évidemment, la division de ces tables rend le balisage moins sémantique. Je ne sais pas quel effet cela a sur l'accessibilité.
  • Ce plugin ne traite pas des pieds de page, des en-têtes multiples, etc.
  • Je ne l'ai testé que dans Chrome version 20.

Cela dit, cela fonctionne pour mes besoins et vous êtes libre de le prendre et de le modifier.

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

1
Joli script, celui-ci fonctionnait mieux dans mon environnement. J'ai étendu votre script avec un support de pied de page fixe, consultez mon article ci-dessous.
gitaarik

4

:)

Solution pas si propre, mais pure HTML / CSS.

table {
    overflow-x:scroll;
}

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

Mis à jour pour l' exemple IE8 + JSFiddle


2
Bonne solution, seulement pour mentionner, que ces cellules sont flottantes et que, selon le contenu, elles peuvent avoir des hauteurs différentes, c'est visible si vous les définissez des bordures: jsfiddle.net/ZdeEH/15
Stano

3

Prise en charge du pied de page fixe

J'ai étendu la fonction de Nathan pour prendre également en charge un pied de page fixe et une hauteur maximale. De plus, la fonction définira le CSS lui-même et vous n'aurez qu'à prendre en charge une largeur.

Usage:

Hauteur fixe:

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

Hauteur maximale (si le navigateur prend en charge l'option CSS «max-height»):

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

Scénario:

jQuery.fn.scrollableTable = function(options) {

    var $originalTable, $headTable, $bodyTable, $footTable, $scrollableDiv, originalWidths;

    // Prepare the separate parts of the table
    $originalTable = $(this);
    $headTable = $originalTable.clone();

    $headTable.find('tbody').remove();
    $headTable.find('tfoot').remove();

    $bodyTable = $originalTable.clone();
    $bodyTable.find('thead').remove();
    $bodyTable.find('tfoot').remove();

    $footTable = $originalTable.clone();
    $footTable.find('thead').remove();
    $footTable.find('tbody').remove();

    // Grab the original column widths and set them in the separate tables
    originalWidths = $originalTable.find('tr:first td').map(function() {
        return $(this).width();
    });

    $.each([$headTable, $bodyTable, $footTable], function(index, $table) {
        $table.find('tr:first td').each(function(i) {
            $(this).width(originalWidths[i]);
        });
    });

    // The div that makes the body table scroll
    $scrollableDiv = $('<div/>').css({
        'overflow-y': 'scroll'
    });

    if(options.height) {
        $scrollableDiv.css({'height': options.height});
    }
    else if(options.maxHeight) {
        $scrollableDiv.css({'max-height': options.maxHeight});
    }

    // Add the new separate tables and remove the original one
    $headTable.insertAfter($originalTable);
    $bodyTable.insertAfter($headTable);
    $footTable.insertAfter($bodyTable);
    $bodyTable.wrap($scrollableDiv);
    $originalTable.remove();
};

3

D'une certaine manière, j'ai fini par Position:Stickybien travailler sur mon cas:

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>


1
C'est la solution la plus propre que j'ai vue jusqu'à présent. caniuse montre qu'au 5/2/2020, position non préfixée: sticky bénéficie d'un support global de 90,06%. Cette solution fonctionne donc bien dans tous les navigateurs modernes.
AlienKevin

2

Deux divs, un pour l'en-tête, un pour les données. Faites défiler la div des données et utilisez JavaScript pour définir la largeur des colonnes de l'en-tête de la même manière que les largeurs des données. Je pense que les largeurs des colonnes de données doivent être fixes plutôt que dynamiques.


3
Si vous vous souciez de l'accessibilité, c'est un échec.
epascarello

1
concernant l'accessibilité, peut-être pourrions-nous remplacer l'utilisation des divs par un style sur <thead> et <tbody> ??
Cheekysoft

1

Je me rends compte que la question autorise JavaScript, mais voici une solution CSS pure que j'ai élaborée qui permet également au tableau de se développer horizontalement. Il a été testé avec Internet Explorer 10 et les derniers navigateurs Chrome et Firefox. Un lien vers jsFiddle est en bas.

Le HTML:

Putting some text here to differentiate between the header
aligning with the top of the screen and the header aligning
with the top of one of its ancestor containers.

<div id="positioning-container">
<div id="scroll-container">
    <table>
        <colgroup>
            <col class="col1"></col>
            <col class="col2"></col>
        </colgroup>
        <thead>
            <th class="header-col1"><div>Header 1</div></th>
            <th class="header-col2"><div>Header 2</div></th>
        </thead>
        <tbody>
            <tr><td>Cell 1.1</td><td>Cell 1.2</td></tr>
            <tr><td>Cell 2.1</td><td>Cell 2.2</td></tr>
            <tr><td>Cell 3.1</td><td>Cell 3.2</td></tr>
            <tr><td>Cell 4.1</td><td>Cell 4.2</td></tr>
            <tr><td>Cell 5.1</td><td>Cell 5.2</td></tr>
            <tr><td>Cell 6.1</td><td>Cell 6.2</td></tr>
            <tr><td>Cell 7.1</td><td>Cell 7.2</td></tr>

        </tbody>
    </table>
</div>
</div>

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

Pour ceux qui ont essayé la belle solution proposée par Maximilian Hils et qui n'ont pas réussi à la faire fonctionner avec Internet Explorer, j'ai eu le même problème (Internet Explorer 11) et j'ai découvert quel était le problème.

Dans Internet Explorer 11, la transformation de style (au moins avec translate) ne fonctionne pas <THEAD>. J'ai résolu ce problème en appliquant le style à tous les éléments <TH>d'une boucle. Ça a marché. Mon code JavaScript ressemble à ceci:

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

Dans mon cas, la table était une GridView dans ASP.NET. J'ai d'abord pensé que c'était parce qu'il n'en avait pas <THEAD>, mais même quand je l'ai forcé à en avoir un, cela n'a pas fonctionné. Ensuite, j'ai découvert ce que j'ai écrit ci-dessus.

C'est une solution très agréable et simple. Sur Chrome, c'est parfait, sur Firefox un peu saccadé et sur Internet Explorer encore plus saccadé. Mais dans l'ensemble, une bonne solution.


0

J'aimerais avoir trouvé la solution de @ Mark plus tôt, mais je suis allé écrire la mienne avant de voir cette question SO ...

Le mien est un plugin jQuery très léger qui prend en charge l'en-tête, le pied de page, l'étendue des colonnes (colspan), le redimensionnement, le défilement horizontal et un nombre facultatif de lignes à afficher avant le début du défilement.

jQuery.scrollTableBody (GitHub)

Tant que vous avez une table avec une bonne <thead>, <tbody>et ( en option) <tfoot>, tout ce que vous devez faire est la suivante:

$('table').scrollTableBody();

0

J'ai trouvé cette solution de contournement - déplacer la ligne d'en-tête dans un tableau au-dessus du tableau avec des données:

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


fonctionne pour les petites tables, mais si vous avez un défilement horizontal, cette solution ne fonctionnera pas.
crh225

Cela ne fonctionnera pas non plus correctement car les colonnes du tableau ne s'aligneront pas. Ici vous forcez la largeur pour td mais il ne faut pas le faire ...
Ziggler

0

En appliquant les StickyTableHeaders in jQuery au tableau, les en-têtes de colonne resteront en haut de la fenêtre lorsque vous faites défiler vers le bas.

Exemple:

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

J'aime la réponse de Maximillian Hils mais j'ai eu quelques problèmes:

  1. la transformation ne fonctionne pas dans Edge ou IE sauf si vous l'appliquez au th
  2. l'en-tête scintille pendant le défilement dans Edge et IE
  3. ma table est chargée en utilisant ajax, donc je voulais attacher à l'événement de défilement de la fenêtre plutôt qu'à l'événement de défilement du wrapper

Pour se débarrasser du scintillement, j'utilise un délai d'attente pour attendre que l'utilisateur ait terminé le défilement, puis j'applique la transformation - de sorte que l'en-tête n'est pas visible pendant le défilement.

J'ai également écrit ceci en utilisant jQuery, un avantage de cela étant que jQuery devrait gérer les préfixes des fournisseurs pour vous

    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 table est enveloppée dans un div avec la classe table-container-fixed.

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

J'ai défini border-collapse pour se séparer car sinon nous perdons des bordures pendant la traduction, et je supprime la bordure sur la table pour arrêter le contenu apparaissant juste au-dessus de la cellule où se trouvait la bordure pendant le défilement.

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

Je fais le thfond blanc pour couvrir les cellules en dessous, et j'ajoute une bordure qui correspond à la bordure du tableau - qui est stylisée en utilisant Bootstrap et défilée hors de vue.

 .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

Utilisez la dernière version de jQuery et incluez le code JavaScript suivant.

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

1
Cela ne semble pas fonctionner. Peut-être pourriez-vous clarifier ce que vous voulez que nous fassions?
Chris

1
Quelle div? Nous parlons de tables ici
isapir

0

Ce n'est pas une solution exacte à la ligne d'en-tête fixe, mais j'ai créé une méthode plutôt ingénieuse de répéter la ligne d'en-tête tout au long du long tableau, tout en conservant la possibilité de trier.

Cette petite option soignée nécessite le plugin jQuerytablesorter . Voici comment ça fonctionne:

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>

Évidemment, ma table a beaucoup plus de lignes que cela. 193 pour être exact, mais vous pouvez voir où la ligne d'en-tête se répète. La ligne d'en-tête extensible est configurée par cette fonction:

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

Beaucoup de gens semblent chercher cette réponse. Je l'ai trouvé enterré dans une réponse à une autre question ici: Synchronisation de la largeur des colonnes entre les tables dans deux cadres différents, etc.

Parmi les dizaines de méthodes que j'ai essayées, c'est la seule que j'ai trouvée qui fonctionne de manière fiable pour vous permettre d'avoir une table inférieure défilante avec la table d'en-tête ayant les mêmes largeurs.

Voici comment je l'ai fait, j'ai d'abord amélioré le jsfiddle ci-dessus pour créer cette fonction, qui fonctionne sur les deux tdet th(dans le cas où cela déclenche d'autres utilisateurs qui utilisent thpour le style de leurs lignes d'en-tête).

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

Ensuite, vous devez créer deux tables, NOTEZ que la table d'en-tête doit avoir un supplément TDpour laisser de la place dans la table supérieure pour la barre de défilement, comme ceci:

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

Ensuite, faites quelque chose comme:

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

C'est la seule solution que j'ai trouvée sur Stack Overflow qui fonctionne à partir de nombreuses questions similaires qui ont été publiées, qui fonctionne dans tous mes cas.

Par exemple, j'ai essayé le plugin jQuery stickytables qui ne fonctionne pas avec durandal, et le projet Google Code ici https://code.google.com/p/js-scroll-table-header/issues/detail?id=2

D'autres solutions impliquant le clonage des tables, ont de mauvaises performances ou sont nulles et ne fonctionnent pas dans tous les cas.

Il n'y a pas besoin de ces solutions trop complexes. Créez simplement deux tableaux comme les exemples ci-dessous et appelez la fonction setHeaderTableWidth comme décrit ici et boom, vous avez terminé .

Si cela ne fonctionne pas pour vous, vous jouiez probablement avec votre propriété CSS de dimensionnement de boîte et vous devez la définir correctement. Il est facile de foirer votre contenu CSS par accident. Il y a beaucoup de choses qui peuvent mal tourner, alors soyez conscient / prudent de cela. Cette approche fonctionne pour moi .


0

Voici une solution avec laquelle nous avons fini par travailler (afin de traiter certains cas marginaux et les anciennes versions d'Internet Explorer, nous avons finalement effacé la barre de titre lors du défilement, puis l'avons réintégrée à la fin du défilement, mais dans les navigateurs Firefox et WebKit cette solution fonctionne . Elle suppose border-collapse: collapse.

La clé de cette solution est qu'une fois que vous appliquez border-collapse , les transformations CSS fonctionnent sur l'en-tête, il s'agit donc simplement d'intercepter les événements de défilement et de définir correctement la transformation. Vous n'avez pas besoin de dupliquer quoi que ce soit. À moins que ce comportement soit correctement implémenté dans le navigateur, il est difficile d'imaginer une solution plus légère.

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

Il est implémenté comme un simple plugin jQuery. Vous rendez simplement votre tête collante avec un appel comme $ ('thead'). Sticky (), et ils vont traîner. Cela fonctionne pour plusieurs tableaux sur une page et des sections d'en-tête à mi-chemin des grands tableaux.

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

bonne solution mais comment inclure les bordures de colonne entre les colonnes (les deux dans un en-tête fixe, aligné sur les données td)?
user5249203

Je ne suis pas sûr de comprendre votre problème. border-collapse ne vous empêche pas d'utiliser des bordures, des marges, etc., il supprime simplement les métriques de la table vaudou d'autrefois.
podperson

1
Ajoutez border: 2px solid red;à th, faites défiler et vous verrez le problème. J'ai trouvé moi-même cette solution plus basique: jsfiddle.net/x6pLcor9/19
calandoa

Ajoutez la même bordure dimensionnée à td et il n'y a aucun problème. Je ne vois pas votre point. Votre version est beaucoup plus propre et n'utilise pas jQuery, donc j'irais certainement avec quelque chose de plus comme ça aujourd'hui. (Bien que, franchement, je ne pense pas que j'utiliserais une table du tout aujourd'hui.)
podperson

0

Voici une réponse améliorée à celle publiée par Maximilian Hils .

Celui-ci fonctionne dans Internet Explorer 11 sans aucun scintillement:

var headerCells = tableWrap.querySelectorAll("thead td");
for (var i = 0; i < headerCells.length; i++) {
    var headerCell = headerCells[i];
    headerCell.style.backgroundColor = "silver";
}
var lastSTop = tableWrap.scrollTop;
tableWrap.addEventListener("scroll", function () {
    var stop = this.scrollTop;
    if (stop < lastSTop) {
        // Resetting the transform for the scrolling up to hide the headers
        for (var i = 0; i < headerCells.length; i++) {
            headerCells[i].style.transitionDelay = "0s";
            headerCells[i].style.transform = "";
        }
    }
    lastSTop = stop;
    var translate = "translate(0," + stop + "px)";
    for (var i = 0; i < headerCells.length; i++) {
        headerCells[i].style.transitionDelay = "0.25s";
        headerCells[i].style.transform = translate;
    }
});

0

J'ai développé un plug-in jQuery léger et simple pour convertir un tableau HTML bien formaté en un tableau déroulant avec un en-tête et des colonnes de tableau fixes.

Le plugin fonctionne bien pour faire correspondre le positionnement pixel à pixel de la section fixe avec la section déroulante. De plus, vous pouvez également figer le nombre de colonnes qui seront toujours visibles lors du défilement horizontal.

Démo et documentation: http://meetselva.github.io/fixed-table-rows-cols/

Référentiel GitHub: https://github.com/meetselva/fixed-table-rows-cols

Vous trouverez ci-dessous l'utilisation d'une table simple avec un en-tête fixe,

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

Qu'est-ce qu'un "tableau HTML bien" ?
Peter Mortensen

@PeterMortensen aurait dû être "HTML bien formaté". Modifié, merci.
Selvakumar Arumugam

0
<html>
<head>
    <script src="//cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script>
    <script>
        function stickyTableHead (tableID) {
            var $tmain = $(tableID);
            var $tScroll = $tmain.children("thead")
                .clone()
                .wrapAll('<table id="tScroll" />')
                .parent()
                .addClass($(tableID).attr("class"))
                .css("position", "fixed")
                .css("top", "0")
                .css("display", "none")
                .prependTo("#tMain");

            var pos = $tmain.offset().top + $tmain.find(">thead").height();


            $(document).scroll(function () {
                var dataScroll = $tScroll.data("scroll");
                dataScroll = dataScroll || false;
                if ($(this).scrollTop() >= pos) {
                    if (!dataScroll) {
                        $tScroll
                            .data("scroll", true)
                            .show()
                            .find("th").each(function () {
                                $(this).width($tmain.find(">thead>tr>th").eq($(this).index()).width());
                            });
                    }
                } else {
                    if (dataScroll) {
                        $tScroll
                            .data("scroll", false)
                            .hide()
                        ;
                    }
                }
            });
        }

        $(document).ready(function () {
            stickyTableHead('#tMain');
        });
    </script>
</head>

<body>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>
    gfgfdgsfgfdgfds<br/>

    <table id="tMain" >
        <thead>
        <tr>
            <th>1</th> <th>2</th><th>3</th> <th>4</th><th>5</th> <th>6</th><th>7</th> <th>8</th>

        </tr>
        </thead>
        <tbody>
            <tr><td>11111111111111111111111111111111111111111111111111111111</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
            <tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5555555</td><td>66666666666</td><td>77777777777</td><td>8888888888888888</td></tr>
        </tbody>
    </table>
</body>
</html>

0

Complément à la réponse de @Daniel Waltrip. La table doit être entourée de div position: relativepour fonctionner avec position:sticky. Je voudrais donc publier mon exemple de code ici.

CSS

/* Set table width/height as you want.*/
div.freeze-header {
  position: relative;
  max-height: 150px;
  max-width: 400px;
  overflow:auto;
}

/* Use position:sticky to freeze header on top*/
div.freeze-header > table > thead > tr > th {
  position: sticky;
  top: 0;
  background-color:yellow;
}

/* below is just table style decoration.*/
div.freeze-header > table {
  border-collapse: collapse;
}

div.freeze-header > table td {
  border: 1px solid black;
}

HTML

<html>
<body>
  <div>
   other contents ...
  </div>
  <div>
   other contents ...
  </div>
  <div>
   other contents ...
  </div>

  <div class="freeze-header">
    <table>
       <thead>
         <tr>
           <th> header 1 </th>
           <th> header 2 </th>
           <th> header 3 </th>
           <th> header 4 </th>
           <th> header 5 </th>
           <th> header 6 </th>
           <th> header 7 </th>
           <th> header 8 </th>
           <th> header 9 </th>
           <th> header 10 </th>
           <th> header 11 </th>
           <th> header 12 </th>
           <th> header 13 </th>
           <th> header 14 </th>
           <th> header 15 </th>
          </tr>
       </thead>
       <tbody>
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
         <tr>
           <td> data 1 </td>
           <td> data 2 </td>
           <td> data 3 </td>
           <td> data 4 </td>
           <td> data 5 </td>
           <td> data 6 </td>
           <td> data 7 </td>
           <td> data 8 </td>
           <td> data 9 </td>
           <td> data 10 </td>
           <td> data 11 </td>
           <td> data 12 </td>
           <td> data 13 </td>
           <td> data 14 </td>
           <td> data 15 </td>
          </tr>         
       </tbody>
    </table>
  </div>
</body>
</html>

Démo

entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.