skip to Main Content

Consider this scenario:

  1. form.html has a link to send_email.php.
  2. A user clicks the link twice very quickly.
  3. The server receives two requests for send_email.php and processes them both.
  4. send_email.php sent two emails, but only one should have been sent.

How can we use jQuery to disable the link after the first page request has been made by the browser?

I have built you an elegant MWE. This webpage links to itself and increments a counter with every page request. If you quickly double-click the link (before the current DOM is destroyed to load the page request), it will make two page requests, and you’ll see the counter increment by two. Your task is to write jQuery that will allow the first page request to process as usual, but block any subsequent clicks from creating additional page requests.


Many other people ask questions that seem similar but are actually entirely different use cases. Often they want this:

  1. Display a link or a button on a webpage
  2. Always block its default event (making a page request or submitting a form)
  3. Run some arbitrary Javascript exactly once
  4. Subsequently disable the link or button
    Here are solutions to this different question.

Notice how my use case is different. I have no Javascript to run. I do want the link or button to work as usual! I want it to work exactly once! All I want is to prevent a user from creating a bunch of page requests by mashing a link or a button. I want to allow the normal link or button function, but only once!


I should be able to drop some jQuery into this PHP script and accomplish exactly what the page describes as the expected behavior.

<?php

sleep(1);

session_name('have-a-cookie');
session_start(['cookie_lifetime' => 3600]);

$_SESSION['counter'] = isset($_SESSION['counter']) ? $_SESSION['counter']+1 : 0;

?>
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <link rel="stylesheet" href="bootstrap.min.css" type="text/css" />
        <script src="jquery-3.6.1.min.js"></script>
        <script src="bootstrap.bundle.min.js"></script>
        <script>
// this does not work
// https://stackoverflow.com/questions/41693832/disable-a-link-after-click-using-jquery
$('.btn').on('click', function(e) {
    $(this).prop('disabled',true);
});
        </script>
    </head>
    <body>
        <div class="m-3 card text-dark bg-light" style="max-width: 20em;">
            <div class="card-body">
                <h2>Count: <?php echo $_SESSION['counter']; ?></h2>
                <p>The goal is to prevent multiple requests.</p>
                <p>Double-clicking the button should make <b>one</b> request and increment the counter by one.</p>
                <p><a class="btn btn-primary" href="mwe.php">Count!</a></p>
                <p class="mb-0"><a href="mwe.php.txt">Source code</a></p>
            </div>
        </div>
    </body>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    One solution is to convert the button-styled link into a submit button within a form.

    Use this script:

    (function () {
        var allowSubmit = true;
        $('form').onsubmit = function () {
           if (allowSubmit)
               allowSubmit = false;
           else
               return false;
        }
    })();
    

    Credit to Andy E for this component.

    Substitute the <button> with a form:

    <form action="solution_lucas.php" method="get">
        <input type="submit" class="btn btn-primary" value="Count!" />
    </form>
    

    The live working solution is available here (at least temporarily).

    Complete source code for the working solution is available here.

    I will wait for other better or more elegant solutions! This one puts a question mark at the end of the URL. It is highly flexible though, in case you want send data with the request.


  2. Update

    With further clarification in the comments, all OP needs is to disable further clicks on the link while the browser is in the process of navigating to the target page. As you suggested, there are indeed many duplicate questions, here’s one where the accepted answer should work fine for your use case.

    The idea is that the very first click on the link will fire this event handler, which will only run once, and will not stop the actual navigation. The event handler creates a new event handler that disables further clicks.

    I voted to close this question as a duplicate of (also linked above):


    Original Answer

    Searching for how to disable a link turns up this article, which gives some very good reasons about why not to do it. A link without a, well, link, is meaningless – links are for navigating, and if you don’t intend to navigate somewhere, maybe it shouldn’t be a link.

    The article suggets removing the href, but in your case you are not actually navigating, so that would not help. Your link is styled as a button, and for good reason – making it a button is not only more semantically correct, it would make disabling it a whole lot easier.

    If you really want to stick with a link:

    • removing href is useless because you are not actually navigating anyway;

    • .attr('disabled', 'disabled'), or .prop() variants are useless because a link cannot be disabled;

    • .preventDefault() is actually required, because the default action when clicking a link is to navigate, and you don’t want to do that, but it won’t stop future clicks;

    • The nuclear option – removing the entire click handler so no more clicks are processed by JS – won’t work either, because then you lose your .preventDefault(), and a click will act like a plain, vanilla, no-JS click and naviate away to the href target!

    One option is to track whether or not the link has been clicked with a variable. Set it to true first time through, and only allow the processing if it is false. Working example:

    // Track whether or not the link has been clicked
    let clicked = false;
    
    $('.btn').on('click', function(e) {
        // The default action for a link is to navigate - you don't
        // want to do that, so you do need to prevent it
        e.preventDefault();
        
        // Check if we have clicked, and bail out if so
        if (clicked) {
            return;
        }
        
        // Track that we have clicked
        clicked = true;
        
        // Some visual indicator so we can see what is happening
        console.log('click!');    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <a class="btn" href="somewhere.html">Count!</a>

    If you want to switch to a button, things are considerably simpler, as well as being semantically correct and elegant:

    $('.btn').on('click', function(e) {
        // The default action for a button is to submit - you don't
        // want to do that, so you do need to prevent it
        e.preventDefault();
        
        // Disable button
        $(this).attr('disabled', 'disabled');
        
        // Some visual indicator so we can see what is happening
        console.log('click!');    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    
    <button class="btn" href="somewhere.html">Count!</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search