6

This codepen shows my problem: http://codepen.io/PiotrBerebecki/pen/pNvpdG

When the user clicks on the big button the css opacity is reduced to 0. Since I've applied the following rule: transition: opacity 0.5s ease-in-out; the fade out animation is smooth.

I would like to achieve the same smooth transition when the next button fades in. However for some reason the next button appears suddenly without any transition.

Would you know what causes the issue and how to fix it?

console.clear();

(function() {
  
  // Data for the app
  const model = {
    buttons: ['tomato', 'blue'],
    currentButton: -1
  };
  
  // Logic for the app
  const controller = {
    init: function() {
      view.init();
    },
    getButtonName: function() {
      model.currentButton = (model.currentButton + 1) % model.buttons.length;
      return model.buttons[model.currentButton];
    }
  };
  
  // View for the app
  const view = {
    init: function() {
      this.root = document.getElementById('root');
      this.showNext();
    },
    
    animationDelay: 500,
    
    showNext: function() {
      // Get next button name
      const buttonName = controller.getButtonName();
      
      // Create button DOM element
      const buttonElement = document.createElement('div');
      buttonElement.className = 'button';
      buttonElement.id = buttonName;
      buttonElement.textContent = buttonName;
      buttonElement.style.opacity = 0;
      
      // Add event listender for the button
      buttonElement.addEventListener('click', event => {
        // Reduce opacity
        buttonElement.style.opacity = 0;
        // Remove the button from DOM
        setTimeout(() => {
          this.root.removeChild(buttonElement);
        }, this.animationDelay + 10);
        // Start the function to show next button
        setTimeout(() => {
          this.showNext();
        }, this.animationDelay + 20);
      });
      
      // Add button to DOM
      this.root.appendChild(buttonElement);
      
      // Show button by increasing opacity
      buttonElement.style.opacity = 1;
      
    }
  };
  
  // Start the app
  controller.init();

}());
#tomato {
  background: tomato;
}

#blue {
  background: DeepSkyBlue;
}

.button {
  transition: opacity 0.5s ease-in-out;
  width: 100%;
  height: 50vh;
  border: solid 3px black;
  cursor: pointer;
}
<div id="root"></div>
Piotr Berebecki
  • 7,428
  • 4
  • 33
  • 42

3 Answers3

4

This should work , Code pen link: http://codepen.io/saa93/pen/gLbvmQ

You would need to add this instead of directly setting opacity to 1

// Show button by increasing opacity
buttonElement.style.opacity = 0;
setTimeout(() => {
    buttonElement.style.opacity = 1;
}, this.animationDelay + 20);   
hippietrail
  • 15,848
  • 18
  • 99
  • 158
Saa_keetin
  • 647
  • 6
  • 10
  • 1
    Great, it works! Do you know why I need to do this inside `setTimeout`? – Piotr Berebecki Nov 06 '16 at 20:23
  • Since we are dynamically assigning the transision and property value (opacity to 0) we need to delay the browser so it can apply the transition to the element. Then adding opacity to 1 we get the desired effect. You might find more detailed answer here: http://stackoverflow.com/questions/8210560/css-transitions-do-not-work-when-assigned-trough-javascript – Saa_keetin Nov 06 '16 at 20:28
  • 1
    is anyone else seeing 0 delay regardless of the 'this.animationDelay + 20' value? – Robert Sinclair Apr 30 '20 at 22:13
  • @RobertSinclair on the contrary `setTimeout(() => someEl.classList.add(classWIthOpacity1))` works even with delay 0 (default) – jave.web Aug 20 '22 at 21:32
2

Add a class (in the Snippet is .active) add the following:

CSS

.button {
  opacity: 0;
  transition: opacity 0.5s ease-in-out;
  width: 100%;
  height: 50vh;
  border: solid 3px black;
  cursor: pointer;
}
.button.active {
  opacity: 1;
  transition: opacity 0.5s ease-in-out;
}

JavaScript

  ...
  // Reduce opacity
  buttonElement.classList.toggle('active');
  buttonElement.style.opacity = 0;
  ...
  // Show button by increasing opacity
  buttonElement.classList.toggle('active');
  buttonElement.style.opacity = 1;

SNIPPET

console.clear();

(function() {

  // Data for the app
  const model = {
    buttons: ['tomato', 'blue'],
    currentButton: -1
  };

  // Logig for the app
  const controller = {
    init: function() {
      view.init();
    },
    getButtonName: function() {
      model.currentButton = (model.currentButton + 1) % model.buttons.length;
      return model.buttons[model.currentButton];
    }
  };

  // View for the app
  const view = {
    init: function() {
      this.root = document.getElementById('root');
      this.showNext();
    },

    animationDelay: 500,

    showNext: function() {
      // Get next button name
      const buttonName = controller.getButtonName();

      // Create button DOM element
      const buttonElement = document.createElement('div');
      buttonElement.className = 'button';
      buttonElement.id = buttonName;
      buttonElement.textContent = buttonName;
      buttonElement.style.opacity = 0;

      // Add event listender for the button
      buttonElement.addEventListener('click', event => {
        // Reduce opacity
        buttonElement.classList.toggle('active');
        buttonElement.style.opacity = 0;
        // Remove the button from DOM
        setTimeout(() => {

          this.root.removeChild(buttonElement);
        }, this.animationDelay + 10);
        // Start the function to show next button
        setTimeout(() => {
          this.showNext();
        }, this.animationDelay + 20);
      });

      // Add button to DOM
      this.root.appendChild(buttonElement);

      // Show button by increasing opacity
      buttonElement.classList.toggle('active');
      buttonElement.style.opacity = 1;

    }
  };

  // Start the app
  controller.init();

}());
#tomato {
  background: tomato;
}
#blue {
  background: DeepSkyBlue;
}
.button {
  opacity: 0;
  transition: opacity 0.5s ease-in-out;
  width: 100%;
  height: 50vh;
  border: solid 3px black;
  cursor: pointer;
}
.button.active {
  opacity: 1;
  transition: opacity 0.5s ease-in-out;
}
<div id="root"></div>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Thanks for your help, Saa_keetin's solution seems to be more straightforward. Also, for some reason when I'm running your code here, there is no smooth fade in transition. – Piotr Berebecki Nov 06 '16 at 20:22
  • 1
    Yeah, you change the duration and easing to fit your needs. ex. `transition: opacity 1s linear` Saa_keetin's is much more efficient. My proposition is easily expanded by use of CSS animation on `.active` class. – zer00ne Nov 06 '16 at 20:27
1

after this.root.appendChild(buttonElement);

you should set opacity to 0 and let the browser time to render before buttonElement.style.opacity = 1;

BTW I think removing and adding the element of not a good way to do this

.button {

  width: 100%;
  height: 50vh;
  border: solid 3px black;
  cursor: pointer;
   animation-name: example;
    animation-duration:3.5s;

}
@keyframes example {
        0%   {opacity:1}
        50%  {opacity:0}
    100%  {opacity:1}

}

What U really want is to use animation like this:JSFIDDLE EXAMPLE

This way the animation does all this timing and opacity back and forth using the css only

O_Z
  • 1,515
  • 9
  • 11