82

I have been trying to think of a way to make a table with a fixed first column (and the rest of the table with a horizontal overflow) I saw a post which had a similar question. but the fixed column bit did not seem to be resolved. Help?

2
  • I'd like to expand this question a little: a friend of mine wants to make a table which wouldn't scale to more than 100% of the page, but would have more than two columns that together should be scrollable in case they need more horizontal space than there is. In effect, this should be similar to Frozen columns in MS Excel. Is that possible? Commented Sep 4, 2010 at 11:09
  • See my answer on similar question: stackoverflow.com/a/17557830/1763149 Commented Jul 9, 2013 at 20:49

7 Answers 7

128

How about:

table {
  table-layout: fixed; 
  width: 100%;
  *margin-left: -100px; /*ie7*/
}
td, th {
  vertical-align: top;
  border-top: 1px solid #ccc;
  padding: 10px;
  width: 100px;
}
.fix {
  position: absolute;
  *position: relative; /*ie7*/
  margin-left: -100px;
  width: 100px;
}
.outer {
  position: relative;
}
.inner {
  overflow-x: scroll;
  overflow-y: visible;
  width: 400px; 
  margin-left: 100px;
}
<div class="outer">
  <div class="inner">
    <table>
      <tr>
        <th class=fix></th>
        <th>Col 1</th>
        <th>Col 2</th>
        <th>Col 3</th>
        <th>Col 4</th>
        <th class="fix">Col 5</th>
      </tr>
      <tr>
        <th class=fix>Header A</th>
        <td>col 1 - A</td>
        <td>col 2 - A (WITH LONGER CONTENT)</td>
        <td>col 3 - A</td>
        <td>col 4 - A</td>
        <td class=fix>col 5 - A</td>
      </tr>
      <tr>
        <th class=fix>Header B</th>
        <td>col 1 - B</td>
        <td>col 2 - B</td>
        <td>col 3 - B</td>
        <td>col 4 - B</td>
        <td class=fix>col 5 - B</td>
      </tr>
      <tr>
        <th class=fix>Header C</th>
        <td>col 1 - C</td>
        <td>col 2 - C</td>
        <td>col 3 - C</td>
        <td>col 4 - C</td>
        <td class=fix>col 5 - C</td>
      </tr>
    </table>
  </div>
</div>

You can test it out in this jsbin: http://jsbin.com/uxecel/4/edit

Sign up to request clarification or add additional context in comments.

10 Comments

Simple but effective solution to my query!
This basically works for me but I'd like to start out with the right-most columns of the scrolling div visible. My column titles are dates, with the most-recent date on the far right, and I'd like that to be visible when the <table> is first displayed. Is there any way to do this programmatically? Is there a way to programmatically move the horizontal scroll bar in either direction? I'd like to have always-available "scroll left" and "scroll right" buttons at the top of the table, since the browser-supported scroll bar is at the bottom and not always immediately visible.
I saw it has a big gap between header column and data columns when I set width of table > all data columns width,does anybody know why and how to fix it? please see here: jsbin.com/sesurutojo/edit?output
If I use paging via ajax and I have to go to next page then the scroll does not work and all the table properties are lost. How can I get the same table UI but with some ajax request?
This seems to work only if vertical scrolling is not required. For my case there are many rows so scrolling vertically will only scroll the row data but not the row header fixed in this way using position absolute.
|
22

Based on skube's approach, I found the minimal set of CSS I needed was:

.horizontal-scroll-except-first-column {
  width: 100%;
  overflow: auto;
}

.horizontal-scroll-except-first-column > table {
  margin-left: 8em;
}

.horizontal-scroll-except-first-column > table > * > tr > th:first-child,
.horizontal-scroll-except-first-column > table > * > tr > td:first-child {
  position: absolute;
  width: 8em;
  margin-left: -8em;
  background: #ccc;
}

.horizontal-scroll-except-first-column > table > * > tr > th,
.horizontal-scroll-except-first-column > table > * > tr > td {
  /* Without this, if a cell wraps onto two lines, the first column
   * will look bad, and may need padding. */
  white-space: nowrap;
}
<div class="horizontal-scroll-except-first-column">
  <table>
    <tbody>
      <tr>
        <td>FIXED</td> <td>22222</td> <td>33333</td> <td>44444</td> <td>55555</td> <td>66666</td> <td>77777</td> <td>88888</td> <td>99999</td> <td>AAAAA</td> <td>BBBBB</td> <td>CCCCC</td> <td>DDDDD</td> <td>EEEEE</td> <td>FFFFF</td>
      </tr>
    </tbody>
  </table>
</div>

2 Comments

Great and comprehensible, nice
This only works if all the columns in a row are the same height. If the second column has a taller height for example, you can see it when it goes "behind" the first column. Any ideas on how to fix that?
21

