HTML-таблица с фиксированными заголовками? - PullRequest
220 голосов
/ 23 марта 2009

Существует ли кросс-браузерная техника CSS / JavaScript для отображения длинной таблицы HTML, при которой заголовки столбцов остаются фиксированными на экране и не прокручиваются вместе с телом таблицы. Подумайте об эффекте «стоп-панели» в Microsoft Excel.

Я хочу иметь возможность прокручивать содержимое таблицы, но всегда иметь возможность видеть заголовки столбцов вверху.

Ответы [ 28 ]

172 голосов
/ 18 сентября 2014

Это можно решить в четыре строки кода.

Если вы заботитесь только о современных браузерах, фиксированный заголовок может быть значительно проще с помощью CSS-преобразований. Звучит странно, но прекрасно работает:

  • HTML и CSS остаются как есть.
  • Нет внешних зависимостей JavaScript.
  • Четыре строки кода.
  • Работает для всех конфигураций (макет таблицы: исправлено и т. Д.).
document.getElementById("wrap").addEventListener("scroll", function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});

Поддержка CSS-преобразований широко доступна , за исключением Internet Explorer 8 -.

Вот полный пример для справки:

document.getElementById("wrap").addEventListener("scroll",function(){
   var translate = "translate(0,"+this.scrollTop+"px)";
   this.querySelector("thead").style.transform = translate;
});
/* Your existing container */
#wrap {
    overflow: auto;
    height: 400px;
}

/* CSS for demo */
td {
    background-color: green;
    width: 200px;
    height: 100px;
}
<div id="wrap">
    <table>
        <thead>
            <tr>
                <th>Foo</th>
                <th>Bar</th>
            </tr>
        </thead>
        <tbody>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
            <tr><td></td><td></td></tr>
        </tbody>
    </table>
</div>
84 голосов
/ 12 октября 2011

Я некоторое время искал решение для этого и обнаружил, что большинство ответов не работают или не подходят для моей ситуации, поэтому я написал простое решение с помощью jQuery.

Это схема решения:

  1. Клонируйте таблицу, которая должна иметь фиксированный заголовок, и поместите клонированная копия поверх оригинала.
  2. Снимите тело стола с верхнего стола.
  3. Удалить заголовок таблицы из нижней таблицы.
  4. Настройка ширины столбца. (Мы отслеживаем исходную ширину столбца)

Ниже приведен код в работающей демоверсии.

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>

Это решение работает в Chrome и IE. Поскольку он основан на jQuery, он должен работать и в других браузерах, поддерживаемых jQuery.

57 голосов
/ 06 октября 2009

Я только что закончил собирать плагин jQuery, который будет принимать действительную единственную таблицу с использованием действительного HTML (должен иметь thead и tbody) и выводить таблицу с фиксированными заголовками, необязательный фиксированный нижний колонтитул, который может быть клонирован. заголовок или любой контент, который вы выбрали (нумерация страниц и т. д.). Если вы хотите воспользоваться преимуществами более крупных мониторов, он также изменит размер таблицы при изменении размера браузера. Еще одна добавленная возможность - возможность боковой прокрутки, если столбцы таблицы не могут все вписываться в вид.

http://fixedheadertable.com/

на github: http://markmalek.github.com/Fixed-Header-Table/

Его чрезвычайно легко настроить, и вы можете создавать свои собственные стили для него. Он также использует закругленные углы во всех браузерах. Имейте в виду, что я только что выпустил его, так что он все еще технически бета и у меня очень мало мелких проблем, которые я сглаживаю.

Работает в Internet Explorer 7, Internet Explorer 8, Safari, Firefox и Chrome.

23 голосов
/ 04 декабря 2013

Я также создал плагин для решения этой проблемы. Мой проект - jQuery.floatThead существует уже более 4 лет и является очень зрелым.

Он не требует внешних стилей и не ожидает, что ваш стол будет стилизован каким-либо особым образом. Он поддерживает Internet Explorer9 + и Firefox / Chrome.

