web-dev-qa-db-ja.com

HTMLフォームのネストの制限をどのように克服しますか?

XHTMLはネストされたフォームタグをサポートしていないことを知っており、この主題に関するStack Overflowで既に他の回答を読んだことがありますが、この問題に対するエレガントな解決策はまだわかりません。

あなたはそれを必要とせず、これが必要になるシナリオを考えられないと言う人もいます。まあ、個人的には、私が必要としないというシナリオは考えられません。

非常に簡単な例を見てみましょう:

ブログアプリを作成していて、新しい投稿を作成するためのいくつかのフィールドがあるフォームと、「保存」、「削除」、「キャンセル」などの「アクション」を持つツールバーがあります。

<form
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"
method="post">

    <input type="text" name="foo" /> <!-- several of those here -->
    <div id="toolbar">
        <input type="submit" name="save" value="Save" />
        <input type="submit" name="delete" value="Delete" />
        <a href="/home/index">Cancel</a>
    </div>
</form>

私たちの目的は、JavaScriptを必要としない方法でフォームを作成することです。単純な古いHTMLフォームと送信ボタンだけです。

アクションURLは個々の送信ボタンではなくFormタグで定義されているため、唯一のオプションは汎用URLに投稿し、「if ... then ... else」を開始してボタンの名前を決定することです提出されました。 JavaScriptに依存したくないので、あまりエレガントではありませんが、唯一の選択肢です。

唯一の問題は、[削除]を押すと、このアクションに必要なのはpost-idの非表示入力だけであっても、サーバー上のすべてのフォームフィールドを送信することです。この小さな例ではそれほど大したことではありませんが、 LOB (要件のため)すべてを1つで送信しなければならないアプリケーションに、何百もの(いわば)フィールドとタブがあるフォームがあります。いずれにしても、これは非常に非効率的で無駄に思えます。フォームのネストがサポートされている場合は、少なくとも「削除」送信ボタンをpost-idフィールドのみを持つ独自のフォーム内にラップすることができます。

「送信する代わりに、リンクとして「削除」を実装するだけです」と言うことができます。これは非常に多くのレベルで間違っていますが、最も重要なことは、ここでの「削除」などの副作用アクションは決してGET要求ではないためです。

私の質問(特にフォームのネストは必要ないと言う人への質問)は何をしますか?私が見逃しているエレガントなソリューションはありますか、最終的には「JavaScriptが必要かすべてを送信する」です?

196
gCoder

私はこれをあなたが説明したとおりに実装します。すべてをサーバーに送信し、単純なif/elseを実行して、クリックされたボタンを確認します。

そして、フォームの送信前に確認するフォームのonsubmitイベントに結びつけるJavascriptコールを実装し、サーバーに関連データのみを送信します(おそらく、物事を処理するために必要なIDを持つページ上の2番目のフォームを通じて)非表示の入力、またはGETリクエストとして渡される必要があるデータでページの場所を更新するか、サーバーへのAjax投稿を行う、または...)。

このように、Javascriptを持たない人でもフォームを問題なく使用できますが、Javascriptを持っている人は必要なデータを1つだけ送信するため、サーバーの負荷は相殺されます。どちらか一方のみをサポートすることに集中することは、オプションを不必要に制限します。

あるいは、企業のファイアウォールなどの背後で作業していて、誰もがJavaScriptが無効になっている場合、2つのフォームを実行し、CSSマジックを実行して、削除ボタンが最初のフォームの一部のように見えるようにすることができます。

53
One Crayon

これは古い質問ですが、HTML5にはいくつかの新しいオプションがあります。

1つ目は、フォームをマークアップのツールバーから分離し、削除アクション用に別のフォームを追加し、form属性を使用してツールバーのボタンをそれぞれのフォームに関連付けることです。

<form id="saveForm" action="/post/dispatch/save" method="post">
    <input type="text" name="foo" /> <!-- several of those here -->  
</form>
<form id="deleteForm" action="/post/dispatch/delete" method="post">
    <input type="hidden" value="some_id" />
