HTML フォームの送信2 分で読めます
HTML フォームの action 属性を解説
フォームの action 属性の役割、相対値や空の値の解決のされ方、formaction で 1 つのフォームを 2 か所に送る方法、静的ホストが 405 を返す理由を解説します。
action 属性は、フォームがデータを送る URL です。相対値はページのアドレスを基準に解決され、空または未指定ならページ自身を指し、送信ボタンの formaction はそのボタンについてだけ上書きします。静的サイトでは、自分のホストは POST を受けられないので、https://ikiform.com/f/<formId> のようなフォームバックエンドの完全な URL を設定してください。
静的サイトで壊れているフォームのほとんどは、この 1 つの属性が原因です。マークアップは問題なく見え、ボタンも動くのに、ブラウザーには 405 か空白のページが表示されます。データが受け取れない場所に送られたからです。このページでは、ある action がデータをどこに送るのかを正確に示し、そのうえで formaction を使って 1 つのフォームを 2 つの受信先に振り分けます。
ここが違う
action はどの href とも同じ URL ですが、1 つだけ癖があります。action のないフォームは何もしないのではなく、自分が置かれたページに送信します。サーバーレンダリングのサイトなら、そのページが送信を処理するかもしれません。静的ホストでは処理できないので、action がない、または相対値だと、デプロイしてから初めて失敗します。
値ごとのデータの送り先
https://example.com/company/contact.html にあるページと、そこに置いた 5 つのフォームを例にします。
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
<form action="/api/contact" method="post">
<form action="send" method="post">
<form action="" method="post">
<form method="post">| action | 解決後 | 静的ホストでは |
|---|---|---|
https://ikiform.com/f/frm_... | その URL のまま | 動きます。Ikiform が POST を受けます |
/api/contact | https://example.com/api/contact | そのパスで関数を実行するホストでのみ動きます |
send | フォルダー基準で https://example.com/company/send | 405 か 404 |
"" (empty) | ページ自身、contact.html | ほとんどの静的ホストで 405 |
| 未指定 | ページ自身 | ほとんどの静的ホストで 405 |
- 絶対 URL の action は書かれたとおりに使われるので、送信は Ikiform に届き、Ikiform はリダイレクトを返します。
- 相対、空、未指定の action は、ページ自身のアドレス、またはページに base 要素があればそれを基準に解決されます。
- そのため送信は自分のホストに向かいます。静的ホストはファイルを配信するだけなので、POST を拒否します。たいていは 405 です。
<base href> 要素は、action を含むすべての相対 URL の解決方法を変えます。サイトビルダーやシングルページアプリの構成によっては、これを追加するものがあります。完全な URL を使えば影響はありません。ブラウザーが使う値を確認する
ブラウザーのコンソールでは、属性とプロパティが異なります。getAttribute は書いたとおりの値を返し、action プロパティはブラウザーが実際に送信する解決済みの URL を返します。
const form = document.querySelector("form");
form.getAttribute("action");
form.action;form.action に自分のドメインが表示されたら、データはそこに送られます。Webflow や Framer などのビルダーは、自身の設定パネルから action を設定することが多いので、ソースではなく公開済みのページを確認してください。各ビルダーがどこにそれを持っているかはサイトビルダーのガイドにあります。
1 つのフォーム、2 つの受信先
送信ボタンの formaction は、そのボタンについてだけフォームの action を置き換えます。ここでは、1 組のフィールドを 2 つの Ikiform フォームに送ります。Ask support はサポートフォームに、Talk to sales は営業フォームに送信し、各 Ikiform フォームはそれぞれの宛先にメールを送ります。
<form action="https://ikiform.com/f/frm_supportxxxxxxxxx" method="post">
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="message">How can we help?</label>
<textarea id="message" name="message" rows="5" required></textarea>
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
<button>Ask support</button>
<button formaction="https://ikiform.com/f/frm_salesxxxxxxxxxxx">Talk to sales</button>
</form>- Ikiform のフォームはそれぞれ通知の受信者、スパム設定、Webhook を持つので、サポートと営業が互いのメールを目にすることはありません。
- 両方のフォームに同じフィールド定義を設定するか、どちらにも設定しないでください。フィールドを定義すると、そのフォームが知らないフィールドは
422で拒否されます。 - ほかの
form*属性も同じように動作します:formmethod、formenctype、formnovalidate、formtargetです。formnovalidateを付けた Save draft ボタンは、ブラウザーのチェックをスキップします。 formactionが使えるのは送信ボタンだけです:<button>、<input type="submit">、<input type="image">です。
別の方法として、topic フィールドを持つ Ikiform のフォーム 1 つにまとめ、Webhook や受信箱のフィルターでトピックごとに振り分けることもできます。そうすればリストもエクスポートも 1 つで済みます。2 つのチームで受信者をまったく共有すべきでないなら、フォームを 2 つにするほうがシンプルです。
クエリ文字列付きの action
method="get" では、ブラウザーは action のクエリ文字列をフォームのフィールドで置き換えます。下の 1 つ目のフォームの lang=de は黙って捨てられます。2 つ目は hidden input として残しています:
<form action="/search?lang=de" method="get">
<input name="q" />
</form>
<form action="/search" method="get">
<input name="q" />
<input type="hidden" name="lang" value="de" />
</form>method="post" では、フィールドはボディに入るので、クエリ文字列は残ります。ただし Ikiform では何も変わりません。エンドポイント URL のクエリパラメーターは保存されないので、残したい値には hidden input を使ってください。
フォームの外にある入力欄
レイアウトによっては、ほかのコンテンツの隣に置くフッターの登録欄のように、<form> 要素の中にない入力欄が必要になることがあります。入力欄やボタンの form 属性は、id でフォームに結び付け、そのフォームの action に送信します:
<form id="newsletter" action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post"></form>
<footer>
<input form="newsletter" name="email" type="email" required />
<button form="newsletter">Subscribe</button>
</footer>mailto: と javascript: の action
action="mailto:you@example.com" は、訪問者のメールアプリにフィールド入りのメッセージを開かせます。メールアプリが設定されていて送信ボタンを押さない限り何も送られず、送ろうとしたことすらあなたにはわかりません。action="javascript:..." は送信せずにコードを実行し、ほとんどのコンテンツセキュリティポリシーでブロックされます。どちらも本物のエンドポイントに置き換える価値があります。
動作を確認する
- フォームの Allowed domains に自分のサイトのドメインを追加し、開発中は
localhostも追加します。空のままにすると、どこからの送信も受け付けます。 - ソースではなく公開されたページを開き、コンソールで
document.querySelector("form").actionを実行します。Ikiform の完全な URL が表示されます。 - フォームを送信します。Network パネルには、自分のドメインへの POST ではなく、
303が返されたikiform.comへの POST が表示されます。 - 受信箱が 2 つのフォームで、それぞれのボタンで 1 回ずつ送信します。各送信データは、ダッシュボードでそれぞれのフォームの下に表示されます。
- 通知の各受信者の受信トレイを確認します。それぞれにフィールド入りのメールが届き、フォームに
emailフィールドがあれば Reply で送信者に返信できます。
ハマりどころ
| 問題 | 原因と対処 |
|---|---|
自分のドメインから 405 Method Not Allowed | action が相対パス、空、または欠けているので、送信が静的ホストに届いています。Ikiform の完全な URL を使ってください |
ikiform.com から 404 not_found | action のフォーム ID が間違っているか、フォームが削除されています。ダッシュボードのフォームのページからエンドポイントをコピーしてください |
| ビルダーが action を何度もリセットする | フォームブロックの action をビルダーが管理していることがあります。ビルダーのカスタム action の設定か、自分のマークアップを使った埋め込みを使ってください |
| ブラウザーがフォームは安全でないと警告する | https: のページで action が http: を使っています。Ikiform のエンドポイントは https: のみです。スキームを修正してください |
| 2 つ目のフォームのフィールドも送信される | フォームは入れ子にできません。ブラウザーは内側の <form> タグで外側のフォームを閉じます。代わりに form 属性を使ってください |
クリックからリダイレクトまでの送信の流れ全体は概要を参照してください。エンドポイントが受け付けるものはすべて HTML フォームにあります。
よくある質問
そのフォームがあるページに送信されます。サーバーがその URL で送信を処理するなら動きますが、静的ホストでは失敗し、たいていは 405 Method Not Allowed になります。
action はフォームに設定し、すべての送信に適用されます。formaction は 1 つの送信ボタンに設定し、そのボタンが使われたときに action を上書きするので、1 つのフォームから 2 か所に送れます。
はい。ブラウザーは特別な設定なしにクロスサイトで送信します。静的なページがフォームバックエンドに送信できるのはこのためです。受け付けるかどうかは受け取る側のサーバーが決めます。Ikiform はページの origin をフォームの許可ドメインと照合します。
method が get だと、ブラウザーは action のクエリ文字列をフォーム自身のフィールドで置き換えます。各パラメーターを hidden input に移してください。method が post ならクエリ文字列は残ります。
手順で詰まったときや、あなたの状況に合うガイドがほしいときはメールでどうぞ: hello@ikiform.com