Search code examples
htmlcsshtml-table

How can I stop a table from expanding beyond the size of its container div and scroll instead?


I am trying to create a page layout where I render:

  • A fixed width side bar
  • A main content section with
    • A header row (title + button)
    • A table

The table may hold quite a few columns and I don't want them to get squished together. Instead, I would like the table to scroll horizontally. However, what ends up happening is that the table expands its container and creates a horizontal scroll on the main layout. The scrollbar doesn't show up in the table itself.

You can see the code and the problem in action here:

.App {
  display: flex;
  flex-direction: row;
  width: 100%;
}

.Sidebar {
  display: block;
  width: 150px;
  background-color: red;
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 150px;
}

.Main {
  display: flex;
  flex-direction: column;
  width: 100%;
  background-color: green;
}

.TitleBar {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.TableContainer {
  width: 100%;
  position: relative;
  overflow-x: scroll;
}

.Table {
  width: 100%;
  table-layout: auto;
}
<div class="App">
  <div class="Sidebar">Sidebar</div>
  <main class="Main">
    <div class="TitleBar">
      <div>Title</div>
      <div>
        <button>Create</button>
      </div>
    </div>
    <div class="TableContainer">
      <table class="Table">
        <thead>
          <tr>
            <th>Name</th>
            <th>Prop 1</th>
            <th>Prop 2</th>
            <th>Prop 3</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Item 1</td>
            <td>Value 1.a</td>
            <td>Value 1.b</td>
            <td>Value 1.c</td>
            <td>
              <button>Edit</button>
            </td>
          </tr>
          <tr>
            <td>Item 2</td>
            <td>Value 2.a</td>
            <td>Value 2.b</td>
            <td>Value 2.c</td>
            <td>
              <button>Edit</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </main>
</div>


Solution

  • First set your table to display: block then set overflow-x: auto

    .Table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
    }
    

    As you are saying the rows squash together when going into responsive view. You can prevent that by giving the TD and the TH elements a min-width to not collapse further then the limit.

    This method works. See snippet down below:

    Note: I added a td min-width so they don't shrink down responsively to show how it works.

    body {
      margin: 0;
    }
    
    .App {
      display: flex;
      flex-direction: row;
      width: 100vw;
    }
    
    .Sidebar {
      display: block;
      width: 100px;
      background-color: red;
      flex-grow: 0;
      flex-shrink: 0;
    }
    
    .Main {
      display: flex;
      flex-direction: column;
        width: calc(100% - 100px);
    
      background-color: green;
    }
    
    .TitleBar {
      display: flex;
      flex-direction: row;
      align-items: center;
      justify-content: space-between;
      width: 100%;
    }
    
    .TableContainer {
      position: relative;
    }
    
    td {
      min-width: 500px;
    }
    
    .Table {
      display: block;
      overflow-x: auto;
      white-space: nowrap;
    }
    <div class="App">
      <div class="Sidebar">Sidebar</div>
      <main class="Main">
        <div class="TitleBar">
          <div>Title</div>
          <div>
            <button>Create</button>
          </div>
        </div>
        <div class="TableContainer">
          <table class="Table">
            <thead>
              <tr>
                <th>Name</th>
                <th>Prop 1</th>
                <th>Prop 2</th>
                <th>Prop 3</th>
                <th>Actions</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>Item 1</td>
                <td>Value 1.a</td>
                <td>Value 1.b</td>
                <td>Value 1.c</td>
                <td>
                  <button>Edit</button>
                </td>
              </tr>
              <tr>
                <td>Item 2</td>
                <td>Value 2.a</td>
                <td>Value 2.b</td>
                <td>Value 2.c</td>
                <td>
                  <button>Edit</button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </main>
    </div>

    JS Fiddle