Skip to content
This repository has been archived by the owner on Jun 1, 2022. It is now read-only.

Protect against accidental double-submissions #722

Closed
simonw opened this issue Jul 8, 2021 · 5 comments
Closed

Protect against accidental double-submissions #722

simonw opened this issue Jul 8, 2021 · 5 comments
Labels
django-admin and tools Anything involving the /admin/ tools

Comments

@simonw
Copy link
Collaborator

simonw commented Jul 8, 2021

  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

@simonw simonw added the django-admin and tools Anything involving the /admin/ tools label Jul 8, 2021
@simonw
Copy link
Collaborator Author

simonw commented Jul 8, 2021

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.

@simonw
Copy link
Collaborator Author

simonw commented Jul 8, 2021

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 simonw closed this as completed in 1a79b1e Jul 8, 2021
@simonw
Copy link
Collaborator Author

simonw commented Jul 8, 2021

@abdusco
Copy link

abdusco commented Jul 9, 2021

@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);

@mems
Copy link

mems commented Jul 25, 2021

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>

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
django-admin and tools Anything involving the /admin/ tools
Projects
None yet
Development

No branches or pull requests

3 participants