見出し画像

JavaScriptでフォームデータを全て取得してボタンのデータも付けてPost送信

よくあるはずだけど検索に引っかからず

ウェブフォームに入力中にEnterキーを押したら思っていなかったタイミングで思っていなかったボタンがSubmit!!
英文字入力用に開発がメインのウェブ世界、Enterキーで漢字変換などを確定するなんていう作業は想定外なんでしょうね。
ネット上にはボタン一個でフォームの中身だけを非同期で送るみたいのはたくさん見つかるのですが、ボタンたくさんで、というのが見当たらず思い付かずだったので、これの回避方法の備忘録。
目次を読んでもらえるとほぼ内容把握できます。


ChatGPT様に聞いてみた

ここはやはりまずは聞いてみようよChatGPT様に!

通常のformのsubmitと同じ動作をさせることは可能ですか?

ChatGPT
はい、fetchを使用して非同期にデータを送信した後、通常のフォームのsubmitと同じ動作、つまり送信成功後に受け取り側のページに移動させることは可能です。しかし、fetch自体は非同期通信を行い、ページ遷移せずにサーバーとデータを交換するため、明示的にページ遷移を指示する必要があります。

ChatGPT4の回答
function submitFormWithFetch() {
  const form = document.getElementById('myForm'); // フォームのIDを取得
  const formData = new FormData(form);

  fetch(form.action, {
    method: 'POST',
    body: formData
  })
  .then(response => {
    if (response.ok) {
      return response.text(); // またはJSONなど、必要に応じて
    } else {
      throw new Error('送信に失敗しました。');
    }
  })
  .then(data => {
    // ここでページ遷移を行う
    window.location.href = 'success_page.html'; // 遷移先のURLを指定
  })
  .catch(error => {
    console.error('エラー:', error);
  });
}

うーん、それだとフォーム送ってそれを次のページで受け取ることができないじゃない。

javascript で formの同期送信はできますか?

ChatGPT
はい、JavaScriptを使用してフォームを同期送信することは可能です。ただし、JavaScriptで明示的に同期送信を行う場合は、主にフォームのsubmitイベントをプログラム的にトリガーすることにより行われます。この方法は、フォームのデータを非同期で送信するfetchやXMLHttpRequestとは異なり、ページ遷移を伴う通常のフォーム送信の挙動を模倣します。
以下に、JavaScriptを使用してフォームの同期送信を行う基本的な例を示します。

ChatGPT4の回答
function submitForm() {
  // フォーム要素を取得
  var form = document.getElementById('myForm');

  // フォームのsubmitイベントをプログラム的にトリガーする
  form.submit();
}

うーん、模倣する、ということはまねするということだからできそうだけどちゃんとはできないってこと? これをこのまま送信するとフォームタグを含めた状態で送られてしまうから先方でまた解体作業して組み立ててと面倒な話。

普通にbutton type="submit"と同じ挙動にしたいのです。
ま、ともかく作っていきましょう。

ページの中にボタンがいくつもある状態を

ページの中にあれやこれやのボタンが設置してあるのです。例えば個人情報の書き換えのページで、パスワードの書き換えは別なページで処理したいからそっち行きのボタンもそこにある、みたいな。
そしてどのボタンが押されたのかを判明したい値がそのボタンのvalueに書いてあります。

JavaScriptで制御します

まずはtype="submit" にしてあるボタンを全て type="button"にします。時々<form>にonSubmit="return false;"を入れるというのを見かけますが、typeを全てbuttonにしてあればこの必要はないです。だって、Submitボタンが無いですからその指示が発火することないですし。。。

<form method="post" id="myForm">

--ここにいろいろフォーム内容--

<button type="button" name="act" value="send"> 送る </button>
<button type="button" name="act" value="other"> フォーム以外のこと </button>
</form>

ボタンの中にonClick="関数名()";を入れて

この関数名、フォームのSubmitの関数をするのだから onClick="submit();" にしちゃえ、は絶対ダメです。なぜなら、submit()というのはすでに挙動が決まっている関数。そしてその挙動は<<<submit>>>>そのもの!!
せっかくtype="button"にしたのにJavaScriptでsubmit()しかも何にも引き連れて行かない(=フォームデータを送らない)submitボタンの出来上がり、なんて悲しすぎる結末に。。。
ここはひとつsendform()という関数名を付けて進めてみましょう。

<button type="button" name="act" value="send" onClick="sendform();"> 送る </button>
<button type="button" name="act" value="other" onClick="sendform();"> フォーム以外のこと </button>
function sendform(){
}

関数の中身を書いていきます

作ったフォームの名前jsfrmはJavaScript formから考えたもの。いつも思うのだけれど、formとfromは似てるな、と。。。frmと略すとどっちかわからない。。。

function sendform(){
  var jsfrm = document.createElement("form");
  jsfrm.setAttribute("action", "ここは移動先のURLを入れます 絶対でも相対でも大丈夫");
  jsfrm.setAttribute("method", "post");
  jsfrm.style.display = "none";
  document.body.appendChild(jsfrm);
}

input = 'hidden"にするけど念の為にdisplay = "none"

 jsfrm.style.display = "none";

も入れました。

どのボタンをクリックしたか判明させるために(this.value)

今回の一番の目的はどのボタンをクリックしたのかを判明すること。なので、ボタンに書いたonClick  = sendform()の括弧の中にthis.valueと記入。

<button type="button" name="act" value="send" onClick="sendform(this.value);"> 送る </button>
<button type="button" name="act" value="other" onClick="sendform(this.value);"> フォーム以外のこと </button>

受け取る関数にも function sendform(n){

受け取り側にも代入のための文字を適当に入れます。今回はnを使用しよう。

function sendform(n){
  var jsfrm = document.createElement("form");
  jsfrm.setAttribute("action", "ここは移動先のURLを入れます 絶対でも相対でも大丈夫");
  jsfrm.setAttribute("method", "post");
  jsfrm.style.display = "none";
  document.body.appendChild(jsfrm);
}

ボタンのvalueをjsformに加えます

関数の中にちょっとずつ足してフォームを完成させていきます。

  var input = document.createElement('input');
  input.setAttribute('type', 'hidden');
  input.setAttribute('name', 'act'); //もともとのボタンに付いていたボタンの名前
  input.setAttribute('value', n); //ここでボタンから送られてきたthis.valueの値を入れます
  jsfrm.appendChild(input);

フォームの中身を全て取得

ボタンデータだけではなく、フォームの中身も手に入れます。

const data = new FormData(myForm);

for文で回します

取得したフォーム、記入欄が一つならforは不要ですが、まずそんなことはないでしょう。すべてをjsfrmに格納していきます。

  for (let entry of data) {
    let input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', entry[0]);
    input.setAttribute('value', entry[1]);
    jsfrm.appendChild(input);
  }

完成です

できました。JavaScriptに精通されている方ならなんてことはないでしょうし、勘の良い方ならここまでの記事で実装できてしまうでしょう。でもここは敢えて完成形の表示は100円とさせていただきます。だって私の5時間くらいの作業、それをたったの100円!! 応援感覚で投げ銭して(買って)ください。

しかし、<input type="text">がひとつだけだとEnterでまだ何かをSubmitしているみたい

これについての強制回避も以下の部分に書きました。

ここから先は

1,740字

¥ 100

この記事が気に入ったらチップで応援してみませんか?