Search code examples
javascripthtmlcsscss-grid

CSS grid - Animating content placement


I have a 2x2 grid that contains three divs: #blue (2x1), #red (1x1) and #yellow (1x1) (see JSFiddle). This is what the grid looks like initially:

enter image description here

NOTE: #red is overlapped by #blue and hence not visible. To make it visible, the user must click Show. This is what the grid looks like after Show has been clicked:

enter image description here

In my current setup, clicking the Show button adds the .expanded class to #red, which changes the line placement of #red from grid-column: 1/2 to grid-column: 1/span2 and makes #red visible. This works fine, however, I don't like the abruptness of the change and would like to give #red the appearance of sliding out from under #blue when Show is clicked. The problem is that since grid-column has a discrete animation type, it cannot be tween-transitioned in the way that a continuous property like width can.

My attempt at a work-around:

I tried specifying a width property for #red. It is initially set to 100px and transitioned to 200px when Show is clicked.

This almost gives me the desired result in that #red slides smoothly when Show is clicked but not when Hide is clicked. My guess is that grid-column does not wait for the transition to complete and I also don't like having to hardcode the values of width.

I also tried using translateX with transition but once again grid-column is set before the transition finishes.

Is there a solution to this problem that uses pure CSS? (I don't mind using JS but it is a last resort and do not want to use anything that involves setTimeout or jQuery.)


Solution

  • You could add another class that could take care of the hide transition combined with an webkitTransitionEnd event.

    Also note that I added a width: 50% rather than your fixed width.

    const show = document.getElementById('show');
    const hide = document.getElementById('hide');
    const red = document.getElementById('red');
    
    show.addEventListener('click', function() {
      red.classList.add('expanded');
      red.classList.remove('notexpanded');
    
    });
    
    hide.addEventListener('click', function() {
      red.classList.remove('expanded');
      red.classList.add('notexpanded');
      red.addEventListener('webkitTransitionEnd',
        function(event) {
          red.classList.remove('notexpanded');
        }, false);
    });
    #page {
      display: grid;
      grid-template-columns: 100px 100px;
      grid-template-rows: 100px 100px;
      /* grid-auto-flow: column dense; */
    }
    
    #blue {
      background-color: lightblue;
      grid-row: 1/span 2;
      grid-column: 1/2;
      z-index: 1;
    }
    
    #yellow {
      background-color: yellow;
      height: 100%;
    }
    
    #red {
      background-color: red;
      text-align: right;
      grid-column: 1;
      grid-row: 1;
      /* z-index: -1; cant click hide */
      /* float: right; */
      /* transform: translateX(0);
      transition: transform 2s; */
      width: 50%;
      transition: width 2s;
    }
    
    #red.expanded {
      /* transform: translateX(100%); */
      width: 100%;
      grid-column: 1/span 2;
    }
    
    #red.notexpanded {
      /* transform: translateX(100%); */
      width: 50%;
      grid-column: 1/span 2;
    }
    <div id='page'>
      <div id='blue'>
        <button type='button' id='show'>Show</button>
      </div>
      <div id='red'>
        <button type='button' id='hide'>Hide</button>
      </div>
      <div id='yellow'></div>
    </div>