Gup3rSuR4c
Gup3rSuR4c

Reputation: 9488

How do I create a table with fixed left and right columns and horizontal scrolling?

I'm trying to create a scheduling system and am having trouble with the grid. I want to have a table whose left and right th cells are fixed in place, but be able to scroll horizontally through the body td cells. The number of body td cells per tr is dynamic and can shrink and grow as the number of appointments in a time slot increases or decreases. I've looked around and found similar questions here, but they only ask for the left column to be fixed, where as I need both. I tried adapting what I saw in the answers, but it's not working.

My current prototype, here: https://jsfiddle.net/by2r6pkh/1/; is failing badly so I figured I'd ask for help. For what it's worth I'm only targeting Chrome for the browser.

Upvotes: 1

Views: 747

Answers (2)

Rick Hitchcock
Rick Hitchcock

Reputation: 35680

I don't think this is possible in CSS alone, but it doesn't take much JavaScript.

Change your TH elements from position: absolute to position: relative. That will resolve the problem with the wrapper's height, so you can remove overflow-y: visible. It also resolves the problem with the TH elements' height.

You can also remove:

  • position: relative; from the wrapper.
  • margin-left: 55px from the table.
  • left: 0; from th:first-child.
  • right: 0; from th:last-child.

Vanilla JavaScript solution:

var wrapper = document.querySelector('.wrapper');

function scrollTable() {
  var thl = document.querySelectorAll('.wrapper th:nth-child(1)'),
      thr = document.querySelectorAll('.wrapper th:nth-last-child(1)');

  for(var i = 0; i < thl.length; i++) {
    thl[i].style.left = wrapper.scrollLeft + 'px';
    thr[i].style.right = wrapper.scrollWidth - wrapper.offsetWidth - wrapper.scrollLeft + 'px';
  }
} //scrollTable

wrapper.addEventListener('scroll', scrollTable);

scrollTable();

var wrapper = document.querySelector('.wrapper');

function scrollTable() {
  var thl = document.querySelectorAll('.wrapper th:nth-child(1)'),
      thr = document.querySelectorAll('.wrapper th:nth-last-child(1)');

  for(var i = 0; i < thl.length; i++) {
    thl[i].style.left = wrapper.scrollLeft + 'px';
    thr[i].style.right = wrapper.scrollWidth - wrapper.offsetWidth - wrapper.scrollLeft + 'px';
  }
} //scrollTable

wrapper.addEventListener('scroll', scrollTable);

scrollTable();
* {
  margin: 0;
  padding: 0;
}
body {
  margin: 32px;
}
.wrapper {
  overflow-x: scroll;
  /* scrolls the entire table, not just the tds */
  width: 400px;
}
table {
  font: 14px arial;
  border-collapse: collapse;
  table-layout: fixed;
  /* fixes column widths, but overflows */
  width: calc(100% - 110px);
  /* 2x left margin */
}
td,
th {
  border: 1px solid #CCC;
  padding: 2px;
}
td {
  background: #FFF;
  width: 150px;
}
th {
  position: relative;
  background: #EEE;
  width: 50px;
}
<div class="wrapper">
  <table>
    <tr>
      <th>12:00
        <br />pm</th>
      <td>{CONTENT}</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <th>12:00
        <br />pm</th>
    </tr>
    <tr>
      <th>12:30
        <br />pm</th>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <th>12:30
        <br />pm</th>
    </tr>
  </table>
</div>


jQuery solution:

$('.wrapper')
  .scroll(function() {
    $('.wrapper th:nth-child(1)').css('left', this.scrollLeft);
    $('.wrapper th:nth-last-child(1)').css(
      'right', this.scrollWidth - this.offsetWidth - this.scrollLeft
     );
  })
  .scroll();

$('.wrapper')
  .scroll(function() {
    $('.wrapper th:nth-child(1)').css('left', this.scrollLeft);
    $('.wrapper th:nth-last-child(1)').css('right', this.scrollWidth - this.offsetWidth - this.scrollLeft);
  })
  .scroll();
* {
  margin: 0;
  padding: 0;
}
body {
  margin: 32px;
}
.wrapper {
  overflow-x: scroll;
  /* scrolls the entire table, not just the tds */
  width: 400px;
}
table {
  font: 14px arial;
  border-collapse: collapse;
  table-layout: fixed;
  /* fixes column widths, but overflows */
  width: calc(100% - 110px);
  /* 2x left margin */
}
td,
th {
  border: 1px solid #CCC;
  padding: 2px;
}
td {
  background: #FFF;
  width: 150px;
}
th {
  position: relative;
  background: #EEE;
  width: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
  <table>
    <tr>
      <th>12:00
        <br />pm</th>
      <td>{CONTENT}</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <th>12:00
        <br />pm</th>
    </tr>
    <tr>
      <th>12:30
        <br />pm</th>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
      <th>12:30
        <br />pm</th>
    </tr>
  </table>
</div>

Upvotes: 1

Related Questions