0

This is probably best illustrated with an example. I have a parent and a child. The parent is hidden by default unless it has an in class. Its child should transition its background-color from green to white, after a 3s delay, only once the parent receives the in class.

So when the parent is shown, the child's background color is already white. There's no 3 second period of orange, with a transition to white. Can someone explain what's happening here?

$('button.show').on('click', () => {
  $('.parent').addClass('in');
});

$('button.hide').on('click', () => {
  $('.parent').removeClass('in');
});
.parent, .child {
  display: flex;
  align-items: center;
  justify-content: center;
}

.parent {
  background-color: orange;
  width: 300px;
  height: 300px;
}

.child {
  background-color: green;
  padding: 20px;
  width: 150px;
  height: 150px;
  transition: background-color 1s linear;
}

.parent:not(.in) {
  display: none;
}

.parent.in .child {
  background-color: white;
  transition-delay: 3s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="parent">
  <div class="child">
    <span>Background should start green, then turn white, but transition isn't happening.</span>
  </div>
</div>

<br>
<button type="button" class="show">Show parent</button>
<button type="button" class="hide">Hide parent</button>
Matt
  • 23,363
  • 39
  • 111
  • 152

2 Answers2

1

I got a bit confused with exactly when the settings would occur given the display:none starting condition on the parent.

This snippet takes a slightly different path - using a CSS animation which I found easier to control. The animation is not set up until the parent is displayed so there is no room for confusion on timing.

$('button.show').on('click', () => {
  $('.parent').addClass('in');
});

$('button.hide').on('click', () => {
  $('.parent').removeClass('in');
});
.parent,
.child {
  display: flex;
  align-items: center;
  justify-content: center;
}

.parent {
  background-color: orange;
  width: 300px;
  height: 300px;
}

.child {
  background-color: green;
  padding: 20px;
  width: 150px;
  height: 150px;
  animation-name: none;
}

.parent:not(.in) {
  display: none;
}

.parent.in .child {
  animation-name: change;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-delay: 3s;
  animation-fill-mode: forwards;
}

@keyframes change {
  0% {
    background-color: green;
  }
  100% {
    background-color: white;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
  <div class="child">
    <span>Background should start green, then turn white, but transition isn't happening.</span>
  </div>
</div>

<br>
<button type="button" class="show">Show parent</button>
<button type="button" class="hide">Hide parent</button>
A Haworth
  • 30,908
  • 4
  • 11
  • 14
  • Great solution. I try to use transitions when I can, but this animation appears to be the only option without the need for additional classes and JS. – Matt Jul 15 '21 at 18:51
0

The reason you're not seeing the transition is because the child is never displayed with the first background color. Starts out as display:none inherited from parent then switches to parent display and new color at same time.

You need to first display the parent (and the child by inheritance) and then change the color of the child.

The JS

 $('button').on('click', () => {
     $('.parent').addClass('in');
     //Without settimeout this will run same time as first added class
     setTimeout(() => $('.parent').addClass('color'), 0);
 });

Then in you're css

.parent.in.color .child {
    background-color: white;
    transition-delay: 3s;
}
Moshe Sommers
  • 1,466
  • 7
  • 11
  • Ok, I see what you're saying, but my preference is not to add a second class to the parent element strictly to transition the child. In my real world project, the parent gets the class via an operation outside of any code designed to handle the child element. It doesn't feel correct for that code to worry about a child element outside of its purview. Is there a solution that doesn't require a second class to the parent? – Matt Jul 15 '21 at 17:10
  • By definition any code to display parent is code to display child... In your real world project is the child displayed when the parent is display none? You can add the class to the child if you prefer – Moshe Sommers Jul 15 '21 at 17:13
  • I should clarify. My parent element is a page-wide container that uses CSS classes to control which "page" the app is currently displaying. Each page is a child of the parent. So if the parent gets a class of `settings`, the settings page is shown. The code handling these page switches is high-level code, not code contained within a `settings.js` module. So say I want an element somewhere within settings to transition into view when settings is shown, I don't want that high-level code having to add a random class to the page-wide container just to generate that transition in settings. – Matt Jul 15 '21 at 17:16
  • Ok that makes sense – Moshe Sommers Jul 15 '21 at 17:24
  • Where does the code (or button) that triggers the page changes sit? – Moshe Sommers Jul 15 '21 at 17:25
  • The buttons and their handling code sit higher than `settings.js`, to continue my example. They'd sit in a file akin to something like `app.js`. – Matt Jul 15 '21 at 18:03