0

I have a menu button and a menu, there is 1 known bug:

Bug: One of the events is on mouseleave of the menu box and it should add a class dropDownMenuPulsate which is applied but does not work, I am forced to use !important in css animation declaration, why?

To reproduce this bug: click the menu button, place the mouse over the menu and then out(don't go back over), wait... in 2 seconds it applies animation if !important is used and 4 seconds later menu closes as expected.

var menuAutoHideTimeoutId = '';
var menuAutoWarningTimeoutId = '';
// dropdown menu button click
$("#dropDownMenuBtn").on("click", function(){
  event.stopPropagation();
    if($("#dropDownMenuWrap").css("opacity") == 0){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapHideAnimation').addClass('dropDownMenuWrapShowAnimation');
    }else{
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation').addClass('dropDownMenuWrapHideAnimation');
       if($("#dropDownMenuWrap").hasClass("dropDownMenuPulsate")){
          $("#dropDownMenuWrap").removeClass('dropDownMenuPulsate');
          window.clearTimeout(menuAutoWarningTimeoutId);
          window.clearTimeout(menuAutoHideTimeoutId);
       } 
    }
});

$(".dropDownMenuLinkWrap").on("click", function(){
  console.log('page render');
    // avoid menu hide to fire when click click outside menu wrap
    event.stopPropagation();
})

// dropdown menu hide on click outside menu wrap 
$(document).on("click", function(){
  if($("#dropDownMenuBtn:hover").length == 0){
    if($("#dropDownMenuWrap").css("opacity") == 1){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation').addClass('dropDownMenuWrapHideAnimation');
    }
  }
});

$("#dropDownMenuWrap").on("mouseleave", function(){
  // add class showing pulsating effect as warning of closing soon
  menuAutoWarningTimeoutId = setTimeout(function(){ 
    if($("#dropDownMenuWrap").hasClass("dropDownMenuWrapShowAnimation")){
      $("#dropDownMenuWrap").addClass('dropDownMenuPulsate');
    } 
  }, 2000);
  menuAutoHideTimeoutId = setTimeout(function(){ 
    // add closing animation and remove others
    if($("#dropDownMenuWrap").hasClass("dropDownMenuWrapShowAnimation")){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation dropDownMenuPulsate').addClass('dropDownMenuWrapHideAnimation');
    } 
  }, 6000);
})

//mouse re enter cancel mouseout event
$("#dropDownMenuWrap").on("mouseenter", function(){
  window.clearTimeout(menuAutoWarningTimeoutId);
  window.clearTimeout(menuAutoHideTimeoutId);
  if($("#dropDownMenuWrap").hasClass("dropDownMenuPulsate")){      
     $("#dropDownMenuWrap").removeClass('dropDownMenuPulsate');
  } 
})
.dropDownMenuPulsate{
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  /*ANIMATION WORKS ONLY WITH !IMPORTANT!!!!*/
  animation: pulsateAnimation .2s linear infinite !important;
}
@keyframes pulsateAnimation {
  0% { transform: rotate(10deg); }
  33% { transform: rotate(0deg); }
  66% { transform: rotate(-10deg); }
  100% { transform: rotate(0deg); }
}
#dropDownMenuBtn {
    cursor: pointer;
    position: fixed;
    top: 3px;
    right: 20px;
    font-size: xx-large;
    color: #f997bb;
}
#dropDownMenuBtn:hover {
    color: lightgrey;
}
.dropDownMenuWrapInitial{
  position: fixed;
  z-index: 99;
  top: 60px;
  background: white;
  border: 1px solid lightgrey;
  width: 200px;
  padding: 10px;
  border-radius: 5px;
  pointer-events :none;
  right:-300px;
  opacity: 0;
}
.dropDownMenuWrapShowAnimation{
  pointer-events :auto;
  opacity:1;
  right:10px;
  transform-origin: top right;
  -webkit-transform-origin: top right;
  -ms-transform-origin: top right;

  animation: .2s ease 1 linkMenuShowAnimation;
  -webkit-animation: .2s ease 1 linkMenuShowAnimation;
}

