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


231

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

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


3
Попробуйте это: Чистая CSS прокручиваемая таблица с фиксированным заголовком. РЕДАКТИРОВАТЬ : эта должна работать в Internet Explorer 7, как показано в примере : Прокрутка HTML-таблицы с фиксированным заголовком. РЕДАКТИРОВАНИЕ 2: Я нашел пару дополнительных ссылок, которые могут быть полезны: - Глупо исправлен заголовок - плагин jQuery с некоторыми ограничениями. - [Фиксированные заголовки таблиц] ( cross-browser.com/x/examp
gcores

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

9
В 2018 году, все браузеры могут использовать следующее простое решение: thead th { position: sticky; top: 0; }. Safari нужен префикс поставщика:-webkit-sticky
Даниэль Уолтрип

1
@DanielWaltrip, вы должны добавить этот ответ, чтобы он мог быть поставлен на первое место - все остальные ответы избыточны с позицией: липкая - лучшая поддержка в настоящее время
Питер Керр,

Ответы:


88

Некоторое время я искал решение для этого и обнаружил, что большинство ответов не работают или не подходят для моей ситуации, поэтому я написал простое решение с помощью 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.


4
и как мы можем решить проблему, когда содержание больше ширины?
Мэрц

1
@tetra td {max-width: 30px; } это позволит вам контролировать, как отображаются строки.
Любен Тодоров

Но что, если содержимое в некоторой ячейке заголовка длиннее, чем в ячейках td? Я пробовал это в IE7, и width () ломает все. IE8 и IE9 работают нормально, хотя ...
JustAMartin

4
К сожалению, если вам требуется точное выравнивание столбцов по пикселям, это не сработает: jsbin.com/elekiq/1 ( исходный код ). Вы можете видеть, что некоторые заголовки немного смещены относительно того места, где они должны быть. Эффект более очевиден, если вы используете фоны: jsbin.com/elekiq/2 ( исходный код ). (Я работал в том же духе, наткнулся на это в своем коде, нашел ваш и подумал: «Интересно, решил ли он это для меня!» К сожалению, нет. :-)) Браузеры - ТАКАЯ боль от желания контролировать ширины ячеек ...
TJ Crowder

Похоже, что это не работает с горизонтальной прокруткой - он создает заголовок, но он выходит за пределы области прокрутки (заметно) и не прокручивает содержимое.
Сбой

183

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

Если вы заботитесь только о современных браузерах, то с помощью 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-.

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


8
Я должен сказать, что, несмотря на мой предыдущий комментарий, это самое близкое к идеальному решению, которое я видел. Даже горизонтальная прокрутка идеальна (лучше, чем мое собственное решение). Вот пример с границами (вы не можете использовать border-collapse) и полосой прокрутки, которая придерживается таблицы вместо контейнера: jsfiddle
DoctorDestructo

11
Выяснилось, что это работает, но нужно применить преобразование к th / td, а не к thead.
рыжий

5
@AlexAlexeev, ваше решение великолепно. Спасибо. Я заметил, что полученный фиксированный заголовок не имеет границ, которые различают столбцы. Стиль CSS по умолчанию потерян. Даже когда я включаю это ... $(this).addClass('border')изменяет остальную часть таблицы со шрифтами, размером, цветом, которые я передаю в классе границы. Но, не добавляет строки в фиксированный заголовок. Цените любые входные данные о том, как это исправить
user5249203

5
@ user5249203 Я знаю, что вы спросили несколько месяцев назад, но у меня была та же проблема, и это было из-за коллапса границы: посмотрите это: stackoverflow.com/questions/33777751/… .
Archz

6
Это не работает ни в одной версии IE или в Edge. Вот версия , которая делает на основе комментариев @ рыжей jsfiddle.net/n6o8ocwb/2
отнимает

58

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

http://fixedheadertable.com/

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

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

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


Спасибо! Я добавляю новую версию позже сегодня, когда вернусь с работы. Вот ссылка на мою запись в блоге с тем, что я добавил: fixedheadertable.mmalek.com/2009/10/07/...
Mark

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

