Sebastian Zartner
Sebastian Zartner

Reputation: 20085

Hard transition of other properties when display property is involved

I want to fade between two differently sized elements within a container overlaying each other. The first element should be faded out, then the container resized and finally the other element faded in.

Here's the related snippet:

var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");

function switchLayers() {
  layer1.addEventListener("transitionend", function() {
    layer2.classList.add("fadein");
  });
  layer1.classList.add("fadeout");
}
#container {
  position: relative;
  background-color: yellow;
  padding: 10px;
  overflow: hidden;
}

.layer {
  position: relative;
  width: 400px;
}

#layer1 {
  height: 100px;
  float: left;
  background-color: blue;
}

#layer2 {
  height: 150px;
  background-color: red;
  display: none;
  opacity: 0;
}

#layer1.fadeout {
  opacity: 0;
  transition: opacity 1s ease-out;
}

#layer2.fadein {
  display: block;
  opacity: 1;
  transition: opacity 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
  <div id="layer1" class="layer"></div>
  <div id="layer2" class="layer"></div>
</div>

When the second layer's display property is set to block it works as expected, i.e. the opacity is changed from 0 to 1 within a second. Though if it's set to none, the transition suddenly is discrete.

I've tried to set all within the transition value to transition all properties and also tried to include the display property in the transition like this:

transition: display 0s, opacity 1s ease-out;

Though without success. Note that because the container should resize to the size of the currently displayed layer, the visibility property can't be used (as it hides the element but still lets it occupy the space).

How to made this work?

Upvotes: 1

Views: 47

Answers (2)

Abhitalks
Abhitalks

Reputation: 28387

There is no straightforward way. Transitions do not work on display, nor do they work on auto height. So, visibility is a good bet.

Note that because the container should resize to the size of the currently displayed layer, the visibility property can't be used (as it hides the element but still lets it occupy the space).

Then, you will need to hack it out. You can make use of min-height. Give a faux min-height to your container, and then apply the height of your layer2 to it once the transition ends. Also, because display on layer2 will block the transition, you need to separate out the classes for display and opacity and space out their application using a zero timeout in between.

Here is a crude idea:

var layer1 = document.getElementById("layer1"),
    layer2 = document.getElementById("layer2"),
    container = document.getElementById("container"),
    h = window.getComputedStyle(layer2).getPropertyValue("height");

container.addEventListener("transitionend", function(e) {
  if (e.target.id === 'layer1') {
    // apply layer2 height to container min-height
    container.style.minHeight = h; 
  }
  if (e.target.id === 'container') { 
    // First show the layer2
    layer2.classList.add("show"); 
    // Then a dummy pause to fadein
    setTimeout(function(){
      layer2.classList.add("fadein");
    }, 0);
  }
}, false);

function switchLayers() {
  layer1.classList.add("fadeout");
}
#container {
  position: relative;
  background-color: yellow;
  padding: 10px; overflow: hidden;
  min-height: 1px; /* faux min-height */
  transition: min-height 1s linear;
}

.layer { position: relative; width: 400px; }

#layer1 {
  height: 100px; float: left;
  background-color: blue;
  transition: all 1s linear;
}

#layer2 {
  height: 150px; background-color: red;
  display: none; opacity: 0;
  transition: opacity 1s linear;
}

#layer1.fadeout { opacity: 0; }
#layer2.show { display: block; } /* Separate out display */
#layer2.fadein { opacity: 1; } /* Separate out opacity */
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
  <div id="layer1" class="layer"></div>
  <div id="layer2" class="layer"></div>
</div>

Upvotes: 1

nashcheez
nashcheez

Reputation: 5183

Try using the visibility property instead of display.

For more information regarding the state changes in visibility and display, refer article.

For transitioning the parent height, you have to manually change the height property of the #container. Using display: block & display: none will never transition the parent.

Refer code:

var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");

function switchLayers() {
  layer1.addEventListener("transitionend", function() {
    layer2.classList.add("fadein");
    document.getElementById("container").style.height = "170px";
  });
  layer1.classList.add("fadeout");
}
#container {
  position: relative;
  background-color: yellow;
  padding: 10px;
  height: 100px;
  overflow: hidden;
  transition: all 0.5s;
}

.layer {
  position: relative;
  width: 400px;
}

#layer1 {
  height: 100px;
  float: left;
  background-color: blue;
}

#layer2 {
  height: 150px;
  background-color: red;
  visibility: none;
  opacity: 0;
}

#layer1.fadeout {
  visibility: none;
  opacity: 0;
  transition: all 1s ease-out;
}

#layer2.fadein {
  visibility: visible;
  opacity: 1;
  transition: all 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
  <div id="layer1" class="layer"></div>
  <div id="layer2" class="layer"></div>
</div>

Upvotes: 1

Related Questions