Blog. Just Blog

Таблица с фиксированной шапкой на JavaScript

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
В одном из рабочих проектов появилась необходимость выводить достаточно длинную таблицу с большим количеством однотипных колонок. При вертикальной прокрутке заголовок таблицы уезжает, и уже на втором скролле становится очень сложно сориентироваться, какая из колонок к чему относится. Проблема решается с помощью создания фиксированной шапки у таблицы, которая всегда будет отображаться, пока "родной" заголовок таблицы находится за пределами видимой области страницы.

Начнем с верстки. Для корректной работы скрипта нужно выполнение всего несколько требований. Таблица, у которой будет фиксироваться заголовок, обязательно должна иметь атрибут id и фиксированную ширину. Шапка таблицы должна быть обрамлена тегом thead. Например:
  1. <table id="my_table" style="width:980px;" rules="all">
  2. <thead>
  3. <tr>
  4.     <th style="width:20px;">N</th>
  5.     <th style="background: #BABABA;">Наименование</th>
  6.     <th>Описание</th>
  7.     <th style="width:60px;">Цена</th>
  8.     <th style="width:60px;">Количество</th>
  9.     <th style="width:60px;">Сумма</th>
  10. </tr>
  11. </thead>
  12.  
  13. <tr>
  14.     <td>1</td>
  15.     <td>Предмет 1</td>
  16.     <td>Описание 1</td>
  17.     <td>26</td>
  18.     <td>97</td>
  19.     <td>2522</td>
  20. </tr>
  21.  
  22. ... остальное содержимое таблицы
  23.  
  24. </table>
Обычно при верстке HTML я никогда не использую thead, но тут этот элемент необходим, чтобы обозначить границы и содержимое шапки таблицы. Дополнительных обрамляющих элементов для таблицы не требуется.

А вот сам скрипт, который надо подключить к странице. Можно вынести его в отдельный файл или добавить прямо в HTML-код, кому как нравится.
  1. //-----------------------------------------------------
  2. // Фиксированный заголовок у таблицы
  3. //-----------------------------------------------------
  4. // by ManHunter / PCL (www.manhunter.ru)
  5. //-----------------------------------------------------
  6. fix_header={
  7.   'fixed_el'null,
  8.   'new_table'null,
  9.  
  10.   bind : function(eleventNamecallback) {
  11.     if (el) {
  12.       if (el.addEventListener) {
  13.         el.addEventListener(eventNamecallbackfalse);
  14.       }
  15.       else if (el.attachEvent) {
  16.         el.attachEvent("on" eventNamecallback);
  17.       }
  18.     }
  19.   },
  20.  
  21.   get_position: function(el) {
  22.     var offsetLeft 0offsetTop 0;
  23.     do {
  24.       offsetLeft += el.offsetLeft;
  25.       offsetTop  += el.offsetTop;
  26.     }
  27.     while (el el.offsetParent);
  28.     return {x:offsetLefty:offsetTop};
  29.   },
  30.  
  31.   chk_position: function() {
  32.     var doc document.documentElement;
  33.     var body document.body;
  34.  
  35.     if (typeof(window.innerWidth) == 'number') {
  36.       my_width window.innerWidth;
  37.       my_height window.innerHeight;
  38.     }
  39.     else if (doc && (doc.clientWidth || doc.clientHeight)) {
  40.       my_width doc.clientWidth;
  41.       my_height doc.clientHeight;
  42.     }
  43.     else if (body && (body.clientWidth || body.clientHeight)) {
  44.       my_width body.clientWidth;
  45.       my_height body.clientHeight;
  46.     }
  47.  
  48.     if (doc.scrollTop) { dy=doc.scrollTop; } else { dy=body.scrollTop; }
  49.  
  50.     var coord=fix_header.get_position(fix_header.fixed_el);
  51.  
  52.     // Заголовок таблицы еще на экране или таблица уже не на экране
  53.     if (coord.y>dy || (coord.y+fix_header.fixed_el.clientHeight)<dy) {
  54.       fix_header.new_table.style.left='-9999px';
  55.     }
  56.     // Заголовок уже прокручен вверх
  57.     else {
  58.       fix_header.new_table.style.left=
  59.         fix_header.fixed_el.getBoundingClientRect().left+'px';
  60.     }
  61.   },
  62.  
  63.   fix: function (id) {
  64.     var tmp,st;
  65.     var ftable=document.getElementById(id);
  66.     if (ftable) {
  67.       if (this.new_table!=null) {
  68.         if (this.new_table.parentNode!=undefined) {
  69.           this.new_table.parentNode.removeChild(this.new_table);
  70.         }
  71.         this.new_table=null;
  72.       }
  73.       else {
  74.         this.bind(window,'scroll',this.chk_position);
  75.         this.bind(window,'resize',this.chk_position);
  76.       }
  77.  
  78.       this.fixed_el=ftable;
  79.  
  80.       tmp=ftable.getElementsByTagName('thead');
  81.       if (tmp) {
  82.         var fthead=tmp[0];
  83.  
  84.         new_table=document.createElement('table');
  85.  
  86.         for(var i in this.fixed_el.style) {
  87.           if (this.fixed_el.style[i]!='') {
  88.             try {
  89.               new_table.style[i]=this.fixed_el.style[i];
  90.             }
  91.             catch (e) {};
  92.           }
  93.         }
  94.  
  95.         new_table.id='fixed_'+id;
  96.         new_table.rules='all';
  97.         new_table.border='1';
  98.         new_table.style.position='fixed';
  99.         new_table.style.left='-9999px';
  100.         new_table.style.top='0px';
  101.  
  102.         var cln fthead.cloneNode(true);
  103.         var cth=cln.getElementsByTagName('th');
  104.         var fth=fthead.getElementsByTagName('th');
  105.  
  106.         for(var i=0i<fth.lengthi++) {
  107.           cth[i].style.width=(fth[i].clientWidth+(window.opera?1:0))+'px';
  108.           cth[i].style.paddingLeft='0';
  109.           cth[i].style.paddingRight='0';
  110.         }
  111.         new_table.appendChild(cln);
  112.  
  113.         this.fixed_el.parentNode.appendChild(new_table);
  114.         this.new_table=new_table;
  115.         this.chk_position();
  116.       }
  117.     }
  118.   }
  119. };