В вашей демонстрации ширина отключена в ie6 :-( заголовок таблицы и тело не выровнены.
Cheekysoft

4
Последняя версия не работает в IE6. Я больше не поддерживаю IE6.
Марк

отличная работа Марк - к сожалению, есть некоторые проблемы с прокруткой фиксированного заголовка и столбца на мобильных устройствах (iPad, планшет Android) - когда я прокручиваю контент, эти фиксированные части не прокручиваются - когда я прекращаю прокручивать и нажимаю один раз на таблицу фиксированные части «прыгают» в правильные положения - есть ли простой способ исправить это?
Okizb

23

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

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

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

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


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

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


1
Отличный плагин, поддерживает вложенные таблицы и смещения.
Михай Алекс

2
Отлично. Большое спасибо. Плагин хорошо работал в Firefox 45.2, Chromium 51 и IE 11. Кроме того, он не мешает большому количеству кода JS и jQuery, созданного на одной странице.
Альдо Парадизо

Спасибо. Я рад сообщить, что проект получает примерно 1 сообщение об ошибке каждые 4 месяца. Я не делаю много критических изменений. Это довольно солидно и работает.
mkoryak

20

TL; DR

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

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

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

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

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>

Но как?!

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

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

Но ...

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

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

Резюме

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


Если вы уменьшаете ширину окна до тех пор, пока не появится горизонтальная прокрутка, то заголовок не будет прокручиваться горизонтально вместе с телом. Штопать.
dlaliberte

@dlaliberte - Ну, так как заголовок и таблица на самом деле два разных элемента, вы, конечно, можете попасть в странность. Но мой пример не допускает переполнения столбцов таблицы, а заголовки обычно легче контролировать, чем содержимое таблицы. Тем не менее, если вы заставите заголовок «переполниться», он будет торчать справа от таблицы и будет выглядеть сильно разбитым. Вы можете исправить это, установив минимальную ширину таблицы, заставив ее также переполнить страницу ... Но это хак, поэтому она никогда не будет идеальной ...
Йонас Шуберт Эрландссон

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

1
@Cheekysoft - Нет, содержимое таблицы и строки могут свободно перемещаться. Контейнер, в моем примере <section>элемент, должен быть ограничен по высоте только для того, чтобы заставить его переполниться и показать прокрутку. Любой макет, который сделает переполнение контейнера будет работать. Если вы обнаружите случай, когда он не появится, пожалуйста, оставьте ссылку на скрипку.
Йонас Шуберт Эрландссон

Жестко закодированное padding-topзначение также означает, что если текст заголовка таблицы занимает более одной строки, он будет отображаться поверх ячеек таблицы. Жаль, потому что это работает как очарование большую часть времени. Действительно хороший трюк с divв thобойти вопрос колонка проклейки большинства других решений есть.
Бернхард Хофманн

19

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

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

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

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

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


Было бы замечательно, чтобы колонны с лисами были одинаковыми
Csaba Toth

4
Число рейнольдса Firefox и position: sticky, он не работает для заголовков таблиц: bugzilla.mozilla.org/show_bug.cgi?id=925259#c8 ... Патч для этой ошибки прямо заявляет: «В настоящее время мы не поддерживаем относительное позиционирование внутренних элементов таблицы, поэтому мы также исключаем их из закрепления. "
Йонас Шуберт Эрландссон

2
Это работает во всех браузерах прямо сейчас: thead th { position: sticky; top: 0; }. Можем ли мы обновить этот ответ, чтобы четко заявить об этом?
Даниэль Уолтрип

1
@DanielWaltrip все браузеры? stackoverflow.com/a/37646284/3640407 Еще больше MSIE, чем Edges
edc65

Честная оценка. По словам caniuse.com/#search=position%3Asticky
Даниэль Уолтрип,

14

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

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

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


1
Облом - ссылка на пример мертва. "Ой! Денне Сиден Бле Икке Фуннет ..." Жаль, что код не был вставлен сюда.
JosephK

да ... извините за это. удалил ссылку. Проект больше не поддерживается
oma

Не беспокойтесь - я попробовал несколько из этих готовых предполагаемых решений - ни одно из них не работало с таблицей flex-col-width, которая превышала ширину экрана. Я закончил писать свое собственное решение.
JosephK

9

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

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

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


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

1
Эй, я тебя знаю! Похоже, мы написали очень похожее дерьмо ( github.com/mkoryak/floatThead ) - Миша
mkoryak

9

Свойство CSS position: stickyимеет большую поддержку в большинстве современных браузеров (у меня были проблемы с Edge, см. Ниже).

Это позволяет нам легко решить проблему фиксированных заголовков:

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

Safari необходим префикс поставщика: -webkit-sticky.

Для Firefox мне пришлось добавить min-height: 0к одному из родительских элементов. Я точно забыл, зачем это было нужно.

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


Использование position: sticky;с таблицей внутри DIV , который имеет overflow: scroll;, overflow-x: scroll;или overflow-y: scroll;. представляется лучшим и самым простым решением для фиксированных заголовков таблиц и столбцов в современных браузерах. Этот ответ должен быть поставлен на голосование.
Аберрант

Это просто, но эффективно. Это то, что я ищу. Спасибо.
Catbuilts

6

Более изысканная таблица прокрутки с чистым 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;
  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]-->

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


