CAVaccineInventory / vial

The Django application powering calltheshots.us

Home Page:https://vial.calltheshots.us

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Protect against accidental double-submissions

simonw opened this issue · comments

  1. I just added a location, and hit save and continue editing twice (by accident) and it saved two of them- had to merge them.

https://discord.com/channels/799147121357881364/813861006718926848/862730318821982208

Some thoughts here: https://stackoverflow.com/questions/15671335/prevent-multiple-form-submissions-in-django

My concern is accidentally disabling the submit button despite there being validation errors on-page or similar, such that the user cannot later submit it properly.

One workaround for this: when the user clicks "submit", disable it only for the next 5 seconds - that way if something goes wrong they can wait 5 seconds and try again.

This seems to work (I tried it out on https://htmledit.squarefree.com/ with the console open):

<form action="https://httpbin.org/delay/10" method="POST">
<input type="text" name="Name">
<input type="submit">
</form>

<script>
function protectForm(form) {
    var locked = false;
    form.addEventListener('submit', (ev) => {
        if (locked) {
            console.log('locked');
            ev.preventDefault();
            return;
        }
        locked = true;
        setTimeout(() => {
            locked = false;
            console.log('unlocking');
        }, 2000);
        console.log('allowed but now locked');
    });
}

Array.from(document.querySelectorAll('form')).forEach(protectForm);
</script>

@simonw NodeList already has a forEach method, so you can skip creating an array with Array.from and iterate over the collection directly.

- Array.from(document.querySelectorAll('form')).forEach(protectForm);
+ document.querySelectorAll('form').forEach(protectForm);

You can simplify using event delegation:

<form action="https://httpbin.org/delay/10" method="post">
  <input type="text" name="name">
  <input type="submit">
</form>

<form action="https://httpbin.org/delay/10" method="post">
  <input type="text" name="name">
  <input type="submit">
</form>

<script>
var locks = new WeakSet();
document.addEventListener("submit", function(e){
  var form = e.target;
  if (locks.has(form)) {
    console.warn("Form already submitted", form);
    e.preventDefault();
    return;
  }
  
  locks.add(form);
  setTimeout(function(){
    locks.delete(form);
    console.log("Auto-release form lock", form);
  }, 2000);
});
</script>