Rahul
Rahul

Reputation: 518

Unable to show and hide text with fade effect on click

I was trying to create a similar effect on up and down arrows as shown in the image below but got stuck midway because of my low javascript/jquery skills.

I can't figure out how to make the text appear and then fade away on click with color change.

Here's a link to the fiddle just in case SO code snippet doesn't work

$("span").click(function() {
    $("span").css("color", "grey");
    $(this).css("color", "red");
  });
ul > li{
  list-style:none;
} 
span {
    cursor: pointer;
}

.fa {
    font-size: 55px;
    text-indent: 200px;
    margin-bottom: 20px;
    margin-top:30px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
    <li><span id='select1'><i class="fa fa-long-arrow-up" aria-hidden="true"></i></span></li>
    <li><span id='select2'><i class="fa fa-long-arrow-down" aria-hidden="true"></i></span></li>
    </ul>

So far none of the answers have worked for me so I am asking for more help on this.

I saw this effect on reddit and I've tried many times and spent so much time but failed to get the similar effect. I'd really appreciate it if anybody could help me understand and create the exact effect.

Upvotes: 1

Views: 1183

Answers (4)

Anton Boritskiy
Anton Boritskiy

Reputation: 1569

here is my version of the solution, https://jsfiddle.net/hnk1vw6x/33/ see some explanations below.

HTML

<div class="padding-container">
<span id="rating">0</span>
<a class="arrow fa fa-arrow-up" data-animation-text="Nice!" data-value="1"></a><br/>
<a class="arrow fa fa-arrow-down" data-animation-text="Troll" data-value="-1"></a>
</div>

CSS

.padding-container {
  width: 60px;
  margin: 100px;
}
#rating {
  float: right;
  font-size: 2.1em;
  width: auto;
}
a.arrow {
  display: inline-block;
  position: relative;
  cursor: pointer;
}
a.arrow:after {
  content: attr(data-animation-text);
  display: block;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  width: auto;
  opacity: 0;
}
a.arrow.fa-arrow-up {
  color: #FF0000;
}
a.arrow.fa-arrow-down {
  color: #0000FF;
}
a.arrow.fa-arrow-up:after {
  bottom: 100%;
}
a.arrow.fa-arrow-down:after {
  top: 100%;
}
a.arrow.animate.fa-arrow-up:after {
  animation-name: slideup, bounce;
  animation-duration: 3s;
}
a.arrow.animate.fa-arrow-down:after {
  animation-name: slidedown, bounce;
  animation-duration: 3s;
}
@keyframes slideup {
  from {
    bottom: 100%;
    opacity: 1;
  }
  to {
    bottom: 300%;
    opacity: 0;
  }
}
@keyframes slidedown {
  from {
    top: 100%;
    opacity: 1;
  }
  to {
    top: 300%;
    opacity: 0;
  }
}
@keyframes bounce {
  from {
    font-size: 1em;
  }
  3% {
    font-size: 1.25em;
  }
  6% {
    font-size: 0.75em;
  }
  9% {
    font-size: 1em;
  }
}

JavaScript

function arrowAnimationEndHandler(e) {
  var arrow = e.target;
  if (typeof arrow === 'undefined') {
    return;
  }

  arrow.className = arrow.className.replace(/\banimate\b/,'');
}

function arrowClickHandler(e) {
  var arrow = e.target;
  if (typeof arrow === 'undefined') {
    return;
  }

  arrow.className = arrow.className.replace(/\banimate\b/,'');
  setTimeout(function () {
    arrow.className += ' animate';
  }, 0);

  ratingUpdateBusinessLogic(arrow);
}

function ratingUpdateBusinessLogic(arrow) {
  if (typeof ratingElement === 'undefined') {
    return;
  }

  var ratingDelta = parseInt(arrow.getAttribute('data-value'), 10);
  ratingElement.innerHTML = parseInt(ratingElement.innerHTML, 10) + ratingDelta;
}
var ratingElement = document.getElementById("rating");
var arrows = document.getElementsByClassName("arrow");

for (var i = 0; i < arrows.length; i++) {
  arrows[i].addEventListener("animationend", arrowAnimationEndHandler, false);
  arrows[i].addEventListener("click", arrowClickHandler, false);
}

Now little bit of explanation:

The problem is quite complex and author is asking for a complete solution rather then explanation of one aspect which is not clear. I decided to give an answer because then I can outline the software design steps, which might help someone else to solve another complex problem.

In my opinion the key to complex tasks is the ability to split them in smaller, which in turn are easier to approach. Let's try to split this task into smaller pieces:

  1. We need to draw two arrows and a number.
  2. Up and down arrows should have different colors.
  3. We need to draw the arrow tooltips/labels next to them.
  4. We need to animate the arrow tooltips/labels on user interaction.
  5. We need to apply our business logic (change the rating) on user input.

