Search code examples
htmlcsshtml-table

How do I make a div inside a table cell follow the cell's width?


I have a table like this:

<table style="width: 100%;">
  <tr>
    <td>
      Title 1
    </td>
    <td>
      Stuff
    </td>
  </tr>
  <tr>
    <td>
      Title 2
    </td>
    <td>
      More stuff
    </td>
  </tr>
</table>

The columns in this table adjust themselves based on required and available space. I now want to add a subtitle under the title, which cuts off when it gets too long:

<table style="width: 100%;">
  <tr>
    <td>
      <div>Title 1</div>
      <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
        Subtitle that might be long
      </div>
    </td>
    <td>
      Stuff
    </td>
  </tr>
  <tr>
    <td>
      <div>Title 2</div>
      <div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
        Subtitle that might be long
      </div>
    </td>
    <td>
      More stuff
    </td>
  </tr>
</table>

The table will now resize the columns based on the width of the subtitle, so it can display as much of the subtitle as possible. I don't want this, I want the subtitle div to basically "follow" the size of its surroundings and render in whatever width that it ends up with.

Is there an easy, general way to accomplish this kind of thing?

I've tried alternatives like explicitly setting widths based on the viewport but it always leads to strange behaviour because there are other responsive elements on the page that make the size of the table variable compared to the viewport. So those solutions are suboptimal and ideally I just want the div to be ignored during width calculations and just get whatever width the cell ends up with.


Solution

  • If you want some element to not reflow other content around it, then you need to take that element out of normal document flow.

    One way to do that is by positioning the element via position: absolute. Note that of course you'll need to create an explicit "space" for this to exist, because by design we are saying this subtitle is out of flow so it won't create it own space!

    Note that this also has the downside that the space we create for it is fixed. In your example, you mention wanting the subtitle to go under the title, so this then makes an assumption about the title, namely how tall the title is (how many lines it is). For this example, it assumes the title is 1 line, or 1em worth of vertical space. If this is too limiting, you'll need to adjust your layout, or use javascript to dynamically set the element's positioning. I wouldn't recommend that.

    table {
      width: 100%;
    }
    
    td {
      position: relative;
    }
    
    .title {
      /* Create 1 line of space for subtitle below */
      margin-bottom: 1em;
    }
    
    .subtitle {
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      
      position: absolute;
      
      top: 1em;
      left: 0;
      width: 100%;
      height: 100%;
    }
    <table border="1">
      <tr>
        <td>
          <div class="title">Title 1</div>
          <div class="subtitle">
            Subtitle that might be long. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          </div>
        </td>
        <td>
          Stuff
        </td>
      </tr>
      <tr>
        <td>
          <div class="title">Title 2</div>
          <div class="subtitle">
            Subtitle short
          </div>
        </td>
        <td>
          More stuff
        </td>
      </tr>
    </table>
    
    <hr>
    
    <table border="1">
      <tr>
        <td>
          <div class="title">Title 1</div>
          <div class="subtitle">
            Subtitle that might be long. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          </div>
        </td>
        <td>
          Stuff
        </td>
        <td>
          Extra Stuff
        </td>
      </tr>
      <tr>
        <td>
          <div class="title">Title 2</div>
          <div class="subtitle">
            Subtitle that might be long
          </div>
        </td>
        <td>
          More stuff
        </td>
        <td>
          Lots more stuff
        </td>
      </tr>
    </table>