# 素の JavaScript

> fetch で HTML フォームをプログレッシブエンハンスメントします。インラインのフィールドエラーとステータスメッセージを表示し、ページを再読み込みしません。

このページにはビルドの手順もパッケージも不要です。JavaScript がオフのときは通常の HTML の送信として動き、オンのときはスクリプトが `fetch` で送信して結果をインラインで表示します。

`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` を付けると、Ikiform はリダイレクトではなく JSON で応答します。
- `result.error.fields` はフィールド ID をキーにしています。スクリプトは各フィールドの下に最初のメッセージを表示します。
- `fetch` が reject するのはネットワークエラーのときだけです。HTTP エラーでも resolve され、ボディにエラーの情報が入っています。
- `429` のレスポンスには `Retry-After` が付きます。Ikiform が CORS で公開しているので、ブラウザーのコードから読み取れます。

## Ikiform の人間確認を使う場合

中間の確認ページが表示されるのは、通常のブラウザーのページ遷移の場合だけです。**Ikiform** のチェックがあるフォームに `fetch` で送信すると、代わりに `403 challenge_required` が返ります。バンドラー経由で [SDK](https://ikiform.com/ja/docs/sdk.md#challenge) の `challenge` を使うか、JavaScript なしでフォームを送信させてください。
