0

Apologies in advance, I am completely new to transitions.

I am working with CSS and JavaScript to attempt a menu that slides down smoothly on click, and returns just as smoothly with another click.

I tried to mimic an example animation as shown in here: https://codepen.io/shshaw/pen/gsFch

My relevant code is as follows:

HTML

<div class="sort">
  <div class="sort-options">
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </div>
</div>

CSS

.sort-options {
  margin: 20px;
  display: none;
  flex-direction: column;
  align-items: flex-start;
  font-size: 20px;
  width: 100%;
  transition: display 3s ease-out;
}

JS

  const sortDiv = document.querySelector('.sort') 
  sortDiv.addEventListener('click', function() {
    const sortOptions = document.querySelector('.sort-options');
    if (sortOptions.style.display === 'none') {
      sortOptions.style.display = 'flex';
    } else {
      sortOptions.style.display = 'none';
    }
  }); 

The click does hide and show the element, but no animation is given. I tried to achieve the effect using only CSS, but I have found the same result, in which it does hide and show, but no smooth sliding down. This makes me think I am not doing something right with CSS.

CSS only attempt:

.sort:hover .sort-options {
  display: flex;
}

.sort-options {
  visibility: hidden;
  margin: 20px;
  display: none;
  flex-direction: column;
  align-items: flex-start;
  font-size: 20px;
  width: 100%;
  transition: all 3s ease-in-out;
}

My full code is here if you want to review it: https://wulfenn.github.io/todolist

I did some browsing around stackoverflow and it seems that display is not supported as a transition effect, but I am not certain how to achieve the effect using visibility while not occupying the space if the div is not visible.

Help appreciated!

Jack
  • 1,893
  • 1
  • 11
  • 28
Wulfen
  • 33
  • 3
  • Isn't this a duplicate of: https://stackoverflow.com/questions/3508605/how-can-i-transition-height-0-to-height-auto-using-css Tl;Dr: You *cannot* transition `display`, neither `height` `0` to `auto`. – Roko C. Buljan Sep 18 '20 at 23:16

2 Answers2

1

This is because your using display to animate, which is not animated. You're looking for opacity and pointer-events which can give a similar result.

Below is an example

.sort {
  width: 100px;
  
  text-align: center;
}

.sort-heading {
  position: relative;
  z-index: 2;

  background-color: white;
}

.sort:hover .sort-options {
  opacity: 1;
  transform: translateY(0px);
  pointer-events: all;
}

.sort-options {
  display: flex;
  
  width: 100%;
  
  flex-direction: column;
  align-items: flex-start;
  
  margin: 20px;
  
  font-size: 20px;
  
  transition: all 3s ease-in-out;
  
  transform: translateY(-50px);
  opacity: 0;
  pointer-events: none;
}
<div class="sort">
  <div class="sort-heading">Sort</div>
  <div class="sort-options">
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </div>
</div>

First I removed visibility: hidden which was not necessary. I also made it always have display: flex and then simply remove the opacity to have it fade in. I also used pointer-events so that the mouse won't interact with it while it's hidden. I used transform: translateY(-50px) so it would go down some. I also had to add a heading that could be hovered on to make the menu appear (sort-heading). Its also used to hide the options as they come down.

Edit

To not take up space, just add position: absolute to .sort-options.

If you want to control it with just JS, change your CSS line to .sort:hover .sort-options { to .sort.active .sort-options {

Now you can easily open and close the dropdown with

//Open
document.querySelector('.sort').classList.add('active');

//Close
document.querySelector('.sort').classList.remove('active');

If you still want the hover event you can either control that with JS or CSS

JS

var sortEle = document.querySelector('.sort');

sortEle.onmouseenter = function() {
    //Open
    sortEle.classList.add('active');
}

sortEle.onmouseleave = function() {
    //Close
    sortEle.classList.remove('active');
}

CSS

Make .sort.active .sort-options { .sort.active .sort-options, .sort:hover .sort-options {

Jack
  • 1,893
  • 1
  • 11
  • 28
  • Thank you for reply. I have a couple questions about your example. I used it and it does add an animation I can work on, but how can I make it so that those divs do not take up space while they are hidden? With [visibility] that space is always occupied. And how would I be able to translate this to the javascript? – Wulfen Sep 19 '20 at 00:06
  • @Wulfen, check my edit. Did not test the code though, should work – Jack Sep 19 '20 at 00:17
  • Great! I think position: absolute gives me what I am looking for. I have enough to go on, thanks! – Wulfen Sep 19 '20 at 00:21
  • @Wulfen, Sure. If I answered your question, you can mark it as the accepted answer – Jack Sep 19 '20 at 01:01
0

The reason display as a transition is not working, is because there are no values to animate(transition) to/from. The display attribute is more like on/off.

Here is a simple example...

#container {
position: relative;
height: 2rem;
width: 300px;
background-color: blue;
margin: auto;
}

.menu {
position: absolute;
visibility: hidden;
display: flex;
top: 2rem;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: grey;
height: 0rem;
width: 100%;
overflow: hidden;
transition: height 1s ease-in-out, visibility 1s ease-in-out;
}


#container:hover .menu{
visibility: visible;
height: 8rem;
}
<div id='container'>
  <div class='menu'>
    <p>item 1</p>
    <p>item 2</p>
    <p>item 3</p>
   </div>
</div>
Jason
  • 367
  • 1
  • 8
  • Thank you, I think I understand that display is not attribute I can transition off from. How would I go about ensuring that the space those divs use is not taken while they're hidden? – Wulfen Sep 19 '20 at 00:09
  • Well then you could go ahead and add/remove display: flex / display: none like you were originally. – Jason Sep 19 '20 at 00:31