0
Help us understand the problem. What are the problem?

posted at

[HTML, JavaScript]formaction動かない…

はじめに

「登録する」ボタンを持つ登録フォームに「プレビュー」機能を追加するときのお話。

プレビュー機能はPOST送信したいので、1form内で2つのsubmitボタンを置きたい、という形になる。

<form method="POST" action="./register">
    <input type="submit" value="登録する">
    // <input type="submit" value="プレビュー">  // 追加したい
</form>

少し前ではJSで遷移先を変更したり、サーバーにリクエスト送ってからsubmitされたinputのvalueの値で処理内容変えたりとかやりたいことのわりに結構面倒なことをしなくてはいけなかったらしい。

※参考:JSでの制御

単一フォーム に 複数サブミット を配置して 遷移先 を切り替える 方法

※参考:サーバーでの制御(PHP)

PHP - 複数のsubmitで処理を分ける

ただ月日は流れ、HTML5で導入されたformactionというHTMLタグを使えばものすごく簡単に遷移先を変更できるとのことでウッキウキで実装してみた。

: 入力欄 (フォーム入力) 要素 - HTML: HyperText Markup Language | MDN

↓実際の使い方

html5 で導入された button タグに付けれる formaction と formtarget 属性 を用いてプレビューを実現する - Qiita

環境

  • PHP 8.0.17
  • Laravel 8.75

formactionうごかなくね?

準備

実際にbladeファイルを以下のようにした

尚、Laravelを使っているので、いろんな補完機能を持つLaravelCollectiveによる実装になっています。

register.blade.php

@extends('layouts.app')

@section('content')
		{{ Form::open(['method' => 'POST', 'route' => 'register']) }}
		...
		...
		{{ Form::submit('登録する') }}
		{{ Form::submit('プレビュー', ['formaction' => '/preview']) }}  // 追加
		{{ Form::close() }}
@endsection

ここでは「登録する」と「プレビュー」の2つのボタンが表示されるだけ。

そして、プレビューボタンを押して./previewにアクセスすることを確認すると、なんだか思った通りに動かない…

検証ツールでリクエスト先を見てみると

リクエスト URL: http://.../register
リクエスト メソッド: POST

と、Form::openで指定しているmethodとactionが反映されていた。

なのでformactionが機能していないことになる。

原因

結論だけ言うとformactionがプロジェクト内のJavaScriptと競合して、formactionの機能がJavaScriptに打ち消されてしまったのが原因でした。

どういうことかというと、プロジェクト内でsubmit押した際のダブルクリック対応として、以下を導入していました。

Laravel 5で確認画面付き問い合わせフォームを作る - Qiita

resources\js\app.js
$('form :submit').click(function (event) {
  var TIMEOUT = 10000;
  var target  = event.target;
  var $form   = $(target).closest('form');
  var $submit = $form.find(':submit');

	...
	...
	...
	
	$form.submit();
});

これによりsubmit押した際にsubmitボタンにdisabled属性が付与されダブルクリックできない、という機能。

ここでformの操作をしていく中でformactionで指定した遷移先がformのactionで指定した遷移先に切り替わりhttp://.../registerへアクセスしてしまった、という流れでした。

対策

JSで打ち消してもらっては困るので、先ほどのダブルクリック対応内で遷移先を切り替える処理を追記

resources\js\app.js
$('form :submit').click(function (event) {
  var TIMEOUT = 10000;
  var target  = event.target;
  var $form   = $(target).closest('form');
  var $submit = $form.find(':submit');

	...

  // 追加: ダブルクリック防止対応により本来のformactionの機能を打ち消してしまうのでここで代入する
  if (target.name === 'preview') {
    $form.attr('action', target.formAction)
  }

	...
	
	$form.submit();
});

<input type="submit">のボタンがname=”preview”であればformactionで指定した遷移先でsubmitするように変更

register.blade.php

@extends('layouts.app')

@section('content')
		<form method="POST" action="./register">
		    <input type="submit" value="登録する">
		    <input type="submit" formaction="./preview" name="preview" value="プレビュー"> // 修正
		</form>
@endsection

プレビューボタンにname=”preview”を追加

これで無事プレビューボタンを押すと、./previrewへ遷移するようになりました!

ちなみに、LaravelCollectiveの書き方でも問題なく動作します。

register.blade.php

@extends('layouts.app')

@section('content')
		{{ Form::open(['method' => 'POST', 'route' => 'register']) }}
		...
		...
		{{ Form::submit('登録する') }}
		{{ Form::submit('プレビュー', ['formaction' => '/preview', 'name' => 'preview']) }}  // 修正
		{{ Form::close() }}
@endsection

備考:他の試したこと

formactionが動かなかった時にいくつか試してみたこと

LaravelCollective不使用

生の

タグを用いて実装

→挙動変わらず。

register.blade.php

@extends('layouts.app')

@section('content')
		<form method="POST" action="./register">
		    <input type="submit" value="登録する">
		    <input type="submit" formaction="./preview" value="プレビュー"> // 追加
		</form>
@endsection

submit毎にformactionを指定

→挙動変わらず

register.blade.php

@extends('layouts.app')

@section('content')
		<form method="POST" action="">
		    <input type="submit" formaction="./register" value="登録する">
		    <input type="submit" formaction="./preview" value="プレビュー"> // 追加
		</form>
@endsection

HTMLディレクティブを削除

→これでやっとformactionが動くことを確認

ただ、Laravelでは必須のHTMLディレクティブとの併用がいけないのか確認したかった

使えないとなるとformactionが実用的じゃなくなる。。。

<form method="POST" action="">
    <input type="submit" formaction="./register" value="登録する">
    <input type="submit" formaction="./preview" value="プレビュー"> // 追加
</form>

プロジェクト以外のBlade内で実装

自分の学習用のLaravelで確認(バージョンは9.系)

→こちらは問題なく動くことを確認。HTMLディレクティブの使用がformactionが機能しない原因じゃなくてよかった、、、

@extends('layouts.app')

@section('content')
<form method="POST" action="">
	<input type="submit" formaction="./register" value="登録する">
	<input type="submit" formaction="./preview" value="プレビュー"> // 追加
</form>
@endsection

ココまでくるとプロジェクト内特有のソースコードが原因で機能していない、というところまでたどり着き今回の原因特定に至ったわけです。

終わりに

  • formaction以外にも、送信方式を変更するformmethod、target属性を指定できるformtargetがある

: 入力欄 (フォーム入力) 要素 - HTML: HyperText Markup Language | MDN

  • 上記は以下でのみ使える
      1. <input type="submit">
      1. <input type="image">
    • 3.<button>
  • formにtarget付与できるの初めて知った。target=”_blank”が持つ脆弱性対策のrel属性も付与できる

最後まで読んでいただきありがとうございました。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?