# JavaScript pur

> Améliorez progressivement un formulaire HTML avec fetch : erreurs de champ en ligne, message de statut et aucun rechargement de page.

Cette page ne nécessite ni build ni package. Le formulaire fonctionne comme un envoi HTML classique quand JavaScript est désactivé ; quand il est activé, le script envoie avec `fetch` et affiche le résultat en ligne.

`contact.html`:

```html
<form id="contact" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post" novalidate>
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required aria-describedby="email-error" />
  <p id="email-error" data-error-for="email"></p>
  <label for="message">Message</label>
  <textarea id="message" name="message"></textarea>
  <p id="message-error" data-error-for="message"></p>
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
  <p role="status" data-status></p>
</form>

<script type="module">
  const form = document.querySelector("#contact");
  const button = form.querySelector("button");
  const status = form.querySelector("[data-status]");

  function showErrors(fields = {}) {
    for (const slot of form.querySelectorAll("[data-error-for]")) {
      const messages = fields[slot.dataset.errorFor];
      slot.textContent = messages ? messages[0] : "";
      form.elements[slot.dataset.errorFor]?.setAttribute("aria-invalid", messages ? "true" : "false");
    }
  }

  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    button.disabled = true;
    status.textContent = "Sending";
    try {
      const response = await fetch(form.action, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(form),
      });
      const result = await response.json();
      showErrors(result.ok ? {} : result.error.fields);
      status.textContent = result.ok ? "Thanks, we got it." : result.error.message;
      if (result.ok) form.reset();
    } catch {
      status.textContent = "Couldn't reach the server. Check your connection and try again.";
    } finally {
      button.disabled = false;
    }
  });
</script>
```

- `Accept: application/json` fait répondre Ikiform en JSON au lieu d'une redirection.
- `result.error.fields` est indexé par id de champ ; le script écrit le premier message sous chaque champ.
- `fetch` n'est rejeté qu'en cas de panne réseau. Les erreurs HTTP se résolvent quand même, avec l'enveloppe d'erreur dans le corps.
- Une réponse `429` contient `Retry-After`, que le code navigateur peut lire car Ikiform l'expose via CORS.

## Avec la vérification humaine Ikiform

La page de vérification intermédiaire n'apparaît que lors d'une navigation classique dans le navigateur. Un envoi `fetch` vers un formulaire avec la vérification **Ikiform** reçoit à la place `403 challenge_required`. Utilisez `challenge` du [SDK](https://ikiform.com/fr/docs/sdk.md#challenge) via votre bundler, ou laissez le formulaire envoyer sans JavaScript.