В настоящее время (2018-05) он имеет:

405 коммитов и 998 звезд на GitHub


Многие (не все) ответы здесь являются быстрыми взломами, которые, возможно, решили проблему, с которой столкнулся один человек, но будут работать не для каждой таблицы.

Некоторые другие плагины старые и, вероятно, отлично работают с Internet Explorer, но будут работать с Firefox и Chrome.

20 голосов
/ 17 ноября 2013

TL; DR

Если вы нацелены на современные браузеры и не нуждаетесь в экстравагантных стилях: http://jsfiddle.net/dPixie/byB9d/3/ ... Хотя версия big four довольно приятна, эта версия намного лучше обрабатывает ширину флюида .

Хорошие новости всем!

Благодаря достижениям HTML5 и CSS3 это стало возможным, по крайней мере, для современных браузеров. Немного хакерскую реализацию, с которой я столкнулся, можно найти здесь: http://jsfiddle.net/dPixie/byB9d/3/. Я тестировал ее в FX 25, Chrome 31 и IE 10 ...

Соответствующий HTML (хотя в начале документа укажите HTML5-тип документа):

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

С этим CSS:

html, body{
  margin:0;
  padding:0;
  height:100%;
}
section {
  position: relative;
  border: 1px solid #000;
  padding-top: 37px;
  background: #500;
}
section.positioned {
  position: absolute;
  top:100px;
  left:100px;
  width:800px;
  box-shadow: 0 0 15px #333;
}
.container {
  overflow-y: auto;
  height: 200px;
}
table {
  border-spacing: 0;
  width:100%;
}
td + td {
  border-left:1px solid #eee;
}
td, th {
  border-bottom:1px solid #eee;
  background: #ddd;
  color: #000;
  padding: 10px 25px;
}
th {
  height: 0;
  line-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  color: transparent;
  border: none;
  white-space: nowrap;
}
th div{
  position: absolute;
  background: transparent;
  color: #fff;
  padding: 9px 25px;
  top: 0;
  margin-left: -25px;
  line-height: normal;
  border-left: 1px solid #800;
}
th:first-child div{
  border: none;
}

Но как?!

Проще говоря, у вас есть заголовок таблицы, который вы визуально скрываете, делая его высотой 0px, который также содержит элементы div, используемые в качестве фиксированного заголовка. Контейнер таблицы оставляет достаточно места наверху, чтобы обеспечить абсолютно позиционированный заголовок, и таблица с полосами прокрутки выглядит так, как вы ожидаете.

Приведенный выше код использует позиционированный класс для абсолютного позиционирования таблицы (я использую его во всплывающем диалоговом окне), но вы также можете использовать его в потоке документа, удалив класс positioned из контейнера .

Но ...

Это не идеально. Firefox отказывается делать строку заголовка 0px (по крайней мере, я не нашел никакого способа), но упрямо удерживает ее на уровне не менее 4px ... Это не большая проблема, но в зависимости от вашего стиля это будет портить ваши границы и т. Д.

В таблице также используется подход с использованием искусственного столбца, в котором цвет фона самого контейнера используется в качестве фона для заголовочных элементов div, которые являются прозрачными.

Краткое описание

В целом могут возникнуть проблемы с оформлением в зависимости от ваших требований, особенно границ или сложного фона. Могут также быть проблемы с вычислимостью, я еще не проверял его во множестве браузеров (пожалуйста, прокомментируйте ваш опыт, если вы попробуете это), но я не нашел ничего подобного, поэтому я подумал, что это стоит опубликовать в любом случае ...

19 голосов
/ 25 октября 2012

Все попытки решить эту проблему вне спецификации CSS являются бледной тенью того, что мы действительно хотим: поставка на основе подразумеваемого обещания THEAD.

Эта проблема «замороженные заголовки для таблицы» уже давно является открытой раной в HTML / CSS.

В идеальном мире было бы чисто CSS-решение для этой проблемы. К сожалению, похоже, что нет хорошего.

