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


199

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アプリケーションには何百もの(いわば)フィールドとタブがあり、(要件のため)一度にすべてを送信する必要があり、いずれにしてもこれは非常に非効率的ですそして廃棄物。フォームのネストがサポートされている場合、少なくとも「削除」送信ボタンを、フォームの内部にpost-idフィールドのみでラップできます。

「送信ではなく、リンクとして「削除」を実装するだけ」と言うことができます。これは多くのレベルで間違っていますが、最も重要なのは、ここでの「削除」のような副作用アクションは決してGETリクエストであってはならないためです。

だから私の質問(特に、フォームの入れ子は必要ないと言っている人たちへの質問)は、あなたは何をしますか?私が見逃しているエレガントな解決策はありますか、それとも最終的には「JavaScriptが必要か、すべてを送信するか」です。


17
おもしろいですが、XHTMLは興味深いメモanderwald.info/internet/nesting-form-tags-in-xhtmlに従って間接的なフォームのネストを許可します-(X)HTMLは「フォーム>フォーム」のようなフォームのネストを許可しませんが、「フォーム> フィールドセット >フォーム」、W3バリデーターはそれが有効であると述べていますが、ブラウザーにはそのようなネストに関するバグがあります。
西


:西さんのコメントリンク@用linkrot修正web.archive.org/web/20170420110433/http://anderwald.info/...
アルバート・

回答:


53

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

次に、フォームが送信される前にチェックするフォームのonsubmitイベントに関連付けられたJavascript呼び出しを実装し、関連データのみをサーバーに送信します(おそらく、処理を実行するために必要なIDを持つページの2番目のフォームを介して)非表示の入力、またはGETリクエストとして渡す必要のあるデータでページの場所を更新するか、サーバーにAjaxポストを実行する、または...)

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

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


15
オリジナルのOPはJavaScriptを明記していませんでした
Jason、

26
この方法の問題は、RESTfulでないことです。保存と削除は、リソースのさまざまなアクションに対応します:「保存」-> POST / my_resource(新しいリソースの作成)「保存」-> PUT / my_resource(既存のリソースの変更)「削除」-> DELETE / my_resource(破棄)リソース)RESTfulに言えば、問題はPOSTが新しいリソースを作成することが期待されていることです。確かに、既存のリソースを削除してはなりません。
user132447

178

これは古い質問であることは承知していますが、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なしでこれらすべてを宣言的に行うことです。欠点は、古いブラウザではサポートされていないため、古いブラウザではポリフィリングを行う必要があることです。


4
+ form=1-しかし、ブラウザのサポートが何であるかを知るのは素晴らしいことです-CanIUseは実際には言っていません。caniuse.com/#feat=forms
AKX

1
これはすばらしいですが、現在のところ、IEでサポートされていない機能は、まだ「プロダクション」での使用には適していません
Jako

3
<input>ボタンの宣言に使用することは悪い習慣です。この<button>要素は、この目的のために15年前に導入されました。本当に人。
ニコラスシャンクス

27
あなたの主張は不必要に論争であり、OPの質問とは無関係です。OPはJavaScriptを使用せずにフォームの入れ子の制限について質問し、答えはソリューションを提供します。ボタン<input><button>ツールバーは通常、ツールバーの方が適切ですが、使用するか要素かは関係ありません。ちなみに、ボタン要素は15年間すべてのブラウザでサポートされていません。
shovavnik 2012

6
@AKX古いステートメントですが、この機能のサポートはhtml5test.com/compare/feature/form-association-form.htmlで確認できます。IEは無効であり、他のほとんどのデスクトップブラウザーは許容できるようです。
脂肪

16

ページ上にフォームを並べて配置できますか?次に、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>

5
はい、それはあまりにもハックに感じられます。さらに、非常に大きなページ(複数のスコープレベルでタブとサブタブ、ネストされた送信ボタンを想定)では、要素の画面位置をドキュメント内の位置から完全に分離することはほとんど不可能です。
gCoder 2009