Этот ответ недооценен! Я потратил несколько дней, пытаясь заставить другие решения работать на мой особенно раздражающий случай. Каждый из них так или иначе не выровнялся. Это, наконец, сделал это! Поначалу кажется слишком сложным, но как только вы это освоите, это здорово. В конце вы можете удалить совсем немного
Джастин Сейн,

1
@JustinSane Рад, что тебе нравится! Я предполагаю, что отсутствие оценки связано с тем, что он делится страницей с удивительным решением Максимилиана Хилса . Если вы не против использования чуть-чуть JS, вам обязательно стоит это проверить.
Доктор Деструкто

Черт, что это почти идеальное решение действительно. В любом случае, я использовал jQuery, пытался заставить его работать с этим, прежде чем нашел ваш (через ваш комментарий к другому вопросу). Не думал о слушателе прокрутки и переводе ... Ну, они говорят, что требуется гений, чтобы придумать простые решения ...;) Я закончил проект, и он отлично работает без JS, но я буду держать это в ум на будущее. Тем не менее, снимаю шляпу перед тобой за то, что ты классный!
Джастин Сейн

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

1
@MattArnold Исправлено. Спасибо за совет!
Доктор Деструкто

5

Простой плагин 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);
};

1
Хороший сценарий, этот работал лучше всего в моей среде. Я расширил ваш скрипт с фиксированной поддержкой нижнего колонтитула, проверьте мой пост ниже.
Гитаарик

4

:)

Не очень чистое, но чистое решение HTML / CSS.

table {
    overflow-x:scroll;
}

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

Обновлен для примера IE8 + JSFiddle


2
Хорошее решение, только упомянуть, что эти клетки плавали и так по содержанию может иметь разную высоту, это видно , если установить их границы: jsfiddle.net/ZdeEH/15
Stano

3

Поддержка фиксированного нижнего колонтитула

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

Использование:

Фиксированная высота:

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

Максимальная высота (если браузер поддерживает опцию CSS 'max-height'):

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

Автор сценария:

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

Каким-то образом я закончил Position:Stickyработать нормально на моем случае:

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
Это самое чистое решение, которое я видел до сих пор. caniuse показывает, что по состоянию на 5/2/2020 позиция без префикса: sticky пользуется 90,06% глобальной поддержки. Так что это решение хорошо работает во всех современных браузерах.
AlienKevin

2

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


3
Если вы заботитесь о доступности, это провал.
epascarello

1
Что касается доступности, может быть, мы можем заменить использование div на стили для <thead> и <tbody> ??
Cheekysoft

1

Я понимаю, что вопрос разрешает JavaScript, но я разработал чистое CSS-решение, которое также позволяет горизонтальному расширению таблицы. Он был протестирован с Internet Explorer 10 и последними браузерами Chrome и Firefox. Ссылка на jsFiddle находится внизу.

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>

И 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

Для тех, кто попробовал хорошее решение, данное Максимилианом Хилсом, и не смог заставить его работать с Internet Explorer, у меня была такая же проблема (Internet Explorer 11), и я выяснил, в чем проблема.

В Internet Explorer 11 преобразование стиля (по крайней мере, с переводом) не работает <THEAD>. Я решил это, применив стиль ко всему <TH>циклу. Это сработало. Мой код JavaScript выглядит так:

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

В моем случае таблица была GridView в ASP.NET. Сначала я подумал, что это потому, что его не было <THEAD>, но даже когда я заставил его иметь его, он не работал. Тогда я узнал, что я написал выше.

Это очень хорошее и простое решение. В Chrome он идеален, в Firefox немного вялен, а в Internet Explorer еще более вялен. Но в целом хорошее решение.


0

Я бы хотел найти решение @ Марка раньше, но я пошел и написал свое собственное, прежде чем увидел этот ТАК вопрос ...

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

jQuery.scrollTableBody (GitHub)

До тех пор , пока у вас есть таблица с собственно <thead>, <tbody>и ( по желанию) <tfoot>, все , что вам нужно сделать , это:

$('table').scrollTableBody();

0

Я нашел этот обходной путь - переместить строку заголовка в таблицу выше таблицы с данными:

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


работает для небольших таблиц, но если у вас горизонтальная прокрутка, это решение не будет работать.
crh225

Это также не будет работать должным образом, так как столбцы таблицы не будут выровнены. Здесь вы заставляете ширину для тд, но мы не должны делать ...
Зигглер

0