Соответствующие обсуждения стандартов на эту тему включают:

ОБНОВЛЕНИЕ : Firefox поставляется position:sticky в версии 32. Все выигрывают!

14 голосов
/ 22 августа 2012

Вот плагин jQuery для фиксированных заголовков таблиц. Это позволяет прокручивать всю страницу, замораживая заголовок, когда он достигает вершины. Хорошо работает с таблицами Twitter Bootstrap .

GitHub репозиторий: https://github.com/oma/table-fixed-header

Он не прокручивает только содержимое таблицы. Посмотрите на другие инструменты для этого, как один из этих других ответов. Вы сами решаете, что лучше подходит для вашего случая.

9 голосов
/ 27 ноября 2012

Большинство решений, размещенных здесь, требуют jQuery. Если вы ищете независимое от фреймворка решение, попробуйте Grid: http://www.matts411.com/post/grid/

Он размещен на Github здесь: https://github.com/mmurph211/Grid

Он не только поддерживает фиксированные верхние колонтитулы, но также поддерживает фиксированные левые столбцы и нижние колонтитулы, между прочим.

6 голосов
/ 13 сентября 2014

Более усовершенствованная таблица прокрутки чистого CSS

Все чистые CSS-решения, которые я видел до сих пор - хотя они и умные, - не имеют определенного уровня полировки или просто не работают должным образом в некоторых ситуациях. Итак, я решил создать свой собственный ...

Особенности:

  • Это чистый CSS, поэтому не требуется jQuery (или вообще никакого кода JavaScript, для этого материя)
  • Вы можете установить ширину таблицы в процентах (например, «жидкость») или фиксированном значении, или позволить содержимому определять его ширину (например, «авто»)
  • Ширина столбцов также может быть изменяемой, фиксированной или автоматической.
  • Столбцы никогда не будут смещены с заголовками из-за горизонтальной прокрутки (проблема, которая возникает в любом другом решении на основе CSS, которое я видел, которое не требует фиксированной ширины).
  • Совместим со всеми популярными настольными браузерами, включая Internet Explorer до версии 8
  • Чистый, полированный внешний вид; нет неаккуратных зазоров в 1 пиксель или смещенных границ; выглядит одинаково во всех браузерах

Вот несколько скрипок, которые показывают параметры жидкости и автоматической ширины:

  • Ширина и высота жидкости (адаптируется к размеру экрана): jsFiddle (Обратите внимание, что полоса прокрутки отображается только при необходимости в этой конфигурации, поэтому вам может понадобиться уменьшить рамку, чтобы увидеть ее)

  • Автоматическая ширина, фиксированная высота (проще интегрировать с другим контентом): jsFiddle

Конфигурация с автоматической шириной и фиксированной высотой, вероятно, имеет больше вариантов использования, поэтому я опубликую код ниже.

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

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

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

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

Метод, который я использовал, чтобы заморозить строку заголовка, похож на метод d-Pixie, поэтому обратитесь к его сообщению за разъяснениями. В этой технике было множество ошибок и ограничений, которые можно было исправить только кучами дополнительного CSS и дополнительным контейнером div или двумя.

5 голосов
/ 05 июля 2012

Простой плагин jQuery

Это вариант решения Махеса. Вы можете назвать это как $('table#foo').scrollableTable();

Идея такова:

  • Разделите thead и tbody на отдельные table элементы
  • Сделайте так, чтобы их ширина снова совпадала
  • Заверните второй table в div.scrollable
  • Используйте CSS, чтобы div.scrollable прокручивал

CSS может быть:

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

Предостережения

  • Очевидно, что разбиение этих таблиц делает разметку менее семантической. Я не уверен, как это повлияет на доступность.
  • Этот плагин не работает с нижними колонтитулами, несколькими заголовками и т. Д.
  • Я тестировал его только в Chrome версии 20.

Тем не менее, это работает для моих целей, и вы можете взять и изменить его.

Вот плагин:

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);
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...