I have a similar table styled like so:

<table style="width:100%; table-layout:fixed">
  <tr>
    <td style="width: 150px">Hello, World!</td>
    <td>
      <div>
        <pre style="margin:0; overflow:scroll">My preformatted content</pre>
      </div>
    </td>
  </tr>
</table>

6 Comments

This answer would really benefit from a demo here.
JSFIDDLE demo so that it's more clear what happens here
That only scrolls a single cell…
I don't know why this is the accepted answer. The question says: and the rest of the table, which in my point of view includes more than one column...
and the columns should be in sync with the headers, like if the columns scroll; headers should scroll too with the columns. But I don't think they both will be in sync after implementing this solution
|
14

You can use below table style to have horizontal scrolling table with fixed first column.

table,
th,
td {
  border: 1px solid black;
}

.table-style {
  overflow-x: auto;
}

.table-style tr th:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background-color: white;
}
<div class="table-style">
  <table>
    <thead>
      <tr>
        <th>_col1_row1_</th>
        <th>_col2_row1_</th>
        <th>_col3_row1_</th>
        <th>_col4_row1_</th>
        <th>_col5_row1_</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th>_col1_row2_</th>
        <td>_col2_row2_</td>
        <td>_col3_row2_</td>
        <td>_col4_row2_</td>
        <td>_col5_row2_</td>
      </tr>
      <tr>
        <th>_col1_row3_</th>
        <td>_col2_row3_</td>
        <td>_col3_row3_</td>
        <td>_col4_row3_</td>
        <td>_col5_row3_</td>
      </tr>
      <tr>
        <th>_col1_row4_</th>
        <td>_col2_row4_</td>
        <td>_col3_row4_</td>
        <td>_col4_row4_</td>
        <td>_col5_row4_</td>
      </tr>
      <tr>
        <th>_col1_row5_</th>
        <td>_col2_row5_</td>
        <td>_col3_row5_</td>
        <td>_col4_row5_</td>
        <td>_col5_row5_</td>
      </tr>
    </tbody>
  </table>

Comments

8

Use jQuery DataTables plug-in, it supports fixed header and columns. This example adds fixed column support to the html table "example":

http://datatables.net/extensions/fixedcolumns/

For two fixed columns:

http://www.datatables.net/release-datatables/extensions/FixedColumns/examples/two_columns.html

1 Comment

Fixing left hand-side columns is done by using the leftColumns initialisation parameter. This link works datatables.net/extensions/fixedcolumns (today)
5

Here's an example with fixed first and last columns. Sticky works better than absolute positioning for expanding the rows when the cells have more content.

Note - this also works with rowspan table cells. The table holds its correct shape.

.wrapper {
  overflow-x:scroll;
  width:100%; 
}
table {
  table-layout: fixed; 
  width: 100%;
  border-collapse: collapse;
  background: white;
}
tr {
  border-top: 1px solid #ccc;
}
td, th {
  vertical-align: top;
  text-align: left;
  width:150px;
  padding: 5px;
}
.fix {
  position:sticky;
  background: white;
}
.fix:first-child {
  left:0;
  width:180px;
}
.fix:last-child {
  right:0;
  width:120px;
}
  <div class="wrapper">
    <table>
      <thead>
        <th class='fix'>Fixed</th>
        <th>Col 1</th>
        <th>Col 2</th>
        <th>Col 3</th>
        <th>Col 4</th>
        <th>Col 5</th>
        <th class='fix'>Fixed</th>
      </thead>
      <tbody>
        <tr>
          <td class='fix'>First Content</td>
          <td>A1</td>
          <td>A2 (with longer content)</td>
          <td>A3</td>
          <td>A4</td>
          <td>A5</td>
          <td class='fix'>Last Content</td>
        </tr>
        <tr>
          <td class='fix'>First Content (with longer content)</td>
          <td>B1</td>
          <td>B2</td>
          <td>B3</td>
          <td>B4</td>
          <td>B5</td>
          <td class='fix'>Last Content</td>
        </tr>
        <tr>
          <td class='fix'>First Content</td>
          <td>C1</td>
          <td>C2</td>
          <td>C3</td>
          <td>C4</td>
          <td>C5</td>
          <td class='fix'>Last Content (with longer content)</td>
        </tr>
      </tbody>
    </table>
  </div>

Example to play with: https://jsbin.com/qawidijigi/edit?html,css,output

Comments

3

Take a look at this JQuery plugin:

http://fixedheadertable.com

It adds vertical (fixed header row) or horizontal (fixed first column) scrolling to an existing HTML table. There is a demo you can check for both cases of scrolling.

2 Comments

note: this relies on obsoleted features in jquery and has not been updated since about 1 year
@HaykSaakian Looks like that project is being updated again now? Says this year, and jQuery version 1.7 is required.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.