</form>
<div id="toolbar">
    <input type="submit" name="save" value="Save" form="saveForm" />
    <input type="submit" name="delete" value="Delete" form="deleteForm" />
    <a href="/home/index">Cancel</a>
</div>

このオプションは非常に柔軟ですが、元の投稿では、単一のフォームで異なるアクションを実行する必要があるかもしれないと述べています。 HTML5が再び役立ちます。送信ボタンでformaction属性を使用できるため、同じフォーム内の異なるボタンを異なるURLに送信できます。この例では、フォームの外側のツールバーにcloneメソッドを追加するだけですが、フォーム内で同じ入れ子になります。

<div id="toolbar">
    <input type="submit" name="clone" value="Clone" form="saveForm"
           formaction="/post/dispatch/clone" />
</div>

http://www.whatwg.org/specs/web-apps/current-work/#attributes-for-form-submission

これらの新機能の利点は、JavaScriptなしでこれらすべてを宣言的に行うことです。欠点は、古いブラウザではサポートされていないことです。そのため、古いブラウザではポリフィルを行う必要があります。

175
shovavnik

フォームをページ上に並べることはできますが、ネストすることはできませんか。次に、CSSを使用してすべてのボタンをきれいに並べますか?

<form method="post" action="delete_processing_page">
   <input type="hidden" name="id" value="foo" />
   <input type="submit" value="delete" class="css_makes_me_pretty" />
</form>

<form method="post" action="add_edit_processing_page">
   <input type="text" name="foo1"  />
   <input type="text" name="foo2"  />
   <input type="text" name="foo3"  />
   ...
   <input type="submit" value="post/edit" class="css_makes_me_pretty" />
</form>
16
Jason

HTML5には、「フォーム所有者」、つまり入力要素の「フォーム」属性の概念があります。ネストされたフォームをエミュレートすることができ、問題を解決します。

13
Nishi

ちょっと古いトピックですが、これは誰かに役立つかもしれません:

上記の誰かが言ったように、ダミーのフォームを使用できます。しばらく前にこの問題を克服しなければなりませんでした。最初は、このHTML制限を完全に忘れて、ネストされたフォームを追加しました。結果は面白かった-ネストから最初のフォームを失った。その後、実際のネストされたフォームの前にダミーのフォーム(ブラウザーから削除されます)を単純に追加する何らかの「トリック」であることが判明しました。

私の場合、次のようになります。

<form id="Main">
  <form></form> <!--this is the dummy one-->
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  ......
</form>

Chrome、FireFox、Safariで正常に動作します。 IE最大9(10については不明)およびOperaは、メインフォームのパラメーターを検出しません。入力に関係なく、$ _ REQUESTグローバルは空です。内部フォームはどこでも問題なく機能するようです。

ここで説明されている別の提案、ネストされたフォームの周りのフィールドセットをテストしていません。

EDIT:フレームセットが機能しませんでした!メインフォームを他のフォームの後に追加し(ネストされたフォームはもうありません)、jQueryの「クローン」を使用して、ボタンクリック時にフォームの入力を複製しました。クローン化された各入力に.hide()を追加してレイアウトを変更せずに維持し、現在ではチャームのように機能するようになりました。

11
B.D.Joe

ジェイソンは正しいと思う。 「削除」アクションが最小限のものである場合は、それを単独でフォームにし、他のボタンと並べて、インターフェースが1つの統一されたフォームのように見えるようにします。

または、もちろん、インターフェイスを再設計し、他の場所からユーザーが完全に削除できるようにします。これにより、enormo-formをまったく見る必要がなくなります。

4
AmbroseChapel

Javascriptなしでこれを行う1つの方法は、実行するアクションを定義するラジオボタンのセットを追加することです。

  • 更新
  • 削除する
  • なんでも

次に、アクションスクリプトは、ラジオボタンセットの値に応じて異なるアクションを実行します。

