Ever wanted to have a nice click effect on your buttons? Today we’re going to create one called: The Ripple Effect. See the demo below:
HTML:
A simple button will do… You could also add a class if you need, but it’s not required if you want to have this effect on all your buttons.
<button>Click me</button>
CSS:
button {
color: #fff;
border: 1px solid purple;
background-color: purple;
font-size: 14px;
padding: 20px 30px;
letter-spacing: 2px;
text-transform: uppercase;
position: relative;
overflow: hidden;
}
Just some basic styling for the button… The part which is important for the ripple effect is the positioning
and the overflowing
:
- The positioning must be relative because we will have the inner circle positioned
absolute
in order to be able to position it wherever the user clicks. - Overflow must be
hidden
as we don’t want to user to see the circle when it scales outside the button.
Styling the inner circle:
button .circle {
position: absolute;
border-radius: 50%;
background-color: #fff;
width: 100px;
height: 100px;
transform: translate(-50%, -50%) scale(0);
animation: scale 0.5s ease-out;
}
@keyframes scale {
to {
transform: translate(-50%, -50%) scale(3);
opacity: 0;
}
}
As mentioned above, we’ll have a .circle
within the button, which will be positioned absolute
, but it won’t have a top
and left
property set yet… We’ll set that dynamically in the JS code.
Note that we need to translate
the circle by -50%
on both X and Y axes as this will position it right in the middle of the mouse click.
Also, it’s initially scaled down to 0
and in the animation we scale it up to 3
and hide it by setting the opacity to 0
. Not rocket science, eh? ;)
Now for the fun part:
The JavaScript:
First we select all the buttons, and we add a click
event for all of them:
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', (e) => {
}
}
For calculating exactly where the circle should be placed in the button, we’re going to need to do some calculations, but don’t worry, it’s pretty simple. :D
We need:
- The X and Y coordinates for where the mouse was clicked.
- The top and left offset of the button.
- The top and left positioning for the circle within the button. We’ll get this by subtracting the button offset positioning from the mouse click coordinates.
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', function(e) {
// 1
let x = e.clientX;
let y = e.clientY;
// 2
let buttonTop = e.target.offsetTop;
let buttonLeft = e.target.offsetLeft;
// 3
let xInside = x - buttonLeft;
let yInside = y - buttonTop;
});
});
Now what’s left is to create the element (pretty much any tag would do), give it a class, set the calculated top
and left
positioning and append
it to the button (this will make the .circle
go INSIDE the button, exactly as we need it):
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', function(e) {
// 1
let x = e.clientX;
let y = e.clientY;
// 2
let buttonTop = e.target.offsetTop;
let buttonLeft = e.target.offsetLeft;
// 3
let xInside = x - buttonLeft;
let yInside = y - buttonTop;
let circle = document.createElement('span');
circle.classList.add('circle');
circle.style.top = yInside + 'px';
circle.style.left = xInside + 'px';
this.appendChild(circle);
});
});
Everything works now, except that there is a little issue… Every time the button is clicked, a new .circle
(DOM element) is created, which is not practical.
We can solve this by setting an interval
function which will remove the .circle
from the DOM after 500
milliseconds:
const buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('click', function(e) {
// 1
let x = e.clientX;
let y = e.clientY;
// 2
let buttonTop = e.target.offsetTop;
let buttonLeft = e.target.offsetLeft;
// 3
let xInside = x - buttonLeft;
let yInside = y - buttonTop;
let circle = document.createElement('span');
circle.classList.add('circle');
circle.style.top = yInside + 'px';
circle.style.left = xInside + 'px';
this.appendChild(circle);
setTimeout(() => {
circle.remove();
}, 500);
});
});
This was easy and fun, don’t you think? ^_^
See the code live on Codepen and make sure you subscribe to my mailing list if you liked this post!
Thanks!