Скрипт подключается к таблице всего одной командой. Она может быть вызвана по событию загрузки контента, просто со страницы из скрипта под таблицей, после динамической отрисовки таблицы скриптом или после вставки ее в DOM.
  1. fix_header.fix('my_table');
В целом решение получилось кроссбраузерным, успешно протестированы все современные и большинство старых браузеров. Работает быстро, легко подключается и не требует никаких посторонних библиотек. Есть некоторое ограничения, которые не влияют на решение моей задачи. Например, скрипт скорее всего не будет работать, если на странице располагается несколько таблиц и хочется сделать фиксированный заголовок для каждой. Также могут возникнуть проблемы с отображением заголовка со сложной версткой с объединенными столбцами и ячейками.

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

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 3488 | Комментариев: 8

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (28.09.2017 в 11:17):
Ну так чо сам не сделал?
Михаил (28.09.2017 в 11:11):
Не респонсив хоть кто может сделать
ManHunter (04.05.2016 в 12:09):
Нет предела совершенству :)
Lyfeh (04.05.2016 в 11:06):
Приветствую ManHunter! У меня была похожая идея, но никак руки не доходили. Ваш труд очень выручил меня в реализации плавающей многоэтажной шапки. Большое человеческое спасибо! Добавлю от себя немного фикса:
1) В вашем варианте не учитывается "colgroup", доработал:
// необходимо добавить после 84 строчки  new_table=document.createElement('table');

tmp = ftable.getElementsByTagName('colgroup');
if (tmp) {
  var fcolgroup=tmp[0];
  if (fcolgroup) {
    var clncolgroup = fcolgroup.cloneNode(true);
    new_table.appendChild(clncolgroup);
  }
}

2) при открытии html шапка отображается сверху, пофиксил добавил в 53 строчку знак "=" (т.е. условие было coord.y>dy, 0>0 шапка отображалась, сделал coord.y>=dy, 0>=0 шапки нет, все ОК), получилось:
// Заголовок таблицы еще на экране или таблица уже не на экране
if (coord.y>=dy || (coord.y+fix_header.fixed_el.clientHeight)<dy) {
ManHunter (25.04.2016 в 17:20):
Сайт без скриптов подобен сайту со скриптами, только без скриптов :)

Хотя по факту не меньше половины современных сайтов с отключенными скриптами даже не отобразятся корректно, не говоря уже о работоспособности, тут их будет чуть меньше, чем все.
Infocatcher (25.04.2016 в 11:53):
Угу. Хотя все равно приятнее, когда сайт без скриптов работает.
ManHunter (25.04.2016 в 11:43):
Так если б position:sticky еще и всеми браузерами поддерживалось, не пришлось бы ничего изобретать. А так один фиг для совместимости точно так же пришлось бы делать свои решения.
Infocatcher (25.04.2016 в 11:18):
Жаль, вот эта экспериментальная штука как-то подзастряла:
https://developer.mozilla.org/..._positioning

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2017
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.13 сек. / MySQL: 2 (0.007 сек.) / Память: 4.5 Mb
Наверх