2

Similar questions have been asked here in various forms of complexity. Some using jQuery:

How to toggle animate with css display:none.

And some very specific ones that over look the scope and simplicity of what is trying to be achieved:

JavaScript - add transition between display:none and display:block.

But take this very stripped down code for example:

var button = document.getElementsByTagName( 'button' )[ 0 ],
    div = document.getElementsByTagName( 'div' )[ 0 ];

button.addEventListener( 'click', toggleVisibility );

function toggleVisibility(){
  if( 
    div.classList.contains( 'show' ) 
   ){
    div.classList.remove( 'show' );
    div.classList.add( 'hide' );
  }
  else {
    div.classList.add( 'show' );
    div.classList.remove( 'hide' );
  }
}
div {
  height: 100px;
  width: 100px;
  background: #000;
  transition: 1s;
}

.hide {
  opacity: 0;
}

.show {
  opacity: 1;
}
<p>A square</p>

<div class="show"></div>

<br>

<button>click to toggle hide/show</button>

This successfully toggles the opacity. But as soon as I add display: none to the .hide class the effect breaks horribly:

var button = document.getElementsByTagName( 'button' )[ 0 ],
    div = document.getElementsByTagName( 'div' )[ 0 ];

button.addEventListener( 'click', toggleVisibility );

function toggleVisibility(){
  if( 
    div.classList.contains( 'show' ) 
   ){
    div.classList.remove( 'show' );
    div.classList.add( 'hide' );
  }
  else {
    div.classList.add( 'show' );
    div.classList.remove( 'hide' );
  }
}
div {
  height: 100px;
  width: 100px;
  background: #000;
  transition: 1s;
}

.hide {
  display: none; /* added "display:none" in this example */
  opacity: 0;
}

.show {
  opacity: 1;
}
<p>A square</p>

<div class="show"></div>

<br>

<button>click to toggle hide/show</button>

What is a simple, reusable solution that we can use without any plugins or libraries to first fade out, then turn off display and first turn on display then fade in?

Edit: I am using display over visibility because in this example as in most cases I use this effect I want the element completly removed as it is usually on top of other elements hindering mouseover/click effects.

Community
  • 1
  • 1
Lynel Hudson
  • 2,335
  • 1
  • 18
  • 34
  • If `display: none` breaks your display, you can always circumvent that by using `visibility: hidden` / `visibility: visible`. It has the effect of hiding the element, without actually removing its structure in the DOM :) – Obsidian Age Feb 16 '17 at 03:58
  • @ObsidianAge Please see **Edit**. – Lynel Hudson Feb 16 '17 at 04:01

3 Answers3

0

Add an event listener on transitionend to change the display property; it cannot transition as it just changes elements from mode to the other. So if you want to change display, do it before/after the transition begins.

var button = document.getElementsByTagName( 'button' )[ 0 ],
    div = document.getElementsByTagName( 'div' )[ 0 ];

button.addEventListener('click', toggleVisibility)
div.addEventListener('transitionend', function() {
    if (this.classList.contains('hide')) {
        this.style.display = 'none'
    }
})

function toggleVisibility(){
    if (div.classList.contains( 'show' )) {
        div.classList.remove( 'show' );
        div.classList.add( 'hide' );
    }
    else {
        div.style.display = 'block'
        div.classList.add( 'show' );
        div.classList.remove( 'hide' );
    }
}




div {
  height: 100px;
  width: 100px;
  background: #000;
  transition: 1s;
}

.hide {
  opacity: 0;
}

.show {
  opacity: 1;
}

EDIT: This doesn't work very well on the transition from hide => show, I've just realized. Another possible solution that would fix your issues with visibility is, not only change the visibility property, but also when it's hidden, just add a z-index: -10, or something like that (maybe not so ridiculous of a number). That was it stays out of the way as far as your layering is concerned, and it won't affect the transitions. (This might not work well if you need the element to be removed from the layout, but that hasn't been clarified.)

thesublimeobject
  • 1,393
  • 1
  • 17
  • 22
0

setvisibilty instead of display,and if you want to set sync you could set transition attribute to all(transition:all 1s).

code

div {
  height: 100px;
  width: 100px;
  background: #000;
  transition: visibility 0s, opacity 0.5s linear;
}

.hide {
  visibility: hidden;
  opacity: 0;
}

.show {
  visibility: visible;
  opacity: 1;
}
Rach Chen
  • 1,322
  • 2
  • 10
  • 26
0

Adding pointer-events to your .hide class should do the trick.

.hide {
  pointer-events: none;
  opacity: 0;
}
kvn
  • 2,210
  • 5
  • 20
  • 47
  • @solacyon My bad. Edited my answer. Can you recheck? `display: none` is not needed anymore as we just need it to be hidden and `pointer-events: none;` would disable any clicks happening on this element. – kvn Feb 16 '17 at 04:13
  • I think sol means that with 0 opacity the element still takes up space, same with visibility. Previously suggested it. – Zed Feb 16 '17 at 04:17
  • @solacyon explanation was that the hidden element should not hinder clicks on other elements. Not sure if he is talking about the space. – kvn Feb 16 '17 at 04:20
  • 1
    This is actually genius. Exactly what I was looking for but I should point out here that the height of the element would also have to be changed to get the full effect. Had no idea there was a `pointer-events` property! Thanks. @thesublimeobject also mentioned changing z-index to a negative number. This solution seems simplest. – Lynel Hudson Feb 16 '17 at 04:21