Now let's try to solve those smaller problems one by one:

  1. We need to draw two arrows and a number. Well, HTML is our friend here and below is a trivial html code. I'm using font-awesome to draw the actual arrow icons.

    <div class="padding-container">
      <span id="rating">0</span>
      <a class="arrow fa fa-arrow-up"></a>
      <a class="arrow fa fa-arrow-down"></a>
    </div>
    

We want our arrows to be positioned in a certain way on the screen, let's make the arrows inline-blocks, and add a line-break between them, also add some CSS to line up:

    .padding-container {
      width: 60px;
      margin: 100px;
    }
    #rating {
      float: right;
      font-size: 2.1em;
      width: auto;
    }
    a.arrow {
      display: inline-block;
      cursor: pointer;
    }
  1. Our arrows should have different colors. Again trivial CSS here. The colors are not 100% like in the gif, but that is the question of making the screenshot and picking the right color - you can do it yourself.

    a.arrow.fa-arrow-up {
      color: #FF0000;
    }
    a.arrow.fa-arrow-down {
      color: #0000FF;
    }
    
  2. We need to draw the arrow tooltips/labels next to them. Ok, that starts to be interesting. Let's use the :after pseudo-element to draw our tooltips, because those tooltips are part of representation (and not content), they don't need to be reflected in the html structure. I use :after and not :before because font-awesome is using before for the arrow icon rendering ;) Let's also use absolute positioning to place them relative to the actual arrows. That gives us the following CSS:

    a.arrow {
      position: relative;
    }
    a.arrow:after {
      content: attr(data-animation-text);
      display: block;
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      text-align: center;
      width: auto;
    }
    a.arrow.fa-arrow-up:after {
      bottom: 100%;
    }
    a.arrow.fa-arrow-down:after {
      top: 100%;
    }
    

    Now, our tooltips are rendered just next to the arrows, and we have the possibility to control the content of them through html, e.g. for translation purposes. Tooltips are also centered relative to the arrows.

  3. We need to animate the arrow tooltips/labels on user interaction. We can animate elements by javascript and we can also do that via CSS. Doing it via CSS is way more efficient, so unless we need to support really old browsers, let's stick to CSS. We need to implement two animations, one is tooltip fading together with lift/drop and the second one is the tooltip bounce. Let's what CSS has to offer:

    a.arrow:after {
      opacity: 0;
    }
    a.arrow.fa-arrow-up:after {
      animation-name: slideup, bounce;
      animation-duration: 3s;
    }
    a.arrow.fa-arrow-down:after {
      animation-name: slidedown, bounce;
      animation-duration: 3s;
    }
    @keyframes slideup {
      from {
        bottom: 100%;
        opacity: 1;
      }
      to {
        bottom: 300%;
        opacity: 0;
      }
    }
    @keyframes slidedown {
      from {
        top: 100%;
        opacity: 1;
      }
      to {
        top: 300%;
        opacity: 0;
      }
    }
    @keyframes bounce {
      from {
        font-size: 1em;
      }
      3% {
        font-size: 1.25em;
      }
      6% {
        font-size: 0.75em;
      }
      9% {
        font-size: 1em;
      }
    }
    

    Now we see a nice label animation straight after we load the page. All that was done without a single line of JavaScript so far. But the task says we need to animate on user interaction.

    Ok, let's now add some javascript. But before that we need a possibility to trigger the animation, let's trigger it using CSS class: animate, our CSS then changes like

    a.arrow.animate.fa-arrow-up:after {
      animation-name: slideup, bounce;
      animation-duration: 3s;
    }
    a.arrow.animate.fa-arrow-down:after {
      animation-name: slidedown, bounce;
      animation-duration: 3s;
    }
    

    Note added animate class. If we now manually add the class to the HTML - we will see the animation again. But we need that to happen on user click, well that is easy:

    function arrowClickHandler(e) {
      var arrow = e.target;
    
      arrow.className += ' animate';
    }
    
    var arrows = document.getElementsByClassName("arrow");
    for (var i = 0; i < arrows.length; i++) {
      arrows[i].addEventListener("click", arrowClickHandler, false);
    }
    

    Now, if we load the page and click the arrow - we will see the animation, but only once. We need to find a way to reset it. Let's remove the animate class on animation finish.

    function arrowAnimationEndHandler(e) {
      var arrow = e.target;
      if (typeof arrow === 'undefined') {
        return;
      }
    
      arrow.className = arrow.className.replace(/\banimate\b/,'');
    }
    
    var arrows = document.getElementsByClassName("arrow");
    for (var i = 0; i < arrows.length; i++) {
      arrows[i].addEventListener("animationend", arrowAnimationEndHandler, false);
    }
    

    Now, we can click the arrow and see an animation as many times as we want. But there is a problem, we can't restart the animation if it is going already. For that we need a little trick:

    function arrowClickHandler(e) {
      var arrow = e.target;
      if (typeof arrow === 'undefined') {
        return;
      }
    
      arrow.className = arrow.className.replace(/\banimate\b/,'');
      setTimeout(function () {
        arrow.className += ' animate';
      }, 0);
    }
    

    as long as we remote the animate class - we give the browser a chance to execute it's code and stop the animation and then we add the animate class again.

  4. We need to apply our business logic (change the rating) on user input. Here is no rocket science, we read current value and update it according to the values we have assigned to arrows:

    function arrowClickHandler(e) {
      ...     
      ratingUpdateBusinessLogic(arrow);
    }
    
    function ratingUpdateBusinessLogic(arrow) {
      if (typeof ratingElement === 'undefined') {
        return;
      }
    
      var ratingDelta = parseInt(arrow.getAttribute('data-value'), 10);
      ratingElement.innerHTML = parseInt(ratingElement.innerHTML, 10) + ratingDelta;
    }
    var ratingElement = document.getElementById("rating");
    

