Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interruption of a CSS transition does not work for same attribute value

I've answered a question on how to start an animation when hovering the child element and then preserve the applied style until un-hovering the parent. However, I discovered a behaviour in my proposed solution that I can't explain and that I would like to understand. Reading the relevant parts of the specification didn't help me.

Here is a minimal example showing the expected behaviour. It works but the properties with comments behind have to be different for some reasons. Otherwise (e.g. both having 10px as value) un-hovering of the parent won't do anything to the width of the child. JSFiddle.

.parent {
  border: 1px solid orange;
  padding: 20px;
  width: 400px;
}

.parent .child {
  display: inline-block;
  height: 40px;
  background: blue;
  
  transition: width 0.5s ease 600s;
  width: 10px; /* why does this value has to be different ... */
  /* Hint: 
  If you hover the child until it reaches the 100px, then hover the
  parent without leaving the parent and keeping that hover for the
  transition-delay (600s) the width will become 10px as defined here.
  And removing this width property here won't make the transition work
  at all. */
}

.parent .child:hover {
  transition-delay: 0s;
  width: 100px;
}

.parent:not(:hover) .child {
  transition: width 0.5s ease 0s;
  width: 11px; /* ... from this value? */
  /* Hint:
  This is used as some kind of interruption of the 600s
  transition-delay in order to achieve the parent un-hover effect.
  I would like to set the width to 10px here as well but this will
  result in having no effect on the width of the enlarged child when
  un-hovering the parent. */
}
<div class="parent">
  <div class="child">
  </div>
</div>

A small observation

Relevant browsers are Firefox and Chrome. In Firefox the following works:

.parent .child {
  /* ... */
  transition: width 0.5s ease 600s;
  width: calc(10px);
}

.parent:not(:hover) .child {
  transition: width 0.5s ease 0s;
  width: 10px;
}

Question

Why do the values of the width property have to differ in order to make the un-hover effect work like expected?

like image 378
Marvin Avatar asked Apr 13 '17 13:04

Marvin


People also ask

Why is my transition CSS not working?

If you have a transition not working, check that the starting value of the property is explicitly set. Sometimes, you'll want to animate height and width when the starting or finishing value is auto . (For example, to make a div collapse, when its height is auto and must stay that way.)

How do you stop a transition in CSS?

To trigger an element's transition, toggle a class name on that element that triggers it. To pause an element's transition, use getComputedStyle and getPropertyValue at the point in the transition you want to pause it. Then set those CSS properties of that element equal to those values you just got.

How do transitions work in CSS?

CSS Transitions allow property changes in CSS values to occur smoothly over a specified duration. This smoothing animates the changing of a CSS value when triggered by a mouse click, focus or active state, or any changes to the element (including even a change on the element's class attribute).

What does the transition CSS property allow you to do?

CSS transitions provide a way to control animation speed when changing CSS properties. Instead of having property changes take effect immediately, you can cause the changes in a property to take place over a period of time.


1 Answers

Sorry - I missunderstood what was happening in yopur question.

I have done a new snippet with a simplified case:

#a, #b {
  width: 200px;
  height: 100px;
  border: solid 1px black;
  display: inline-block;
  background-color: lightgreen;
  margin-top: 50px;
}

#a {
  margin-right: -5px;
}

#container {
  width: 400px;
  height: 50px;
  border: solid 1px black;
  margin: 0px;
}

#child {
  width: 0px;
  height: 50px;
  position: absolute;
  background-color: blue;  
}


#child {
  transition: width 0.5s;
  width: 400px;
}

#a:hover ~ #container #child {
  transition: width 10s;
  width: 0px;
}

#b:hover ~ #container #child {
  transition: width 0.5s;
  width: 0px;
}
<div id="a">A</div>
<div id="b">B</div>
<div id="container">
  <div id="child"></div>
</div>

Hover on A, and then (before the transition ends) hover B. You will see the same behaviour: the transition goes on unchanged.

The reason is that when hovering a, the width (as a property of the element) is 0px. (Not the calculated width, that is being transitioned). So, when you hover on B, and the new style of 0px will not trigger a property a change, and hende will not start a new transition.

Old answer

The key part of the specs is this one: (emphasis mine)

reversing transitions spec

To meet this expectation, when a transition is started for a property on an element (henceforth, the new transition) that has a currently-running transition whose reversing-adjusted start value is the same as the end value of the new transition (henceforth, the old transition), implementations must cancel the old transition [...] and adjust the new transition as follows (prior to following the rules for computing the combined duration, start time, and end time): [...]

So, when the new width is the same as the old one, it's not really that the un-hover has no effect, it's that the effect is very slow (600s) as the browser is reversing the transition that was running.

To prove this, I have set a snippet where the width in the last rule is the same (10px). And the delay is set to 10 seconds.

Hover the child, and unhover it leaving the parent. You will see that 10 seconds later, the child width is modified.

.parent {
  border: 1px solid orange;
  padding: 20px;
  width: 400px;
}

.parent .child {
  display: inline-block;
  height: 40px;
  background: blue;
  
  transition: width 0.5s ease 10s;
  width: 10px;
}

.parent .child:hover {
  transition-delay: 0s;
  width: 100px;
}

.parent:not(:hover) .child {
  transition: width 0.5s ease 0s;
  width: 10px; 
  
<div class="parent">
  <div class="child">
  </div>
</div>
like image 71
vals Avatar answered Oct 26 '22 22:10

vals