Применяя плагин StickyTableHeaders jQuery к таблице, заголовки столбцов будут прилипать к верхней части области просмотра при прокрутке вниз.

Пример:

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

Мне нравится ответ Максимиллиана Хилса, но у меня были некоторые проблемы:

  1. преобразование не работает в Edge или IE, если вы не примените его к
  2. заголовок мерцает во время прокрутки в Edge и IE
  3. моя таблица загружается с использованием ajax, поэтому я хотел прикрепить к событию прокрутки окна, а не к событию прокрутки оболочки

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

Я также написал это, используя jQuery, одно преимущество в том, что jQuery должен обрабатывать префиксы вендоров для вас

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

Таблица обернута в div с классом table-container-fixed.

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

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

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

Я делаю thфон белым, чтобы покрыть ячейки снизу, и добавляю границу, которая соответствует границе таблицы - которая стилизована с помощью Bootstrap и прокручивается вне поля зрения.

 .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

Используйте последнюю версию jQuery и включите следующий код JavaScript.

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

1
Это не похоже на работу. Может быть, вы могли бы уточнить, что вы хотите от нас?
Крис

1
Какой див? Мы говорим о столах здесь
Исапир

0

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

Этот аккуратный маленький вариант требует плагина jQuerytablesorter . Вот как это работает:

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>

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

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

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

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

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

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

Затем вам нужно создать две таблицы. ПРИМЕЧАНИЕ. В таблице заголовков должна быть дополнительная таблица, TDчтобы оставить место в верхней таблице для полосы прокрутки, например:

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

Затем сделайте что-то вроде:

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

Это единственное решение, которое я нашел в Переполнении стека, которое работает на основе множества похожих вопросов и работает во всех моих случаях.

Например, я попробовал плагин jQuery stickytables, который не работает с durandal, и проект Google Code здесь https://code.google.com/p/js-scroll-table-header/issues/detail?id=2

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

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

Если это не работает для вас, вы, вероятно, играете со своим свойством CSS для определения размера блока, и вам нужно установить его правильно. Это легко испортить ваш CSS-контент случайно. Есть много вещей, которые могут пойти не так, так что будьте осторожны. Этот подход работает для меня .


0

Вот решение, с которым мы в конечном итоге работали (для того, чтобы справиться с некоторыми крайними случаями и старыми версиями Internet Explorer, мы в конечном итоге также исчезли в строке заголовка при прокрутке, а затем вернули ее обратно, когда прокрутка заканчивается, но в браузерах Firefox и WebKit это решение просто работает и предполагает границу коллапс: коллапс.

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

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

Он реализован в виде простого плагина jQuery. Вы просто привязываете свою теаду к вызову типа $ ('thead'). Sticky (), и они будут зависать. Это работает для нескольких таблиц на странице и заголовках разделов на полпути больших таблиц.

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

хорошее решение, но как включить границы столбцов между столбцами (оба в фиксированном заголовке, выровненном по данным td)?
user5249203

Я не уверен, что понимаю вашу проблему. border-collapse не мешает вам использовать границы, поля и т. д., он просто удаляет показатели таблицы вуду прошлого.
podperson

1
Добавить border: 2px solid red;к th, свитка, и вы увидите эту проблему. Я сам придумал это более простое решение: jsfiddle.net/x6pLcor9/19
calandoa

Добавьте ту же размерную границу к td, и нет проблем. Я не понимаю вашу точку зрения. Ваша версия намного чище и не использует jQuery, так что я бы определенно выбрал нечто подобное сегодня. (Хотя, честно говоря, я не думаю, что я бы сегодня вообще использовал таблицу.)
podperson

0

Вот улучшенный ответ на ответ Максимилиана Хилса .

Этот работает в Internet Explorer 11 без мерцания:

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

Я разработал простой облегченный плагин jQuery для преобразования хорошо отформатированной таблицы HTML в прокручиваемую таблицу с фиксированным заголовком таблицы и столбцами.

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

Демонстрация и документация: http://meetselva.github.io/fixed-table-rows-cols/

Репозиторий GitHub: https://github.com/meetselva/fixed-table-rows-cols

Ниже приведено использование простой таблицы с фиксированным заголовком.

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

Что такое "хорошо HTML-таблица" ?
Питер Мортенсен

@PeterMortensen Должен был быть «хорошо отформатированный HTML». Отредактировано, спасибо.
Сельвакумар Арумугам

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

Дополнительно к @Daniel Waltrip answer. Таблицу нужно заключить в div position: relativeдля работы с position:sticky. Поэтому я хотел бы опубликовать мой пример кода здесь.

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>

демонстрация

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.