I needed a click - too many

I needed a click - too many

Photo by By S. Bollmann - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=42555908

Well, technically it wasn't me - it was Bill Atkinson! Heck, even the quote isn't me - that's Giovanni "Giorgio" Moroder

Removing the double-click

One very frustrating issue with the web is that of servers getting hit by double POSTs!

Symptom:

In your tables you find strange looking records almost identical to one another, perhaps except for a few 100 milliseconds apart on the created_at timestamp.

Cause:

When users – by habit or error – double-clicks a form submit button, you get "double the fun" in your tables.

Another cause is network congestion. TCP connections abide by a send-ACK rule by which the receiver is required to send an 'ACK' (acknowledge) after each packet within a given timeframe or the sender will resend it, and theoretically, statistically (and, at least a little while longer, certainly in reality ) packets will 'drop' (not being ACK'ed) and thus get resend. Not by the client but some equipment in between. If an entire form and data will travel in a single packet, the risk is bigger. So the receiver does receive the packet but the ACK is not received (due to congestion) by the sender within the timeframe, hence the sender resubmits the packet - and you're left with the "body-double".

Action:

There really are only one way to do away with the double-click entirely - that is writing solid server code!

That part of double-clicks that are "on" the client and - let's be honest - on the user could be remedied by this JavaScript code - which BTW is not mine either; it all belongs to Andrea Giammarchi:

function disableMultipleSubmits() { // by Andrea Giammarchi - WTFPL
  Array.prototype.forEach.call(
    document.querySelectorAll('form[disablemultiplesubmits]'),
    function (form) {
      form.addEventListener('submit', this, true);
    },
    {
      // button to disable
      query: 'input[type=submit],button[type=submit]',
      // delay before re-enabling
      delay: 500,
      // handler
      handleEvent: function (e) {
        var button = e.currentTarget.querySelector(this.query);
        button.disabled = true;
        setTimeout(function () {
          button.disabled = false;
        }, this.delay);
      }
    }
  );
}