4

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?

Community
  • 1
  • 1
War Gravy
  • 1,543
  • 3
  • 20
  • 32
  • 3
    The term for this, as I understand it, is **debouncing**, or at least that is what it is called when applied to the context of key presses. – zero298 Feb 10 '16 at 16:48
  • Thank you! I didn't know what the term was, I'm going to keep researching to see what I can find. – War Gravy Feb 10 '16 at 16:49
  • Possible duplicate of [jQuery disable/enable submit button](http://stackoverflow.com/questions/1594952/jquery-disable-enable-submit-button). Although the handler is different, the concept is exactly the same. – Evan Davis Feb 10 '16 at 16:49
  • 1
    Check this out: https://davidwalsh.name/javascript-debounce-function, just wrap your function with it. – Reda Feb 10 '16 at 16:50
  • 1
    Do you want to prevent subsequent clicks until the animation ends, or make it so subsequent clicks halt the previous animations and start a new one? Looking at your code, it looks like you might want the latter? Debouncing would fix the former, whereas you need to still handle the click and interrupt the jquery animation for the latter. – James Thorpe Feb 10 '16 at 16:50
  • The latter @JamesThorpe – War Gravy Feb 10 '16 at 16:51
  • @Reda - This looks like a solution to my problem, thank you! testing it now to see how it works. – War Gravy Feb 10 '16 at 16:53
  • Please tag `jquery` in your questions about jQuery from now on. Some of us JavaScript folk don't want questions about it in our feed. – birdoftheday Feb 10 '16 at 16:56
  • @stanpines - Sorry, I was just looking for a general answer and had no idea it was going to be specific to jquery or not. – War Gravy Feb 10 '16 at 16:58

3 Answers3

2

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.

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
1

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.

Joseph
  • 1,003
  • 3
  • 11
  • 25
1

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