Button Ripple Effect

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:

demo

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:

  1. 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.
  2. 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:

  1. The X and Y coordinates for where the mouse was clicked.
  2. The top and left offset of the button.
  3. 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!

Tagged with html5, css3, animation, javascript, button, ripple-effect