War Gravy
War Gravy

Reputation: 1683

How to prevent spamming of an onclick listener?

This is a new issue for me and I've been unable to find any information about this.

I have a simple onclick listener that performs a smooth scroll to a target on the page. Here is the code below:

$(".nav-scroll").click(function (event) {
    var ID = this.hash;
    //has a few extra conditionals here specific to the page
    $('html,body').animate({ scrollTop: $(ID).offset().top }, 1000);
});

Ultimately, the scenario is that this function takes time to execute. Not much time, but it does add up if someone spams the link on the page and the browser starts queuing the function calls. Now spamming is an extreme case, but even after 3 or 4 consecutive clicks it hinders the user experience while they can't scroll down the page because the page is trying to scroll them back to the target 3 or 4 times.

All my research has been able to turn up is checking if a window has an event listener already like found here: JavaScript - how to check if event already added or listing all the event listeners on an element like here: jQuery find events handlers registered with an object , but nothing to check to see if something is currently running.

Is it possible to prevent this side effect by dumping all previous listener calls on the page mid execution before executing or by another method? Or is this something that is not offered by JavaScript? If so, is there strategies to get around this like checking to see if the function is already executing?

Upvotes: 4

Views: 3091

Answers (3)

Tobias Berg
Tobias Berg

Reputation: 19

You could wrap you onClick function in a throttle which keeps it from executing again while it's scrolling.

Like so:

// lodash is defined here as _

var animationTime = 1000;

var scrollTo = _.throttle(function (event) {
    var ID = this.hash;
    //has a few extra conditionals here specific to the page
    $('html,body').animate({ scrollTop: $(ID).offset().top }, animationTime);
}, animationTime);

$(".nav-scroll").click(scrollTo);

Is this case the first time the users clicks the function gets called but if they click again within the time frame (the 1000ms) the the function does not execute again. Only after the time has passed can the user invoke the function again.

Here you can find the documentation for lodash throttle: https://lodash.com/docs#throttle

Upvotes: 1

Joseph
Joseph

Reputation: 1051

You need this:

var debounce = false;
$(".nav-scroll").click(function (event) {
    if (debounce) return;
    debounce = true;
    var ID = this.hash;
    //has a few extra conditionals here specific to the page
    $('html,body').animate({ scrollTop: $(ID).offset().top}, 1000, function() {debounce=false;});
});

Basically, it disables the onclick event on fire until it is scrolled up, then enables the event.

Upvotes: 1

James Thorpe
James Thorpe

Reputation: 32222

From the conversation in the comments, it sounds like you want the users to be able to click on the element as fast as they want to, and have it interrupt the existing animation and start a new one, rather than queueing them up and causing it to scroll around the page. You can achieve this by simply using stop:

$(".nav-scroll").click(function (event) {
    var ID = this.hash;
    //has a few extra conditionals here specific to the page

    //call stop to kill old animations
    $('html,body').stop().animate({ scrollTop: $(ID).offset().top }, 1000);
});

A debouncing approach would prevent them from clicking at all until the animation ends, rather than allowing them to click one element, realise they've clicked in slightly the wrong place and quickly click the right element.

Upvotes: 2

Related Questions