1

I have a number of tables that have many rows. I want to initially only show the first three table rows. When a user clicks the Show/Hide link, then all hidden rows will be toggled. I have achieved this so far with CSS3 and JavaScript (see Fiddle).

CSS (only show three rows by default):

.hide tr:nth-child(n+4) {
  display:none;
}

JavaScript (toggle above class to show all rows):

function ShowHide() {
  var tables = document.getElementsByClassName('foo');
  for(i=0; i<tables.length; i++) {
    tables[i].classList.toggle("hide");  
  }
}

Is it possible to use the transition functionality of CSS3 to make the rows appear slowly, rather than instantly? For example, fade in/out over 2 seconds?

I know this can be done easily in JQuery, but I'm looking for a pure CSS solution.

EvilDr
  • 8,943
  • 14
  • 73
  • 133
  • 1
    You can't transition the `display` property - https://stackoverflow.com/questions/3331353/transitions-on-the-display-property – Paulie_D Apr 11 '19 at 09:35
  • `display` is not a transitionable property. You could set the height to 0 and hide overflow to “hide” those rows, but you can not transition to `height:auto` either, so you will either have to go with a specified height (would only make sense if you knew how high the rows would be upfront), or at least involve JS at that point to determine the necessary target height to transition to. – 04FS Apr 11 '19 at 09:37
  • You can transition the opacity css property so the item slowly takes on the color of the underlying background which is basically the same effect as fading. Then you can add a `transitionend` event handler to actually put the display to none once the opacity reaches 0 and vice versa. – Shilly Apr 11 '19 at 09:40

2 Answers2

3

best way to do this is indeed to play with height and opacity. But you can't set height on table elements (tr/td)

so here is an example on how to do it with only CSS

#expand {
 /* hide the checkbox */
 display: none
}

label {
  /* style as a button (you can't use button inside label with for attribute) */
  padding: 2px 5px;
  border: 1px solid rgba(0,0,0,.2);
  border-radius: 5px;
  font-size: .7rem;
  cursor: pointer;
}

table {
  border-spacing: 0;
  border-collapse: collapse;
}

table tr:nth-child(n+4)>td {
  padding: 0;
}

table tr:nth-child(n+4)>td>div {
  opacity: 0;
  max-height: 0;
  transition: all 250ms ease-in;
}

#expand:checked + table tr:nth-child(n+4)>td {
  padding: 1px;
  /* default */
}

#expand:checked + table tr:nth-child(n+4)>td>div {
  opacity: 1;
  /* try to use something just slightly above the true height */
  max-height: 20px;
  overflow: hidden;
  transition-timing-function: ease-out;
}
<input type="checkbox" id="expand"/>
<table id="table">
  <tbody>
    <tr>
      <td>
        <div>Row 1</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 2</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 3</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 4</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 5</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 6</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 7</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 8</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 9</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Row 10</div>
      </td>
    </tr>
  </tbody>
</table>
<br/>
<label for="expand">Toggle</label>

Edit

Similar trick to hide columns:

#expand {
  /* hide the checkbox */
  display: none
}

label {
  /* style as a button (you can't use button inside label with for attribute) */
  padding: 2px 5px;
  border: 1px solid rgba(0, 0, 0, .2);
  border-radius: 5px;
  font-size: .7rem;
  cursor: pointer;
}

table {
  border-spacing: 0;
  border-collapse: collapse;
}

tr>*:nth-child(n+2)>div {
  opacity: 0;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: all 250ms ease-in;
}

#expand:checked+table tr>*:nth-child(n+2)>div {
  max-width: 100%;
  opacity: 1;
  transition-timing-function: ease-out;
}
<input type="checkbox" id="expand" />
<table id="table">
  <thead>
    <tr>
      <th>Column1</th>
      <th>
        <div>Column2</div>
      </th>
      <th>
        <div>Column3</div>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <div>Cell 1-1</div>
      </td>
      <td>
        <div>Cell 1-2</div>
      </td>
      <td>
        <div>Cell 1-3</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Cell 2-1</div>
      </td>
      <td>
        <div>Cell 2-2</div>
      </td>
      <td>
        <div>Cell 2-3</div>
      </td>
    </tr>
    <tr>
      <td>
        <div>Cell 3-1</div>
      </td>
      <td>
        <div>Cell 3-2</div>
      </td>
      <td>
        <div>Cell 3-3</div>
      </td>
    </tr>
  </tbody>
</table>
<br/>
<label for="expand">Toggle</label>
Apolo
  • 3,844
  • 1
  • 21
  • 51
0

Implementation of my 'you can use opacity to simulate a fading' comment based on your Fiddle code.

var theButton = document.getElementById("bar");
theButton.onclick = ShowHide;

function ShowHide() {
  var tables = document.getElementsByClassName('foo');
  for(i=0; i<tables.length; i++) {
   tables[i].classList.toggle("hide");  
  }
}
td { border-bottom:1px solid #ccc; }

.foo tr:nth-child(n+4) {
  display: block;
  opacity: 1;
  transition: opacity 2s ease-in-out;
}

.foo.hide tr:nth-child(n+4) {
  opacity: 0;
  transition: opacity 2s ease-in-out;
}

.foo.gone tr:nth-child(n+4) {
  display: none;
}
<p>
 <input type="button" id="bar" value="Show/Hide" />
</p>

<h2>Table 1</h2>
<table class="foo hide">
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
</table>

<h2>Table 2</h2>
<table class="foo hide">
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
  <tr><td>Text</td></tr>
</table>

Optionally you can include transitionstarts and transitionend events on the tables to toggle the .gone class after the transition finishes to toggle their display property. That way you can retain things like positioning, since elements with opacity 0 will still take up room on the page.

Shilly
  • 8,511
  • 1
  • 18
  • 24
  • Hmm interesting. Thank you. The goal was to reduce the space used by the tables by hiding certain rows, so that's not the ideal outcome. Are the events you refer to part of the CSS spec, or JavaScript? – EvilDr Apr 11 '19 at 09:59
  • Javascript. Hence i mention then, since usually it's indeed to make sure the table collapses properly. I'll add the relevant code. – Shilly Apr 11 '19 at 10:00
  • Tansitioning max-height together with opacity as Apollo shows is probably better suited than `transitionend` if the only thing you need to do is toggle CSS properties. – Shilly Apr 11 '19 at 10:19