3
まあ、それは常にあなたがしたいこととあなたができることの間のバランスです。それらはオプションのようです-サーバーで処理される1つのフォーム、または保守が困難になる1つの複数のフォーム-賢明に選択してください:)
Jason

3
はい、残念ながらhtmlのこれらすべての制限があるので、サポートされているものに固執してフォーム全体をサーバーに送信します。その後、フォームの送信をインターセプトし、本当に必要なものだけを送信するために、サポートするクライアントのために控えめにJavaScriptを使用します。それが最善の妥協案です。
gCoder 2009

13

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


1
リンクする価値があるこれについての参照はありますか?
ダカブ2016年

w3.org/TR/html5/forms.html#form-ownerを参照してください「フォームに関連付けられた要素は、デフォルトでは、最も近い祖先のフォーム要素に関連付けられていますが、再関連付け可能な場合は、オーバーライドするように指定されたフォーム属性がある場合があります。この。"
西

11

古いトピックのようなものですが、これは誰かにとって役立つかもしれません:

上記の誰かが述べたように-ダミーフォームを使用できます。私はこの問題を少し前に克服しなければなりませんでした。最初、私はこの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グローバルは、入力に関係なく空です。インナーフォームはどこでも問題なく動作するようです。

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

編集:フレームセットが機能しませんでした!Mainフォームを他のフォームの後に追加し(ネストされたフォームは不要)、jQueryの「クローン」を使用して、ボタンクリック時にフォームの入力を複製しました。クローンされた各入力に.hide()を追加して、レイアウトを変更しないようにして、魅力的に機能するようにしました。


これは完璧に機能しました。私はasp.netページで作業していました。これには、すべてを囲むフォームが含まれていました。jQuery Validationプラグイン(github.com/jzaefferer/jquery-validation)に使用する内部の入れ子になったフォームがあり、FireFoxとIEでは完全に機能しましたが、Chromeで 'TypeError:メソッド'要素 'を呼び出せませんで失敗しました未定義'。ChromeがjQuery.html()を使用して追加されたhtmlのブロックから内部フォームを削除したため、内部フォームが見つからなかったため、validate()初期化呼び出しが失敗していることがわかりました。小さなダミーフォームを追加すると、最初にChromeが実際のフォームを離れます。
ジョン-番号ではない