UPDATE: solution with glyphicons would require replacing css/html classes fa fa-arrow-up and fa fa-arrow-down with corresponding glyphicon classes, i.e.: glyphicon glyphicon-arrow-up and glyphicon glyphicon-arrow-down. After little thinking I also decided to unbind the custom css from library classes and added custom arrow-up and arrow-down classes to simplify the icon library replacement:

<a class="arrow arrow-up glyphicon glyphicon-arrow-up" data-animation-text="Sick!" data-value="1"></a>
<a class="arrow arrow-down glyphicon glyphicon-arrow-down" data-animation-text="Suck!" data-value="-1"></a>

CSS

a.arrow.arrow-up {
  .
}
a.arrow.arrow-down {
  ...
}
a.arrow.arrow-up:after {
  ...
}
a.arrow.arrow-down:after {
  ...
}
a.arrow.animate.arrow-up:after {
  ...
}
a.arrow.animate.arrow-down:after {
  ...
}

Upvotes: 3

Navneeth
Navneeth

Reputation: 988

You can use jquery animate to get that effect. Try this

EDIT: for exact effect use jquery easing plugin and give

easeOutElastic easing effect

$("#select1").click(function() {
  $(".nice").css("display","block");
  
  $(".nice").animate({
      top: -10, 
    }, 500, "easeOutElastic", function() {
    // Animation complete.
      $(".nice").css({"opacity":"1", "top":"10px","display":"none"});
  });
   });

$("#select2").click(function(){
   $(".troll").css("display","block");
    $(".troll").animate({
      top: 130,     
    }, 500,"easeOutElastic", function(){
       $(".troll").css({"opacity":"1", "top":"120px","display":"none"});
    });
  });
ul > li{
  list-style:none;
} 
span {
    cursor: pointer;
}

.fa {
    font-size: 55px;
    text-indent: 200px;
    margin-bottom: 20px;
    margin-top:30px;
}
.nice{
    position:absolute;
    top:10px;
    text-indent :190px;
    display:none;
}
.troll{
   position:absolute;
   top:120px;
   text-indent : 190px;
  display:none;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<ul>
    <li>
      <p class="nice">Nice</p>
      <span id='select1'><i class="fa fa-long-arrow-up" aria-hidden="true"></i></span></li>
    <li><span id='select2'><i class="fa fa-long-arrow-down" aria-hidden="true"></i></span>
   <p class="troll">Troll</p>
  </li>
    </ul>

Upvotes: 2

Aircrafter
Aircrafter

Reputation: 89

Add a setTimeout to make the text fade out after a few milliseconds:

$("span").click(function() {
  $("span").css("color", "grey");
  $(this).css("color", "red");
});
$("#select1").click(function() {
  $("#down").fadeOut(300);
  $("#up").fadeIn(300);
  setTimeout(function() {
    $("#up").fadeOut(300); // fade out the up text
  }, 300); // delay of 0.3s before fading out
});
$("#select2").click(function() {
  $("#up").fadeOut(300);
  $("#down").fadeIn(300);
  setTimeout(function() {
    $("#down").fadeOut(300); // fade out the down text
  }, 300); // delay of 0.3s before fading out
});

jsFiddle: https://jsfiddle.net/qze7mqj4/16/

I also added a position:absolute; to the fading text so that it doesn't make the arrows "jump" around.

You can read more about setTimeout here: http://www.w3schools.com/jsref/met_win_settimeout.asp

Basically it tells the browser to execute a function after a specified number of milliseconds, in this case, we tell the browser to fadeOut() the text after 300ms.

Upvotes: 0

Nalin Aggarwal
Nalin Aggarwal

Reputation: 888

Just Add the text and show/hide it with the help of fadeout and fadein property of Jquery. Check your updated fiddle

$("span").click(function() {
  if($(this).attr('id')=='select1')
    {
      $("#downText").fadeOut(300);
        $("#upText").fadeIn(300);      
    }
    else
    {
    $("#upText").fadeOut(300);
        $("#downText").fadeIn(300);
    }
    $("span").css("color", "grey");
    $(this).css("color", "red");
  });

$("fa").click(function(){
        $("fa").fadeTo("slow", 0.15);
});

Upvotes: 1

Related Questions