Robs
Robs

Reputation: 8279

How to fade html element out and the fade new element in it's place

I have a number of questions:

<div id='q1'>question...</div>
<div id='q2'>question...</div>
<div id='q3'>question...</div>
etc...

Only one question div should be visible at any time.

I want each question to fade out and the new question fade in exactly where the previous question was.

I am using this CSS for the fade transitions:

.fade-out {
    opacity: 0;
    animation-name: fadeOutOpacity;
    animation-iteration-count: 1;
    animation-timing-function: ease-out;
    animation-duration: 1s;
}


.fade-in {
    opacity: 1;
    animation-name: fadeInOpacity;
    animation-iteration-count: 1;
    animation-timing-function: ease-in;
    animation-duration: 2s;
    transition-delay: 4s;
}

@keyframes fadeInOpacity {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

@keyframes fadeOutOpacity {
    0% {
        opacity:1;
    }
    100% {
        opacity:0;
    }
}

But when the second question has the fade-in css class applied, it makes the first question jump up before it's faded out.

Ideally, I'd like the first question to fade out and the become display: none;

But I am struggling to get it to work.

Questions

  1. How can I get the first question to 'fade-out' and the second 'fade-in' in it's place?

N.B I'd rather a pure CSS solution than a Jquery solution if possible...

Upvotes: 1

Views: 313

Answers (3)

Adnan Toky
Adnan Toky

Reputation: 1943

Try this sample:

.fade-out { 
    opacity: 0; 
    animation-name: fadeOutOpacity; 
    animation-iteration-count: 1; 
    animation-timing-function: ease-out; 
    animation-duration: 2s;
    height: 0;
    width: 0;
} 

.fade-in { 
    opacity: 1; 
    animation-name: fadeInOpacity; 
    animation-iteration-count: 1; 
    animation-timing-function: ease-in; 
    animation-duration: 4s; 
    animation-delay: 0s;
} 

@keyframes fadeInOpacity { 
    0% { 
        opacity: 0; 
    } 
    50% {
        opacity: 0;
    }
    100% { 
        opacity: 1; 
    } 
} 

@keyframes fadeOutOpacity { 
    0% { 
        opacity: 1; 
        height: auto;
        width: auto;
    } 
    100% { 
        opacity: 0; 
    } 
}
<div id='q1' class="fade-out">question...1</div>
<div id='q2' class="fade-in">question...2</div>

Upvotes: 0

user7826291
user7826291

Reputation:

Like in most pure CSS solutions you might want to utilize input elements. Here I am using radio types with labels inside to reach the next question.

As you are only changing opacity you might want to use a transition instead of an animation. I am using the shorthand here which can be expanded like this:

transition: property-name duration easing delay;

Here is a working example:

/* wrapper to hold absolute positioned children */
.wrapper {
  position: relative;
}

/* hide actual radio buttons */
.wrapper input {
  display: none
}

.question {
  /* float above each other */
  position: absolute;
  /* transparency by default */
  opacity: 0;
  /* fade out without delay */
  transition: opacity 1s ease-out;
}

input:checked + .question {
  /* always put current question first */
  z-index: 1;
  /* fade in with delay */
  opacity: 1;
  transition: opacity 2s ease-in 1s;
}
<div class="wrapper">
  <input id="question-1" type="radio" name="question" checked>
  <div class="question">
    <p>Question 1</p>
    <label for="question-2">next</label>
  </div>

  <input id="question-2" type="radio" name="question">
  <div class="question">
    <p>Question 2</p>
    <label for="question-3">next</label>
  </div>

  <input id="question-3" type="radio" name="question">
  <div class="question">
    <p>Question 3</p>
    <label for="question-1">start over</label>
  </div>
</div>

Upvotes: 3

Intervalia
Intervalia

Reputation: 10965

You can do it easily without needing an animation Instead you only need transition.

Run this code snippet example:

function doNext() {
  let el = document.querySelector('.questions [showing]');
  el.removeAttribute('showing');
  let temp = el.nextElementSibling;
  if (temp === null) {
    temp = el.parentElement.firstElementChild;
  }
  
  temp.setAttribute('showing', '');
}

const btn = document.querySelector('#next');
btn.addEventListener('click', doNext);
.questions {
  height: 60px;
  position: relative;
}

.fader {
  left: 0;
  opacity: 0;
  position: absolute;
  top: 0;
  transition: all 1s ease;
}

.fader[showing] {
  opacity: 1;
  transition: all 1s ease 0.75s;
}
<div class="questions">
  <div id='q1' class="fader" showing>question...1</div>
  <div id='q2' class="fader">question...2</div>
  <div id='q3' class="fader">question...3</div>
  <div id='q4' class="fader">question...4</div>
</div>
<button id="next">Next</button>

When the attribute showing is removed then the opacity of the question goes from 1 to 0 over 1 seconds. When the attribute showing is added then the element waits for 0.75 seconds and then changes opacity from 0 to 1 over 1 seconds.

The JavaScript I have added simple allows the changing of which element has the attribute showing. Your code would need to do something similar to change which question is showing.

I set the position of each question to absolute with top set to 0 so that all questions show in the same place. BUT doing this requires that you know that maximum size of your questions so the container can be set to the correct height.

Upvotes: 1

Related Questions