JSFiddle - React, Tailwind, and code Playground

by Posandu Mapa

HTML

<h1>Ripple Click Effect</h1>
<ul>
  <li><a>Dashboard</a></li>
  <li><a>My Account</a></li>
  <li><a>Direct Messages</a></li>
  <li><a>Chat Rooms</a></li>
  <li><a>Settings</a></li>
  <li><a>Logout</a></li>
</ul>

CSS

/*custom fonts - Bitter, Montserrat*/
@import url('httsp://fonts.googleapis.com/css?family=Montserrat|Bitter');


/*basic reset*/
* {
  margin: 0;
  padding: 0;
}


h1 {
  font: normal 32px/32px Bitter;
  color: white;
  text-align: center;
  padding: 85px 100px;
}

/*nav styles*/
ul {
  background: white;
  border-top: 6px solid hsl(180, 40%, 60%);
  width: 200px;
  margin: 0 auto;
}

ul li {
  list-style-type: none;
  /*relative positioning for list items along with overflow hidden to contain the overflowing ripple*/
  position: relative;
  overflow: hidden;
}

ul li a {
  font: normal 14px/28px Montserrat;
  color: hsl(180, 40%, 40%);
  display: block;
  padding: 10px 15px;
  text-decoration: none;
  cursor: pointer;
  /*since the links are dummy without href values*/
  /*prevent text selection*/
  user-select: none;
  /*static positioned elements appear behind absolutely positioned siblings(.ink in this case) hence we will make the links relatively positioned to bring them above .ink*/
  position: relative;
}

/*.ink styles - the elements which will create the ripple effect. The size and position of these elements will be set by the JS code. Initially these elements will be scaled down to 0% and later animated to large fading circles on user click.*/
.ink {
  display: block;
  position: absolute;
  background: hsl(180, 40%, 80%);
  border-radius: 100%;
  transform: scale(0);
}

/*animation effect*/
.ink.animate {
  animation: ripple 0.65s linear;
}

@keyframes ripple {

  /*scale the element to 250% to safely cover the entire link and fade it out*/
  100% {
    opacity: 0;
    transform: scale(2.5);
  }
}

JavaScript

//jQuery time
var parent, ink, d, x, y;
$("ul li a").click(function(e) {
  parent = $(this).parent();
  //create .ink element if it doesn't exist
  if (parent.find(".ink").length == 0)
    parent.prepend("<span class='ink'></span>");

  ink = parent.find(".ink");
  //incase of quick double clicks stop the previous animation
  ink.removeClass("animate");

  //set size of .ink
  if (!ink.height() && !ink.width()) {
    //use parent's width or height whichever is larger for the diameter to make a circle which can cover the entire element.
    d = Math.max(parent.outerWidth(), parent.outerHeight());
    ink.css({
      height: d,
      width: d
    });
  }

  //get click coordinates
  //logic = click coordinates relative to page - parent's position relative to page - half of self height/width to make it controllable from the center;
  x = e.pageX - parent.offset().left - ink.width() / 2;
  y = e.pageY - parent.offset().top - ink.height() / 2;

  //set the position and add class .animate
  ink.css({
    top: y + 'px',
    left: x + 'px'
  }).addClass("animate");
})