別の方法は、あなたが提案したように、ネストされていない2つのフォームをページに置くことです。ただし、レイアウトを制御するのは難しい場合があります。

 <form name = "editform" action = "the_action_url" method = "post"> 
 <input type = "hidden" name = "task" value = "update" />
 <input type = "text" name = "foo" />
 <input type = "submit" name = "save" value = "Save" />
</ form> 
 
 <form name = "delform" action = "the_action_url" method = "post"> 
 <input type = "hidden" name = "task" value = "delete"/> 
 <input type = "hidden" name = "post_id" value = "5" />
 <input type = "submit" name = "delete" value = "Delete" /> 
 </ form> 

処理スクリプトの非表示の「タスク」フィールドを使用して、適切に分岐します。

2
Ron Savage

この議論は今でも私にとって興味深いものです。元の投稿の背後には、OPが共有しているように見える「要件」、つまり下位互換性のあるフォームがあります。執筆時点での仕事がIE6に戻ってサポートされなければならない(そして今後数年間)誰かとして、私はそれを掘ります。

フレームワークを推し進めることなく(すべての組織は互換性/堅牢性を安心させたいと考えています。フレームワークの正当性としてこの議論を使用していません)、この問題に対するDrupalソリューションは興味深いものです。 Drupalも直接関係します。これは、フレームワークが「Javascriptなしで(必要な場合のみ)動作するはずです」、つまりOPの問題という長年のポリシーがあるためです。

Drupalは、triggering_element(はい、それはコードの名前です)を見つけるために、かなり広範なform.inc関数を使用します。 form_builder のAPIページにリストされているコードの下部を参照してください(詳細を掘り下げたい場合は、ソースをお勧めします-drupal-x.xx/includes/form.inc)。ビルダーは自動HTML属性生成を使用し、それにより、どのボタンが押されたかを検出して、それに応じて動作することができます(これらは個別のプロセスを実行するように設定することもできます)。

フォームビルダー以外に、Drupalはデータの「削除」アクションを個別のURL /フォームに分割します。これは、おそらく元の投稿で述べた理由によります。これには、予備的なものとして、ある種の検索/一覧表示ステップ(groan別のフォームが必要ですが、ユーザーフレンドリーです)が必要です。ただし、これには「すべてを送信する」問題を排除できるという利点があります。データのある大きなフォームは、データの作成/更新(または「マージ」アクション)の目的に使用されます。

言い換えると、問題を回避する1つの方法は、フォームを2つに展開し、問題がなくなることです(HTMLメソッドもPOSTで修正できます)。

1
Rob Crowther

ネストされたフォームにはiframeを使用します。フィールドを共有する必要がある場合は、...ネストされていません。

1
Dan Rosenstark

私の解決策は、ボタンにJS関数を呼び出して、メインフォームでフォームを作成して送信することです

<head>
<script>
function removeMe(A, B){
        document.write('<form name="removeForm" method="POST" action="Delete.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.removeForm.submit();
}
function insertMe(A, B){
        document.write('<form name="insertForm" method="POST" action="Insert.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.insertForm.submit();
}
</script>
</head>

<body>
<form method="POST" action="main_form_purpose_page.php">

<input type="button" name="remove" Value="Remove" onclick="removeMe('$customerID','$productID')">
<input type="button" name="insert" Value="Insert" onclick="insertMe('$customerID','$productID')">

<input type="submit" name="submit" value="Submit">
</form>
</body>
0
scotweb

さて、フォームを送信すると、ブラウザは入力送信名と値も送信します。だからできることは

<form   
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"  
method="post">  

    <input type="text" name="foo" /> <!-- several of those here -->  
    <div id="toolbar">
        <input type="submit" name="action:save" value="Save" />
        <input type="submit" name="action:delete" value="Delete" />
        <input type="submit" name="action:cancel" value="Cancel" />
    </div>
</form>

サーバー側では、幅の文字列「action:」で始まるパラメーターを探すだけで、残りの部分は実行するアクションを示します

そのため、[保存]ボタンをクリックすると、foo = asd&action:save = Saveのようなものが送信されます。

0
Lauri Lüüs