これはもう機能しないようです。Firefoxは2番目の<form>タグを取り除き、最初の</ form>タグでフォームを終了しました。これにより、ページの下部に無効な</ form>タグが残り、フォームは正しく送信されません。:(
ターキン

私にとって非常に便利です。フォームを含むWebウィジェットをasp.netページに追加しています。Webフォームに驚かされ、常にHTML本体のすべてを囲むフォームが生成されます。囲んでいるフォームは、Webウィジェットの頭痛の種になります。ダミーフォームは、この状況に対して迅速かつ効果的な解決策のようです。これまでのところ、ChromeとSafariで完全に動作します。Firefoxはまだテストしていません。共有してくれてありがとう、私は多くの時間を節約できました!
JMヤン

4

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

または、もちろん、インターフェースを再設計し、他の場所を完全に削除して、enormoフォームを表示する必要がないようにします。


2

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 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" />
</フォーム>

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


1

この議論はまだ私にとって興味深いものです。オリジナルの投稿の背後には、OPが共有しているように見える「要件」があります。つまり、下位互換性のあるフォームです。執筆時の仕事でIE6をサポートする必要がある場合(および今後数年間)、私はそれを掘り下げます。

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

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

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

つまり、問題を回避する1つの方法は、フォームを2つに展開することです。その後、問題は消えます(HTMLメソッドはPOSTでも修正できます)。


0

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


1
iframeの使用は素晴らしいアイデアです。ほとんどの場合、JavaScriptを使用してサイズを変更する必要があるのは残念です(ユーザーのテキストサイズが何であるか、したがってボタンの大きさがわからないため)。
ニコラスシャンクス2011

@ Nicholas、Javascriptを使用してサイズを変更するにはどうすればよいですか?iframed HTMLは、同じドメイン上にない限り、iframeとやり取りできません。逆も同様です。
Dan Rosenstark、2011

@Nicholas、ありがとう、同じドメインに
属し

0

私の解決策は、ボタンにメインのフォームでフォームを書き出して送信する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>

-1

本当に複数のフォームを使用したくない場合(Jasonが推奨)、ボタンとonclickハンドラーを使用します。

<form id='form' name='form' action='path/to/add/edit/blog' method='post'>
    <textarea name='message' id='message'>Blog message here</textarea>
    <input type='submit' id='save' value='Save'>
</form>
<button id='delete'>Delete</button>
<button id='cancel'>Cancel</button>

そして、JavaScriptで(簡単にするためにここではjQueryを使用しています)(onclickハンドラーを追加するのはかなりやりすぎですが)

$('#delete').click( function() {
   document.location = 'path/to/delete/post/id'; 
});
$('#cancel').click( function() {
   document.location = '/home/index';
});

また、私は知っています。これにより、ページの半分がJavaScriptなしでは機能しなくなります。


1
私が見落としているのでない限り、これは削除アクションにリンクすることと同じです。最終結果は、削除アクションへのGETリクエストになります(悪い)。実はそれは少し粗雑です。なぜなら、プレーンな古いリンクタグができることを実行するためにJavaScriptが必要だからです。
カイルフォックス

-1

Yarが自分の回答へのコメントで投稿した質問に応えて、iframeのサイズを変更するJavaScriptをいくつか紹介します。フォームボタンの場合、iframeが同じドメイン上にあると想定しても安全です。これは私が使用するコードです。あなたは自分のサイトの数学/定数を変更する必要があります:

function resizeIFrame(frame)
{
    try {
        innerDoc = ('contentDocument' in frame) ? frame.contentDocument : frame.contentWindow.document;
        if('style' in frame) {
            frame.style.width = Math.min(755, Math.ceil(innerDoc.body.scrollWidth)) + 'px';
            frame.style.height = Math.ceil(innerDoc.body.scrollHeight) + 'px';
        } else {
            frame.width = Math.ceil(innerDoc.body.scrollWidth);
            frame.height = Math.ceil(innerDoc.body.scrollHeight);
        }
    } catch(err) {
        window.status = err.message;
    }
}

次に、次のように呼び出します。

<iframe ... frameborder="0" onload="if(window.parent && window.parent.resizeIFrame){window.parent.resizeIFrame(this);}"></iframe>

-1

私はjqueryでそれを行うための素晴らしい方法を思いついたばかりです。

<form name="mainform">    
    <div id="placeholder">
    <div>
</form>

<form id="nested_form" style="position:absolute">
</form>

<script>
   $(document).ready(function(){
      pos = $('#placeholder').position();
      $('#nested_form')
         .css('left', pos.left.toFixed(0)+'px')
         .css('top', pos.top.toFixed(0)+'px');
   });
</script>

-1

フォームを送信すると、ブラウザは入力送信の名前と値も送信します。だからあなたができることは

<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のようなものを送信します


-1

私はその人がどんな形をしたいのかに応じてチェックボックスを含めることで問題を回避しました。次に、1つのボタンを使用してフォーム全体を送信しました。


2
Stack Overflowへようこそ。これが問題を解決する方法を説明することは、単にあなたが何をしたかを言うよりも良いでしょう。Stack Overflowで優れた回答を作成するためのヒントについては、回答方法をご覧ください。ありがとうございました!
Qantas 94 Heavy

-2

別の方法として、その場でフォームactiobを割り当てることもできます...最善の解決策ではないかもしれませんが、サーバー側のロジックは確実に緩和されます...

<form name="frm" method="post">  
    <input type="submit" value="One" onclick="javascript:this.form.action='1.htm'" />  
    <input type="submit" value="Two" onclick="javascript:this.form.action='2.htm'" />  
</form>

どうして?どのボタンがクリックされたかを簡単に取得する方法がないので、私は真剣に尋ねています。jQueryのclick()メソッドのようなものを使用してボタンのonclickハンドラーを設定する必要があるとおっしゃっていますか?または、フォームが送信される前にクリックイベントを処理することが問題ですか?
ザックモリス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.