タイトル: Javascript のみで form を post で submit する方法
SEOタイトル: JavaScript のみで form を POST 送信する方法完全ガイド
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>HTML を一切書かず</strong>、JavaScript だけで <code><form></code> を動的生成 → POST 送信する手法</li><li>基本パターンは <code>document.createElement("form")</code> + <code>form.method="POST"</code> + <code>form.action=URL</code> + 各 <code>input</code> を <code>appendChild</code> + <code>document.body.appendChild(form)</code> + <code>form.submit()</code></li><li><strong>ページ遷移を伴う</strong>送信は form.submit、<strong>遷移なし</strong>は <code>fetch()</code> / <code>XMLHttpRequest</code></li><li>ファイル送信は <code>FormData</code> を使い <code>enctype="multipart/form-data"</code> を設定</li><li>Laravel / Rails 等は <strong>CSRF トークン</strong>を hidden input で含める必要あり</li></ul></td></tr></table>
<h2>なぜ JavaScript だけで form を作るのか</h2>
<p>HTML に <code><form></code> を書かず、JavaScript で動的にフォームを組み立てて POST 送信したい場面は意外と多いです。</p>
<ul>
<li><strong>ボタンクリックだけで別画面へ POST 遷移</strong>させたい(GET だとパラメータが URL に出る)</li>
<li>SPA から外部サイトに POST で飛ばす(決済ゲートウェイ等)</li>
<li>動的に生成したデータをそのまま送信</li>
<li>ブックマークレット / ユーザースクリプトから送信</li>
</ul>
<h2>最小コード</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">function postSubmit(url, params) {
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
for (const key in params) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = params[key];
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
}
// 利用例
postSubmit('/order/confirm', {
product_id: 123,
qty: 2,
coupon: 'SPRING20'
});</code></pre>
<p>このコードを実行すると、ブラウザは <code>/order/confirm</code> へ <strong>POST で画面遷移</strong>します。HTML の <code><form></code> をクリックしたのと完全に同じ挙動です。</p>
<h2>各プロパティの意味</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>プロパティ</th><th>値</th><th>説明</th></tr></thead>
<tbody>
<tr><td><code>form.method</code></td><td><code>POST</code> / <code>GET</code></td><td>HTTP メソッド。デフォルト GET</td></tr>
<tr><td><code>form.action</code></td><td>URL</td><td>送信先。相対 / 絶対どちらも可</td></tr>
<tr><td><code>form.target</code></td><td><code>_self</code> / <code>_blank</code></td><td>送信結果の表示先。新タブで開きたいときは <code>_blank</code></td></tr>
<tr><td><code>form.enctype</code></td><td><code>application/x-www-form-urlencoded</code> / <code>multipart/form-data</code></td><td>エンコード形式。ファイル送信は後者必須</td></tr>
<tr><td><code>form.acceptCharset</code></td><td><code>UTF-8</code></td><td>文字コード</td></tr>
</tbody>
</table>
<h2>document.body.appendChild は必要か</h2>
<p>はい、必要です。<strong>DOM ツリーに接続されていない form は submit できません</strong>(一部ブラウザで silently fail)。送信後すぐ削除する場合:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">document.body.appendChild(form);
form.submit();
// submit() 後にすぐ navigation が始まるので不要だが、
// target=_blank で残したくない場合:
form.remove();</code></pre>
<h2>CSRF トークンを含める(Laravel / Rails)</h2>
<p>Laravel は <code><meta name="csrf-token" content="..."></code> をレイアウトに置く慣習があります:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">function postWithCsrf(url, params) {
const token = document.querySelector('meta[name="csrf-token"]').content;
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
// Laravel の _token フィールド
const csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = '_token';
csrfInput.value = token;
form.appendChild(csrfInput);
for (const key in params) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = params[key];
form.appendChild(input);
}
document.body.appendChild(form);
form.submit();
}</code></pre>
<p>Rails は <code>authenticity_token</code> という名前、Django は <code>csrfmiddlewaretoken</code>。フレームワークごとに名前が違うので注意。</p>
<h2>PUT / DELETE / PATCH を送りたい</h2>
<p>HTML 標準の form は <code>GET</code> / <code>POST</code> しか送信できません。Laravel / Rails では <code>_method</code> hidden field を使った method spoofing が定番:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">function deleteForm(url) {
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
// Laravel の method spoofing
const methodInput = document.createElement('input');
methodInput.type = 'hidden';
methodInput.name = '_method';
methodInput.value = 'DELETE';
form.appendChild(methodInput);
// CSRF も忘れずに
const token = document.querySelector('meta[name="csrf-token"]').content;
const csrf = document.createElement('input');
csrf.type = 'hidden';
csrf.name = '_token';
csrf.value = token;
form.appendChild(csrf);
document.body.appendChild(form);
form.submit();
}</code></pre>
<h2>ファイルを送信したい(FormData)</h2>
<p>動的 form はバイナリを直接持てないため、<strong>fetch + FormData</strong> を使うのが現代的:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">async function uploadFile(url, file, extraParams = {}) {
const fd = new FormData();
fd.append('file', file);
for (const k in extraParams) fd.append(k, extraParams[k]);
const token = document.querySelector('meta[name="csrf-token"]').content;
const res = await fetch(url, {
method: 'POST',
headers: { 'X-CSRF-TOKEN': token },
body: fd,
});
return res.json();
}
// 利用例(input[type=file] から取得)
const fileInput = document.querySelector('#file');
fileInput.addEventListener('change', e => {
uploadFile('/upload', e.target.files[0], { user_id: 1 })
.then(json => console.log(json));
});</code></pre>
<h2>form.submit() と fetch() の使い分け</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>用途</th><th>form.submit()</th><th>fetch()</th></tr></thead>
<tbody>
<tr><td>ページ遷移</td><td>○ 起きる</td><td>× 起きない</td></tr>
<tr><td>サーバ応答を JS で受ける</td><td>×</td><td>○</td></tr>
<tr><td>ファイル送信</td><td>○ (multipart 設定要)</td><td>○ (FormData)</td></tr>
<tr><td>クロスオリジン</td><td>○ (リダイレクトは見れる)</td><td>○ (CORS 設定要)</td></tr>
<tr><td>決済ゲートウェイ POST</td><td><strong>○ 推奨</strong></td><td>× CORS で弾かれる</td></tr>
<tr><td>SPA 内部 API</td><td>×</td><td><strong>○ 推奨</strong></td></tr>
</tbody>
</table>
<h2>配列パラメータの送信</h2>
<p>PHP / Laravel は <code>name="tags[]"</code> 形式で配列を受け取ります:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">function appendArray(form, name, values) {
values.forEach(v => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name + '[]'; // tags[]
input.value = v;
form.appendChild(input);
});
}
const form = document.createElement('form');
form.method = 'POST';
form.action = '/search';
appendArray(form, 'tags', ['php', 'laravel', 'vue']);
document.body.appendChild(form);
form.submit();
// → POST /search tags[]=php&tags[]=laravel&tags[]=vue</code></pre>
<h2>よくある落とし穴</h2>
<ul>
<li><strong>form を appendChild しない</strong> → ブラウザによっては submit() が無視される</li>
<li><strong>input.value に objet を入れる</strong> → <code>[object Object]</code> の文字列になる。事前に <code>JSON.stringify</code></li>
<li><strong>改行を含む値</strong> → hidden input でも保持される。サーバ側で正しく受け取れているか確認</li>
<li><strong>CSRF 忘れ</strong> → 419 Page Expired / 422 になる</li>
<li><strong>同じ name を 2 つ</strong> → 後勝ち。配列にしたければ <code>name[]</code></li>
</ul>
<h2>FAQ</h2>
<p><strong>Q: GET でも同じやり方でできる?</strong><br />
A: できます。<code>form.method = "GET"</code> にするだけ。ただし GET は URL にパラメータが付くので、長すぎる値や秘匿情報は POST にすべき。</p>
<p><strong>Q: window.location.href で POST はできる?</strong><br />
A: できません。location.href は GET 専用。POST 遷移したい場合は form を動的生成するこの方法が唯一の手段です。</p>
<p><strong>Q: jQuery でも同じことできる?</strong><br />
A: <code>$("<form>").attr({method:"POST",action:url}).append(...).appendTo("body").submit()</code> で同じ。ただし現代は Vanilla で十分。</p>