Подтвердить что ты не робот

Таблица HTML с фиксированными (замороженными) столбцами и заголовками

Я искал в Интернете способ создания таблицы с фиксированными (замороженными) столбцами и заголовком. Похоже, я наконец нашел решение и модифицировал его для моих нужд.

Здесь оригинальная скрипка здесь.

Здесь - мое модифицированное решение. Я протестировал его в Chrome (версия: 55.0.2883.87 м) и Firefox (версия: 51.0.1).

Проблема в том, что она работает не полностью в IE (версия: 11.0.9600.18427). Во время горизонтальной прокрутки прокручивается и замороженная часть заголовка. Может ли кто-нибудь помочь мне заставить его работать в IE? И еще один вопрос: безопасный ли подход? Я имею в виду, если он использует какое-то неуказанное поведение, то некоторые из будущих браузеров или даже некоторых современных браузеров могут неправильно отображать мою таблицу, и лучше использовать безопасное решение с несколькими разными таблицами и синхронизировать положение и строки прокрутки высота. UPD: еще один вопрос: как сделать эту работу стабильной на мобильных устройствах?

Вот какой код демонстрирует подход:

$(document).ready(function() {
  $('tbody').scroll(function(e) { //detect a scroll event on the tbody
  	/*
    Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement
    of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain 			it relative position at the left of the table.    
    */
    $('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling
    $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first cell of the header
    $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody
  });
});
.container {
  height:200px; 
  width:400px;
  overflow: hidden;
}

table {
  position: relative;
  background-color: #aaa;
  border-collapse: collapse;
table-layout: fixed;
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}


/*thead*/
thead {
  position: relative;
  display: block; /*seperates the header from the body allowing it to be positioned*/
}

thead th {
  background-color: #99a;
  min-width: 120px;
  border: 1px solid #222;
}

thead th:nth-child(1) {/*first cell in the header*/
  position: relative;
  background-color: #88b;
}


/*tbody*/
tbody {
  flex: 1;
  position: relative;
  display: block; /*seperates the tbody from the header*/
  overflow: auto;
}

tbody td {
  background-color: #bbc;
  min-width: 120px;
  border: 1px solid #222;
}

tbody tr td:nth-child(1) {  /*the first cell in each tr*/
  position: relative;
  background-color: #99a;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="container">

  <table>
    <thead>
      <tr>
        <th>Name<br/>123</th>
        <th>Town</th>
        <th>County</th>
        <th>Age</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
       <tr>
        <th>Name</th>
        <th>Town</th>
        <th>County</th>
        <th>Age<br/>123<br/>321</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>John Smith</td>
        <td>Macelsfield</td>
        <td>Cheshire<br/>123</td>
        <td>52</td>
        <td>Brewer</td>
        <td>£47,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Jenny Jones<br/>123<br/>312</td>
        <td>Threlkeld</td>
        <td>Cumbria</td>
        <td>34</td>
        <td>Shepherdess</td>
        <td>£28,000</td>
        <td>Single</td>
        <td>0</td>
      </tr>
      <tr>
        <td>Peter Frampton</td>
        <td>Avebury</td>
        <td>Wiltshire</td>
        <td>57</td>
        <td>Musician</td>
        <td>£124,000</td>
        <td>Married</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Simon King</td>
        <td>Malvern</td>
        <td>Worchestershire</td>
        <td>48</td>
        <td>Naturalist</td>
        <td>£65,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Lucy Diamond</td>
        <td>St Albans</td>
        <td>Hertfordshire</td>
        <td>67</td>
        <td>Pharmasist</td>
        <td>Retired</td>
        <td>Married</td>
        <td>3</td>
      </tr>
      <tr>
        <td>Austin Stevenson</td>
        <td>Edinburgh</td>
        <td>Lothian </td>
        <td>36</td>
        <td>Vigilante</td>
        <td>£86,000</td>
        <td>Single</td>
        <td>Unknown</td>
      </tr>
      <tr>
        <td>Wilma Rubble</td>
        <td>Bedford</td>
        <td>Bedfordshire</td>
        <td>43</td>
        <td>Housewife</td>
        <td>N/A</td>
        <td>Married</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Kat Dibble</td>
        <td>Manhattan</td>
        <td>New York</td>
        <td>55</td>
        <td>Policewoman</td>
        <td>$36,000</td>
        <td>Single</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Henry Bolingbroke</td>
        <td>Bolingbroke</td>
        <td>Lincolnshire</td>
        <td>45</td>
        <td>Landowner</td>
        <td>Lots</td>
        <td>Married</td>
        <td>6</td>
      </tr>
      <tr>
        <td>Alan Brisingamen</td>
        <td>Alderley</td>
        <td>Cheshire</td>
        <td>352</td>
        <td>Arcanist</td>
        <td>A pile of gems</td>
        <td>Single</td>
        <td>0</td>
      </tr>
    </tbody>
  </table>
  
</div>
</body>
4b9b3361

Ответ 1

Это очень странно. Похоже, что проблемный код - это строка:

$('thead').css("left", -$("tbody").scrollLeft()); //fix the thead relative to the body scrolling

Похоже, что IE11 обрабатывает относительное позиционирование вложенных элементов по-разному (чем Chrome и другие браузеры). В этом случае вы позиционируете thead с относительным позиционированием со смещением. Вы также позиционируете thead th (это дети) со смещением и относительным позиционированием. Кажется, что Chrome позиционирует thead относительно table, а затем позиционирует th относительно thead. С другой стороны, IE11 позиционирует thead относительно table, а затем th просто наследует это же позиционирование независимо от его собственного позиционирования.

Исправление для этого было бы следующим: на IE11 обрабатывать позиционирование по-разному для thead. Вместо того, чтобы устанавливать позиционирование на родительском thead, установите позиционирование в элементах thead th. Таким образом, ваш первый столбец не будет "принудительным" наследовать позиционирование thead (в IE).

$(document).ready(function() {
  var isIE11 = !!navigator.userAgent.match(/Trident.*rv\:11\./);
  var customScroller;
  if (isIE11)
    customScroller = function() {
      $('thead th').css("left", -$("tbody").scrollLeft()); //if using IE11, fix the th element 
    };
  else
    customScroller = function() {
      $('thead').css("left", -$("tbody").scrollLeft()); //if not using IE11, fix the thead element
    };

  $('tbody').scroll(function(e) { //detect a scroll event on the tbody
    /*
    Setting the thead left value to the negative valule of tbody.scrollLeft will make it track the movement
    of the tbody element. Setting an elements left value to that of the tbody.scrollLeft left makes it maintain             it relative position at the left of the table.    
    */
    customScroller(); //fix the thead relative to the body scrolling
    $('thead th:nth-child(1)').css("left", $("tbody").scrollLeft());
//fix the first cell of the header
    $('tbody td:nth-child(1)').css("left", $("tbody").scrollLeft()); //fix the first column of tdbody
  });
});

Вот полный пример с вашим кодом, показывающий разные манипуляции на основе браузера:

https://jsfiddle.net/8tx4xfhx/5/

Alsol, было бы неплохо увидеть, заметил ли кто-нибудь это поведение раньше. Похоже, что IE11 обрабатывает вложенное относительное позиционирование по-другому, чем другие браузеры. Есть ли спецификация где-то, что определяет, какой должен быть стандарт? Должно ли относительное позиционирование быть унаследованным, как IE? Или должно относительное позиционирование всегда относиться к родительскому? Я бы подумал, что последний. Но также следует учитывать соображения производительности.

Ответ 2

Вы должны попробовать ниже пример кода со ссылкой на jquery.floatThead.js.

                    var $demoTable = $("div.table-responsive table");
                    $demoTable.floatThead({
                        top: 200,
                        scrollContainer: function ($table) {                                
                            return $table.closest('.table-responsive');
                        },
                        position: 'absolute'
                    });

вам нужно получить ссылку на файл jquery.floatThead.js и попытаться применить его к таблице.

Вы можете проверить это на ссылку ниже. http://mkoryak.github.io/floatThead/

Ответ 3

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

Я попытался реплицировать ваш код здесь с помощью только css-решения.

Я работаю над mac, поэтому не имею доступа к IE. Пожалуйста, проверьте, работает ли он на том же уровне.

Обновлен скрипт: https://jsfiddle.net/nashcheez/bzuasLcz/81/

Обратитесь к коду:

table {
  position: relative;
  width: 700px;
  background-color: #aaa;
  overflow: hidden;
  border-collapse: collapse;
}
/*thead*/

thead {
  position: relative;
  display: block;
  /*seperates the header from the body allowing it to be positioned*/
  width: 700px;
  overflow: visible;
}
thead th {
  background-color: #99a;
  min-width: 120px;
  height: 36px;
  min-height: 36px;
  border: 1px solid #222;
}
thead th:nth-child(1) {
  /*first cell in the header*/
  position: relative;
  display: block;
  background-color: #88b;
}
tbody tr td:nth-child(2) {
  margin-left: 124px;
  display: block;
}
/*tbody*/

tbody {
  display: block;
  width: 700px;
  height: 239px;
  overflow-y: auto;
}
tbody td {
  background-color: #bbc;
  min-width: 120px;
  border: 1px solid #222;
  height: 36px;
  min-height: 36px;
}
tbody tr td:nth-child(1) {
  /*the first cell in each tr*/
  position: absolute;
  display: inline-block;
  background-color: #99a;
}
<body>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Town</th>
        <th>County</th>
        <th>Age</th>
        <th>Profession</th>
        <th>Anual Income</th>
        <th>Matital Status</th>
        <th>Children</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>John Smith</td>
        <td>Macelsfield</td>
        <td>Cheshire</td>
        <td>52</td>
        <td>Brewer</td>
        <td>£47,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Jenny Jones</td>
        <td>Threlkeld</td>
        <td>Cumbria</td>
        <td>34</td>
        <td>Shepherdess</td>
        <td>£28,000</td>
        <td>Single</td>
        <td>0</td>
      </tr>
      <tr>
        <td>Peter Frampton</td>
        <td>Avebury</td>
        <td>Wiltshire</td>
        <td>57</td>
        <td>Musician</td>
        <td>£124,000</td>
        <td>Married</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Simon King</td>
        <td>Malvern</td>
        <td>Worchestershire</td>
        <td>48</td>
        <td>Naturalist</td>
        <td>£65,000</td>
        <td>Married</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Lucy Diamond</td>
        <td>St Albans</td>
        <td>Hertfordshire</td>
        <td>67</td>
        <td>Pharmasist</td>
        <td>Retired</td>
        <td>Married</td>
        <td>3</td>
      </tr>
      <tr>
        <td>Austin Stevenson</td>
        <td>Edinburgh</td>
        <td>Lothian</td>
        <td>36</td>
        <td>Vigilante</td>
        <td>£86,000</td>
        <td>Single</td>
        <td>Unknown</td>
      </tr>
      <tr>
        <td>Wilma Rubble</td>
        <td>Bedford</td>
        <td>Bedfordshire</td>
        <td>43</td>
        <td>Housewife</td>
        <td>N/A</td>
        <td>Married</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Kat Dibble</td>
        <td>Manhattan</td>
        <td>New York</td>
        <td>55</td>
        <td>Policewoman</td>
        <td>$36,000</td>
        <td>Single</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Henry Bolingbroke</td>
        <td>Bolingbroke</td>
        <td>Lincolnshire</td>
        <td>45</td>
        <td>Landowner</td>
        <td>Lots</td>
        <td>Married</td>
        <td>6</td>
      </tr>
      <tr>
        <td>Alan Brisingamen</td>
        <td>Alderley</td>
        <td>Cheshire</td>
        <td>352</td>
        <td>Arcanist</td>
        <td>A pile of gems</td>
        <td>Single</td>
        <td>0</td>
      </tr>
    </tbody>
  </table>
</body>

Ответ 4

Проблема заключается в том, что IE не позволяет настраивать атрибут left ячейки в строке, независимой от строки в целом. Мы можем это увидеть, отредактировав DOM напрямую, используя окно разработчика в IE и окно разработчика в Chrome.

В Chrome при прокрутке влево и вправо вы можете видеть в средстве просмотра Element, что атрибут left изменяется на самом элементе, который переопределяет все CSS. Мы можем перезагрузить страницу и установить атрибут элемента вручную на этом же экране: style:'left:300px', и мы увидим, что ячейка заголовка перемещается вправо 300 пикселей и наводится на остальные ячейки заголовка. Это хорошее поведение и поведение, которое заставляет этот метод работать.

Если мы сделаем то же самое в IE и добавим style: 'left:300px' в th-элемент, мы увидим, что ячейка не движется. Ничто, что мы делаем с атрибутами стиля этой ячейки, не заставит ее покинуть свое место в таблице. Именно эта функция IE приводит к сбою метода. IE настаивает на поддержании порядка ячеек независимо от того, какие атрибуты применяются к элементам внутри строки.

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

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

Удачи и счастливого кодирования!