@-webkit-keyframes linkMenuShowAnimation {
  0%{
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
  100%{
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
}
@keyframes linkMenuShowAnimation {
  0%{
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
  100%{
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
}

.dropDownMenuWrapHideAnimation{
  pointer-events :none;
  opacity:0;
  right:-300px;
  transform-origin: top right;
  -webkit-transform-origin: top right;
      -ms-transform-origin: top right;

  animation: .2s ease 1 linkMenuHideAnimation;
  -webkit-animation: .2s ease 1 linkMenuHideAnimation; 
}

@-webkit-keyframes linkMenuHideAnimation {
  0%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
  100%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
}
@keyframes linkMenuHideAnimation {
  0%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
  100%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


  <div id="dropDownMenuBtn">
    Menu button
  </div>
  
  <div id="dropDownMenuWrap" class="dropDownMenuWrapInitial">
      <div class="dropDownMenuLinkWrap">
        <p class="dropDownMenuLinkText">Link 1</p>
      </div>
      <div class="dropDownMenuLinkWrap">
        <p class="dropDownMenuLinkText">Link 2</p>
      </div>
      <div class="dropDownMenuLinkWrap">      
        <p class="dropDownMenuLinkText">Link 3</p>
      </div>
  </div>
</div>
Al Ex Tsm
  • 2,042
  • 2
  • 29
  • 47

2 Answers2

0

This happens because the element have three classes among which 2 of them contains animations. In such cases, the order from the css file will be followed. Since you put dropDownMenuPulsate first and dropDownMenuWrapShowAnimation second in css file, the animation of dropDownMenuWrapShowAnimation will override the animation of dropDownMenuPulsate (you can see it in element inspector).

The fix

Move dropDownMenuPulsate block below dropDownMenuWrapShowAnimation block in css file, as you can see here.

var menuAutoHideTimeoutId = '';
var menuAutoWarningTimeoutId = '';
// dropdown menu button click
$("#dropDownMenuBtn").on("click", function(){
  event.stopPropagation();
    if($("#dropDownMenuWrap").css("opacity") == 0){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapHideAnimation').addClass('dropDownMenuWrapShowAnimation');
    }else{
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation').addClass('dropDownMenuWrapHideAnimation');
       if($("#dropDownMenuWrap").hasClass("dropDownMenuPulsate")){
          $("#dropDownMenuWrap").removeClass('dropDownMenuPulsate');
          window.clearTimeout(menuAutoWarningTimeoutId);
          window.clearTimeout(menuAutoHideTimeoutId);
       } 
    }
});

$(".dropDownMenuLinkWrap").on("click", function(){
  console.log('page render');
    // avoid menu hide to fire when click click outside menu wrap
    event.stopPropagation();
})

// dropdown menu hide on click outside menu wrap 
$(document).on("click", function(){
  if($("#dropDownMenuBtn:hover").length == 0){
    if($("#dropDownMenuWrap").css("opacity") == 1){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation').addClass('dropDownMenuWrapHideAnimation');
    }
  }
});

$("#dropDownMenuWrap").on("mouseleave", function(){
  // add class showing pulsating effect as warning of closing soon
  menuAutoWarningTimeoutId = setTimeout(function(){ 
    if($("#dropDownMenuWrap").hasClass("dropDownMenuWrapShowAnimation")){
      $("#dropDownMenuWrap").addClass('dropDownMenuPulsate');
    } 
  }, 2000);
  menuAutoHideTimeoutId = setTimeout(function(){ 
    // add closing animation and remove others
    if($("#dropDownMenuWrap").hasClass("dropDownMenuWrapShowAnimation")){
      $("#dropDownMenuWrap").removeClass('dropDownMenuWrapShowAnimation dropDownMenuPulsate').addClass('dropDownMenuWrapHideAnimation');
    } 
  }, 6000);
})

//mouse re enter cancel mouseout event
$("#dropDownMenuWrap").on("mouseenter", function(){
  window.clearTimeout(menuAutoWarningTimeoutId);
  window.clearTimeout(menuAutoHideTimeoutId);
  if($("#dropDownMenuWrap").hasClass("dropDownMenuPulsate")){      
     $("#dropDownMenuWrap").removeClass('dropDownMenuPulsate');
  } 
})
@keyframes pulsateAnimation {
  0% { transform: rotate(10deg); }
  33% { transform: rotate(0deg); }
  66% { transform: rotate(-10deg); }
  100% { transform: rotate(0deg); }
}
#dropDownMenuBtn {
    cursor: pointer;
    position: fixed;
    top: 3px;
    right: 20px;
    font-size: xx-large;
    color: #f997bb;
}
#dropDownMenuBtn:hover {
    color: lightgrey;
}
.dropDownMenuWrapInitial{
  position: fixed;
  z-index: 99;
  top: 60px;
  background: white;
  border: 1px solid lightgrey;
  width: 200px;
  padding: 10px;
  border-radius: 5px;
  pointer-events :none;
  right:-300px;
  opacity: 0;
}
.dropDownMenuWrapShowAnimation{
  pointer-events :auto;
  opacity:1;
  right:10px;
  transform-origin: top right;
  -webkit-transform-origin: top right;
  -ms-transform-origin: top right;

  animation: .2s ease 1 linkMenuShowAnimation;
  -webkit-animation: .2s ease 1 linkMenuShowAnimation;
}
.dropDownMenuPulsate{
  /*ANIMATION Works without !important
  ¯\_(ツ)_/¯
  */
  animation: pulsateAnimation .2s linear infinite;
}
@-webkit-keyframes linkMenuShowAnimation {
  0%{
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
  100%{
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
}
@keyframes linkMenuShowAnimation {
  0%{
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
  100%{
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
}

.dropDownMenuWrapHideAnimation{
  pointer-events :none;
  opacity:0;
  right:-300px;
  transform-origin: top right;
  -webkit-transform-origin: top right;
      -ms-transform-origin: top right;

  animation: .2s ease 1 linkMenuHideAnimation;
  -webkit-animation: .2s ease 1 linkMenuHideAnimation; 
}

@-webkit-keyframes linkMenuHideAnimation {
  0%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
  100%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
}
@keyframes linkMenuHideAnimation {
  0%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(0deg);
            transform: rotate(0deg);
  }
  100%{
    right:10px;
    opacity:1;
    -webkit-transform: rotate(-90deg);
            transform: rotate(-90deg);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


  <div id="dropDownMenuBtn">
    Menu button
  </div>
  
  <div id="dropDownMenuWrap" class="dropDownMenuWrapInitial">
      <div class="dropDownMenuLinkWrap">
        <p class="dropDownMenuLinkText">Link 1</p>
      </div>
      <div class="dropDownMenuLinkWrap">
        <p class="dropDownMenuLinkText">Link 2</p>
      </div>
      <div class="dropDownMenuLinkWrap">      
        <p class="dropDownMenuLinkText">Link 3</p>
      </div>
  </div>
</div>

Reference
Just a personal opinion, are you sure you want to show such an animation in a page?

Sagar V
  • 12,158
  • 7
  • 41
  • 68
0

When .dropDownMenuPulsate is added to #dropDownMenuWrap, that element already has .dropDownMenuWrapShowAnimation.

Both classes have an "animation" property, and since .dropDownMenuPulsate is declared first, .dropDownMenuWrapShowAnimation is overriding it.

You could move .dropDownMenuPulsate declaration to the bottom, or make it more specific:

.dropDownMenuWrapShowAnimation.dropDownMenuPulsate {
  ...
}
azeós
  • 4,853
  • 4
  • 21
  • 40