AJAXの問い合わせフォーム
同じ問い合わせ項目を fetch で JSON 送信します。訪問者はページに残り、短い状態表示を見ます。
コピーする内容
このスターターは、同じ問い合わせ項目を fetch で送ります。訪問者はページに留まり、短い状態表示を読みます。
フォームには、これまでどおり action と method POST があります。JavaScript が動かない場合、ブラウザは通常の送信を行います。
この代替経路は、古いブラウザや、スクリプトを止める人のために必要です。フォームを二つ保守する必要はありません。
主となるスニペットは JSON を送ります。Content-Type を application/json に、Accept を application/json に設定します。
成功は JSON の論理値 success です。それが true のとき、スニペットは応答メッセージ、または短い礼状を表示し、フォームをリセットします。
false のときは、スニペットが応答メッセージを表示します。Formgong はそのメッセージを _lang の言語で書きます。
JSON と FormData
JSON の本文には、送りたいフィールドを列挙する必要があります。スニペットには access_key、_lang、name、email、phone、message、botcheck が含まれます。
あとから Turnstile のコメントを外すと、JSON の経路は、その欄に値があるとき cf-turnstile-response もコピーします。
追加スニペットは JSON の代わりに FormData を使います。コンテンツタイプは、境界を含めてブラウザが設定します。Content-Type は自分で設定しないでください。
FormData はフォーム内のすべての欄を拾います。ウィジェットがフォームの中にあれば、Turnstile のトークンも含みます。
どちらの経路も Accept: application/json を送ります。このヘッダーがないと、ブラウザの通常のフォーム送信は JSON ではなくサンクスページを受け取ります。
JSON の本文と multipart の本文を同時に送らないでください。どちらか一方を選んでください。二つのスニペットは代替であり、重ねるものではありません。
スパム、Telegram、上限
ハニーポットの名前は botcheck のまま、空で、画面の外に、tabindex -1 で置いてください。埋まったハニーポットはスパムとして保存され、配信されません。
Telegram が太字で表示できるよう、フィールド名は短く保ってください。email を長い文に改名しないでください。
アクセスキーはページに残します。サーバーの秘密へ移してもフォームは安全になりません。このスニペットにサーバーはありません。
無料プランでは Telegram はすぐ届き、メールは日次ダイジェストです。受信箱は送信を 30 日保持します。
無料プランには月 300 件の送信が含まれます。スパムの送信は、そのうちの 1 件を使いません。
このプレビューは送信しません。スニペットを自分のサイトへつなぎ、そのページからテストを送ってください。
サービスを運営しているのは Formgong です。製品についての問い合わせは support@formgong.com へお書きください。
プレビュー
フォームのコード
<form id="fg-ajax" action="https://formgong.com/submit" method="POST" style="position:relative">
<input type="hidden" name="access_key" value="fk_your_access_key">
<input type="hidden" name="_lang" value="ja">
<label>お名前 <input type="text" name="name" autocomplete="name" required></label>
<label>メールアドレス <input type="email" name="email" autocomplete="email" required></label>
<label>電話番号 <input type="tel" name="phone" autocomplete="tel"></label>
<label>メッセージ <textarea name="message" required></textarea></label>
<div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
<label>この欄は空のままにしてください <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
<!--
任意です。このフォームで Turnstile をオンにしてから、このコメント記号を外し、サイトキーを貼ってください。
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="ja"></div>
-->
<button type="submit">送信</button>
<p data-status role="status"></p>
</form>
<script>
(function () {
var form = document.getElementById("fg-ajax");
if (!form) return;
var status = form.querySelector("[data-status]");
form.addEventListener("submit", function (event) {
event.preventDefault();
var data = {
access_key: form.access_key.value,
_lang: form._lang.value,
name: form.name.value,
email: form.email.value,
phone: form.phone.value,
message: form.message.value,
botcheck: form.botcheck.value
};
var token = form.querySelector('[name="cf-turnstile-response"]');
if (token && token.value) data["cf-turnstile-response"] = token.value;
var body = JSON.stringify(data);
fetch("https://formgong.com/submit", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: body
}).then(function (res) { return res.json(); }).then(function (result) {
if (status) status.textContent = result && result.success ? (result.message || "送信しました。ありがとうございます。") : ((result && result.message) || "送信できませんでした。もう一度お試しください。");
if (result && result.success) form.reset();
}).catch(function () {
if (status) status.textContent = "送信できませんでした。もう一度お試しください。";
});
});
})();
</script>JSON の代わりに FormData を使う同じフォーム
<form id="fg-ajax" action="https://formgong.com/submit" method="POST" style="position:relative">
<input type="hidden" name="access_key" value="fk_your_access_key">
<input type="hidden" name="_lang" value="ja">
<label>お名前 <input type="text" name="name" autocomplete="name" required></label>
<label>メールアドレス <input type="email" name="email" autocomplete="email" required></label>
<label>電話番号 <input type="tel" name="phone" autocomplete="tel"></label>
<label>メッセージ <textarea name="message" required></textarea></label>
<div aria-hidden="true" style="position:absolute;inset-inline-start:0;top:0;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)">
<label>この欄は空のままにしてください <input type="text" name="botcheck" tabindex="-1" autocomplete="off"></label>
</div>
<!--
任意です。このフォームで Turnstile をオンにしてから、このコメント記号を外し、サイトキーを貼ってください。
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY" data-language="ja"></div>
-->
<button type="submit">送信</button>
<p data-status role="status"></p>
</form>
<script>
(function () {
var form = document.getElementById("fg-ajax");
if (!form) return;
var status = form.querySelector("[data-status]");
form.addEventListener("submit", function (event) {
event.preventDefault();
var body = new FormData(form);
fetch("https://formgong.com/submit", {
method: "POST",
headers: { Accept: "application/json" },
body: body
}).then(function (res) { return res.json(); }).then(function (result) {
if (status) status.textContent = result && result.success ? (result.message || "送信しました。ありがとうございます。") : ((result && result.message) || "送信できませんでした。もう一度お試しください。");
if (result && result.success) form.reset();
}).catch(function () {
if (status) status.textContent = "送信できませんでした。もう一度お試しください。";
});
});
})();
</script>よくある質問
なぜフォームに action の URL が残っているのですか?
JavaScript がオフでも送信が動くようにするためです。スクリプトは、実行されるときだけ preventDefault を呼びます。
JSON と FormData のどちらを送るべきですか?
どちらも有効です。JSON は各フィールドを明示します。FormData は、あとから入力欄を足すときに短く済みます。フォームを読み取るためです。
状態の行が空のままです。何が失敗していますか?
Accept が application/json であること、アクセスキーがご自身のフォームと一致すること、ページがローカルファイルではなく http または https で配信されていることを確認してください。
このフォームでファイルをアップロードできますか?
このスターターではできません。ファイル欄には、添付の有効化と、有効な Turnstile トークンが必要です。その設定はダッシュボードのドキュメントを参照してください。