Protect against accidental double-submissions
simonw opened this issue · comments
- 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>
Extracted this into a TIL: https://til.simonwillison.net/javascript/preventing-double-form-submission
@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>