Roep event.preventDefault() aan vóór de eerste await, voer je asynchrone validatie uit en gebruik daarna form.requestSubmit() om een nieuwe verzendpoging te starten. Met een eenmalige beveiliging kan die tweede poging doorgaan zonder de asynchrone controle te herhalen.
Deze tutorial is bedoeld voor iemand die een kleine browserinterface op een Mac bewerkt. Je bouwt een lokaal HTML-voorbeeld waarin een formulier een dialoogvenster sluit nadat een synthetische labelcontrole slaagt. Hetzelfde patroon voor gebeurtenisafhandeling is van toepassing op gewone formulieren die navigeren of een verzoek verzenden.
Waarom stelt een asynchrone submit-handler het verzenden niet uit?
De browser wacht niet op de promise die door een gebeurtenislistener wordt geretourneerd. Het DOM-gebeurtenisdispatchalgoritme roept listeners aan en gaat verder; het HTML-verzendalgoritme controleert of de gebeurtenis submit is geannuleerd voordat het verdergaat. async toevoegen aan de listener introduceert geen wachtstap in die levenscyclus. DOM Standard: dispatch van gebeurtenissen, HTML Standard: algoritme voor formulierverzending
Een async-functie wordt synchroon uitgevoerd tot de eerste await en gaat daarna asynchroon verder. Daarom moet je vóór de eerste await annuleren, zelfs wanneer de promise waarop je wacht al is vervuld. preventDefault() daarna aanroepen kan een verzendbeslissing die de browser al heeft genomen niet ongedaan maken. MDN: async-functie
Annuleren beëindigt de oorspronkelijke poging; het pauzeert deze niet om later te hervatten. Nadat de validatie is geslaagd, moet je code expliciet een nieuwe verzending aanvragen. Bovendien laat preventDefault() de gebeurtenispropagatie intact, zodat andere listeners de gebeurtenis nog steeds kunnen waarnemen. MDN: Event.preventDefault()
Luister naar de gebeurtenis submit van het formulier. Dit omvat verzenden via een knop, ondersteunde impliciete verzending zoals het indrukken van Enter en aanroepen van requestSubmit(). Een click-handler die alleen aan een knop is gekoppeld, dekt niet elk toegangspunt. Native validatie van beperkingen wordt normaal gesproken eerst uitgevoerd: een ongeldig verplicht veld voorkomt verzending zonder submit te activeren. MDN: submit-gebeurtenis van HTMLFormElement
Welke methode behoudt het verzendgedrag van het formulier?
Gebruik requestSubmit() wanneer de geslaagde controle moet terugkeren naar de normale levenscyclus van het formulier. Als je submit() rechtstreeks aanroept, worden zowel de validatie van beperkingen als de gebeurtenis submit overgeslagen. Die snelkoppeling verandert het gedrag dat je asynchrone handler juist moet behouden. MDN: HTMLFormElement.submit()
| Aanpak | Wat gebeurt er | Geschiktheid in dit geval |
|---|---|---|
| Een promise retourneren vanuit de listener | De gebeurtenisdispatch gaat verder zonder erop te wachten | Stelt de standaardactie niet uit |
Annuleren en daarna submit() aanroepen |
Verzending omzeilt native validatie en submit-listeners | Verandert de levenscyclus |
Annuleren en daarna requestSubmit(submitter) aanroepen |
Native validatie en de submit-gebeurtenis worden opnieuw uitgevoerd | Behoudt het bedoelde verzendpad |
Leg event.submitter vast voordat je wacht. Door die knop aan requestSubmit() mee te geven, behoud je de rol ervan, inclusief verzendoverschrijvingen en, indien van toepassing, de bijdrage van de naam/waarde. Als er geen submitter was, roep je requestSubmit() zonder argument aan. Een meegegeven knop moet nog steeds bij het formulier horen; een ongeldige knop of een knop die bij een ander formulier hoort, veroorzaakt een uitzondering. MDN: HTMLFormElement.requestSubmit()
De nieuwe verzending activeert je listener opnieuw. Daarom gebruikt het onderstaande voorbeeld twee afzonderlijke statuswaarden: pending voorkomt overlappende asynchrone controles, terwijl allowNextSubmit precies één synchrone doorgang door de handler toestaat. Een permanente vlag ‘gevalideerd’ zou te ruim zijn, omdat latere wijzigingen een eerder resultaat opnieuw zouden kunnen gebruiken.
Bouw een compleet lokaal voorbeeld op je Mac
Sla het volgende volledige document op als async-form.html in een eenvoudige teksteditor voor code en open het daarna in een actuele browser op je Mac. Er zijn geen bibliotheken, imports, server of installatie nodig. De standaard vermeldt ondersteuning voor requestSubmit() vanaf Safari 16. HTML Standard: formulier-API's
Het voorbeeld gebruikt method="dialog" om de standaardactie lokaal goed zichtbaar te maken. Na een geslaagde verzending wordt het omvattende dialoogvenster gesloten en krijgt de eigenschap returnValue de waarde van de submit-knop; de formulierbesturingselementen worden niet verzonden. De openknop roept showModal() aan om het venster weer te geven. MDN: dialog-element, MDN: HTMLDialogElement.showModal()
De validator is bewust synthetisch: na een korte timer accepteert deze labels met na het trimmen ten minste drie tekens, wijst het exacte label taken in kleine letters af en werpt een uitzondering voor het exacte label error in kleine letters. De vertraging illustreert asynchrone voltooiing; het is geen prestatiemeting.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Asynchronous form validation</title>
</head>
<body>
<h1>Label preview</h1>
<button id="open" type="button">Open form</button>
<p id="outcome" role="status"></p>
<dialog id="editor" aria-labelledby="dialog-title">
<form id="label-form" method="dialog">
<h2 id="dialog-title">Choose a label</h2>
<label for="label">Label</label>
<input id="label" name="label" required autofocus>
<button type="submit" value="accepted">Continue</button>
<button id="cancel" type="button">Cancel</button>
<p id="status" role="status"></p>
</form>
</dialog>
<script>
const dialog = document.getElementById("editor");
const form = document.getElementById("label-form");
const input = document.getElementById("label");
const status = document.getElementById("status");
const outcome = document.getElementById("outcome");
const openButton = document.getElementById("open");
const cancelButton = document.getElementById("cancel");
let pending = false;
let allowNextSubmit = false;
let revision = 0;
async function validateLabel(value) {
await new Promise(resolve => setTimeout(resolve, 700));
const label = value.trim();
if (label === "error") {
throw new Error("Synthetic validation failure");
}
return label.length >= 3 && label !== "taken";
}
openButton.addEventListener("click", () => {
revision += 1;
dialog.returnValue = "";
status.textContent = "";
outcome.textContent = "";
dialog.showModal();
});
input.addEventListener("input", () => {
revision += 1;
});
cancelButton.addEventListener("click", () => dialog.close());
dialog.addEventListener("close", () => {
revision += 1;
outcome.textContent = "Dialog closed. Return value: " +
(dialog.returnValue || "(empty)");
});
form.addEventListener("submit", async event => {
if (allowNextSubmit) {
allowNextSubmit = false;
return;
}
event.preventDefault();
if (pending) return;
pending = true;
const submitter = event.submitter;
const value = input.value;
const checkedRevision = revision;
status.textContent = "Checking label...";
try {
const valid = await validateLabel(value);
if (!dialog.open) return;
if (revision !== checkedRevision || input.value !== value) {
status.textContent = "The label changed. Submit again.";
return;
}
if (!valid) {
status.textContent =
"Use at least three characters and avoid taken.";
return;
}
status.textContent = "Check passed. Submission requested.";
allowNextSubmit = true;
try {
if (submitter) form.requestSubmit(submitter);
else form.requestSubmit();
} finally {
allowNextSubmit = false;
}
} catch (error) {
if (dialog.open && revision === checkedRevision) {
status.textContent = "The check failed. Try again.";
}
console.error(error);
} finally {
pending = false;
}
});
</script>
</body>
</html>
Het browsergedrag van het voorbeeld is afgeleid van de gedocumenteerde levenscyclus. Een kleine Node.js-controle met EventTarget bevestigde het timingverschil: synchrone annulering veranderde het resultaat van de dispatch, terwijl annulering na await te laat kwam om dat resultaat te beïnvloeden. Het volledige dialoogvoorbeeld is hier niet uitgevoerd in een Mac-browser.
Hoe behoudt de beveiliging de volgorde en verwerkt deze wijzigingen?
Bij de eerste verzending annuleert de handler onmiddellijk en legt deze zowel het label als de knop waarmee de verzending is gestart vast. Terwijl de controle nog loopt, wordt een volgende submit-gebeurtenis ook geannuleerd, maar er wordt geen nieuwe validatie gestart. Zo worden overlappende controles door een snelle reeks verzendpogingen voorkomen.
Wanneer de controle is voltooid, controleert de handler of het dialoogvenster nog open is en of het label niet is gewijzigd. De revisieteller detecteert wijzigingen door de gebruiker, ook als die de tekst wijzigt en daarna herstelt. De waardevergelijking vangt ook een rechtstreekse waardewijziging op waarbij de tekst in het veld anders blijft.
Bij een geslaagd resultaat wordt allowNextSubmit direct vóór requestSubmit() ingesteld. Die methode voert de native validatie en de submit-gebeurtenisroute opnieuw uit. Wanneer de listener opnieuw wordt aangeroepen, verbruikt deze de toestemming en keert terug zonder te annuleren. De browser kan daarna de standaardactie uitvoeren en het dialoogvenster sluiten. Deze volgorde volgt de volgorde in de standaard, waarin validatie voorafgaat aan de submit-gebeurtenis. HTML Standard: algoritme voor formulierverzending
De binnenste finally wist de toestemming, ook als er geen submit-gebeurtenis optreedt of de methode een uitzondering veroorzaakt. De buitenste finally wist pending na een afwijzing, uitzondering, verouderd resultaat of geslaagde nieuwe poging. Een mislukte poging laat het formulier dus beschikbaar voor een volgende controle.
Het sluiten van het dialoogvenster verhoogt ook de revisie. Een resultaat van een eerdere opening kan geen toestemming geven voor een latere opening. Voor dit zelfstandige document dekken deze controles de beschikbare interactiepaden. Als je meer velden toevoegt of scripts die deze velden bijwerken, breid dan de momentopname en revisieafhandeling uit voor elke waarde waarvan de validator afhankelijk is.
Test de paden voor slagen, afwijzen en onderbreken
Voer deze controles uit in het lokale browservoorbeeld. De verwachte resultaten volgen uit de meegeleverde code; ze worden niet gepresenteerd als resultaten van browsertests.
| Actie | Verwacht resultaat |
|---|---|
Laat het label leeg en kies Doorgaan |
Native validatie van verplichte velden blokkeert de verzending voordat de asynchrone handler wordt uitgevoerd |
Voer ab in en kies Doorgaan |
Het dialoogvenster blijft open en toont na de controle het afwijzingsbericht |
Voer taken in |
De synthetische regel wijst dit af |
Voer sample in |
Het dialoogvenster blijft tijdens de controle open en wordt daarna gesloten met retourwaarde accepted |
Voer error in |
Het foutbericht verschijnt en een volgende verzendpoging blijft mogelijk |
| Verzend een label en wijzig het daarna tijdens de controle | Het resultaat wordt verworpen en het formulier vraagt om een nieuwe verzending |
| Verzend meerdere keren terwijl de controle loopt | Alleen de eerste poging start een asynchrone controle |
Kies Annuleren terwijl de controle loopt |
Het dialoogvenster sluit zonder op het lopende resultaat te wachten |
Probeer ook te verzenden met Enter terwijl de invoer focus heeft. Impliciete verzending hangt af van de formulierstructuur en platformconventies; de reden om submit af te handelen is om elke ondersteunde interactie te dekken die daadwerkelijk formulierverzending activeert. MDN: submit-gebeurtenis van HTMLFormElement
Wat verandert er als het formulier een normaal verzoek verzendt?
Behoud bij een bestaand formulier dat verzoeken verzendt de echte action, method, veldnamen, knopwaarden en verzendoverschrijvingen. Vervang de synthetische validator door je echte asynchrone controle. De code voor het openen en sluiten van het dialoogvenster is specifiek voor deze demonstratie; de reeks annuleren–valideren–opnieuw verzenden hoort thuis in de submit-handler van het formulier.
Houd velden beschikbaar voor de uiteindelijke verzending. Besturingselementen tijdens het wachten uitschakelen vereist zorgvuldigheid: uitgeschakelde velden worden weggelaten wanneer de browser de verzendlijst samenstelt. In het voorbeeld kan de gebruiker blijven bewerken en worden verouderde validatieresultaten verworpen. HTML Standard: de verzendlijst samenstellen
Als er andere submit-listeners zijn, ontvangen die zowel de eerste geannuleerde poging als de latere poging. Stem ze op elkaar af, zodat ze niet hetzelfde werk dubbel uitvoeren of gevalideerde waarden wijzigen tijdens de toegestane doorgang. requestSubmit() garandeert ook geen voltooiing: een andere listener kan de verzending annuleren en native beperkingen kunnen deze blokkeren. De retourwaarde is undefined, geen promise voor voltooiing. MDN: HTMLFormElement.requestSubmit()
De afspraak is precies: synchroon annuleren, een vastgelegde set waarden valideren, verouderde resultaten verwerpen en één nieuwe native verzendpoging toestaan. Zo krijgt asynchrone validatie een expliciete plek in de workflow, terwijl de levenscyclus van het formulier in de browser behouden blijft.
Bronnen
DOM Standard — WHATWG
HTML Standard: het form-element — WHATWG
async-functie — MDN Web Docs
Event: methode preventDefault() — MDN Web Docs
HTMLFormElement: submit-gebeurtenis — MDN Web Docs
HTMLFormElement: methode submit() — MDN Web Docs
HTMLFormElement: methode requestSubmit() — MDN Web Docs
Het dialog-element — MDN Web Docs
HTMLDialogElement: methode showModal() — MDN Web Docs