Jak pokonać ograniczenie zagnieżdżania formularzy HTML?

Wiem, że XHTML nie obsługuje tagów zagnieżdżonych formularzy i czytałem już inne odpowiedzi na ten temat na Stack Overflow, ale nadal nie znalazłem eleganckiego rozwiązania problemu.

Niektórzy mówią, że tego nie potrzebujesz i że nie mogą wymyślić scenariusza, gdyby to było potrzebne. Cóż, osobiście nie mogę wymyślić scenariusza, że nie potrzebowałem go.

Spójrzmy na bardzo prosty przykład:

Tworzysz aplikację na blogu i masz formularz z polami do tworzenia nowego posta i pasek narzędzi z "akcjami", takimi jak" Zapisz"," Usuń","Anuluj".
<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>

Naszym celem jest napisanie formularza w sposób, który nie wymaga JavaScript , wystarczy zwykły stary formularz HTML i przyciski submit.

Ponieważ adres URL akcji jest zdefiniowany w tagu formularza, a nie w każdym pojedynczym przycisku Wyślij, naszą jedyną opcją jest wysłanie do ogólnego adresu URL, a następnie uruchomienie " if...więc...else " aby określić nazwę przycisku, który był / align = "left" / Nie bardzo elegancki, ale nasz jedyny wybór, ponieważ nie chcemy polegać na JavaScript.

Jedynym problemem jest to, że naciśnięcie "Delete" spowoduje wysłanie wszystkich pól formularza na serwerze, nawet jeśli jedyną rzeczą potrzebną do tej akcji jest ukryte wejście z POST-id. W tym małym przykładzie nic wielkiego, ale mam formularze z setkami (że tak powiem) pól i kart w moich LOB aplikacjach, które (ze względu na wymagania) muszą złożyć wszystko za jednym razem i w dowolnym sprawa wydaje się bardzo nieefektywne i marnotrawstwo. Jeśli zagnieżdżanie formularza było obsługiwane, byłbym w stanie przynajmniej zawinąć przycisk" Usuń " wyślij wewnątrz własnego formularza tylko z polem POST-id.

Możesz powiedzieć "po prostu zaimplementuj" Usuń "jako link zamiast wyślij". Byłoby to złe na tak wielu poziomach, ale co najważniejsze, ponieważ działania uboczne, takie jak" Usuń " tutaj, nigdy nie powinny być żądaniem GET.

Więc moje pytanie (szczególnie do tych, którzy twierdzą, że nie potrzebowali formy zagnieżdżanie) to czym się zajmujesz? Czy jest jakieś eleganckie rozwiązanie, którego mi brakuje lub najważniejsze jest naprawdę "wymagaj JavaScript lub Prześlij wszystko"?
Author: Peter Mortensen, 2009-02-28

16 answers

Zaimplementowałbym to dokładnie tak, jak opisałeś: Prześlij wszystko na serwer i zrób proste if / else, aby sprawdzić, który przycisk został kliknięty.

A następnie zaimplementowałbym wywołanie Javascript łączące się z zdarzeniem onsubmit formularza, które sprawdzałoby przed wysłaniem formularza i tylko wysyłało odpowiednie dane do serwera (ewentualnie przez drugi formularz na stronie z ID potrzebnym do przetworzenia rzeczy jako ukrytego wejścia, lub odświeżało lokalizację strony z potrzebnymi danymi przekazanymi przez serwer jako żądanie GET, lub zrobić Ajax post do serwera, lub...).

W ten sposób ludzie bez Javascript są w stanie korzystać z formularza tylko dobrze, ale obciążenie serwera jest przesunięty, ponieważ ludzie, którzy mają Javascript są tylko przesyłanie jednego kawałka danych potrzebnych. Coraz się koncentruje się tylko na wspieranie jednego lub drugiego naprawdę niepotrzebnie ogranicza swoje opcje.

Alternatywnie, jeśli pracujesz za firmowym firewallem lub czymś takim i każdy ma wyłączoną obsługę Javascript, możesz zrobić dwa formularze i popracować nad magią CSS, aby wyglądało to tak, jakby przycisk Usuń był częścią pierwszego formularza.

 53
Author: One Crayon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-28 06:24:03

Wiem, że to stare pytanie, ale HTML5 oferuje kilka nowych opcji.

Pierwszy polega na oddzieleniu formularza od paska narzędzi w znacznikach, dodaniu innego formularza dla akcji delete i powiązaniu przycisków na pasku narzędzi z odpowiednimi formularzami przy użyciu atrybutu 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>

Ta opcja jest dość elastyczna, ale w oryginalnym poście wspomniano również, że może być konieczne wykonywanie różnych czynności za pomocą jednego formularza. HTML5 przychodzi na ratunek, ponownie. Możesz użyć atrybut formaction na przyciskach submit, więc różne przyciski w tej samej formie mogą przesyłać do różnych adresów URL. Ten przykład po prostu dodaje metodę klonowania do paska narzędzi poza formularzem, ale działałaby ona tak samo zagnieżdżona w formularzu.

<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

Zaletą tych nowych funkcji jest to, że robią to wszystko deklaratywnie bez JavaScript. Wadą jest to, że nie są one obsługiwane na starsze przeglądarki, więc trzeba zrobić kilka polyfilling dla starszych przeglądarek.

 180
Author: shovavnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-05 01:53:07

Czy możesz mieć formularze obok siebie na stronie, ale nie zagnieżdżone. następnie użyj CSS, aby wszystkie przyciski ładnie się układały?

<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
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-09 07:58:04

HTML5 ma pojęcie "właściciel formularza" - atrybut "formularz" dla elementów wejściowych. Pozwala na emulację zagnieżdżonych formularzy i rozwiąże problem.

 13
Author: Nishi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 22:05:37

Trochę stary temat, ale ten może się komuś przydać:

Jak ktoś wyżej wspomniał - możesz użyć formularza atrapy. Musiałem rozwiązać ten problem jakiś czas temu. Na początku całkowicie zapomniałem o tym ograniczeniu HTML i po prostu dodałem zagnieżdżone formularze. Wynik był interesujący-straciłem pierwszą formę z zagnieżdżonego. Potem okazało się, że to jakiś "trik", aby po prostu dodać fałszywy formularz (który zostanie usunięty z przeglądarki) przed rzeczywistymi zagnieżdżonymi formularzami.

In my sprawa wygląda tak:

<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>

Działa dobrze z Chrome, Firefox i Safari. IE do 9 (nie jestem pewien co do 10) i Opera nie wykrywa parametrów w postaci głównej. Globalny $_REQUEST jest pusty, niezależnie od danych wejściowych. Formy wewnętrzne wydają się działać dobrze wszędzie.

Nie testowałem innej sugestii opisanej tutaj - fieldset wokół zagnieżdżonych formularzy.

EDIT : Frameset nie działa! Po prostu dodałem główny formularz po innych (koniec z zagnieżdżonymi formami) i użyłem jQuery "klon", aby zduplikować dane wejściowe w formularzu po kliknięciu przycisku. Dodano .hide () do każdego z klonowanych wejść, aby utrzymać układ bez zmian i teraz działa jak urok.

 11
Author: B.D.Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-23 14:49:19

Myślę, że Jason ma rację. Jeśli twoja akcja "usuń"jest minimalna, upewnij się, że będzie ona sama w formie i wyrównaj ją z innymi przyciskami, aby interfejs wyglądał jak jedna ujednolicona forma, nawet jeśli nie jest.

Lub, oczywiście, przeprojektować interfejs, i pozwolić ludziom usunąć gdzie indziej całkowicie, co nie wymaga, aby zobaczyć ogromną formę w ogóle.

 4
Author: AmbroseChapel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-28 22:44:29

Jednym ze sposobów, w jaki zrobiłbym to bez javascript, byłoby dodanie zestawu przycisków radiowych, które definiują akcję do wykonania:

  • Update
  • Usuń
  • Whatever

Wtedy skrypt akcji będzie wykonywał różne akcje w zależności od wartości ustawionego przycisku radiowego.

Innym sposobem byłoby umieszczenie dwóch formularzy na stronie, tak jak sugerowałeś, ale nie zagnieżdżonych. Układ może być trudny do kontrolowania:

<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>

Użycie ukrytego pola "zadanie" w skrypt obsługi do odpowiedniego rozgałęzienia.

 2
Author: Ron Savage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-27 16:09:43

Ta dyskusja wciąż mnie interesuje. Za oryginalnym postem są "wymagania", które OP wydaje się dzielić - tj. formularz z kompatybilnością wsteczną. Jako ktoś, kogo praca w momencie pisania musi czasami wspierać IE6 (i przez lata), to mi się podoba.

Bez forsowania frameworka (wszystkie organizacje będą chciały się uspokoić co do kompatybilności/solidności, a ja nie używam tej dyskusji jako uzasadnienia dla frameworka), Drupal rozwiązania tego problemu są interesujące. Drupal jest również bezpośrednio istotne, ponieważ framework miał długą politykę czasu "to powinno działać bez Javascript (tylko jeśli chcesz)" tzn. problem OP.

Drupal używa dość rozbudowanej formy .inc funkcje do znalezienia triggering_element (tak, to nazwa w kodzie). Patrz na dole kodu na stronie API dla form_builder (jeśli chcesz zagłębić się w szczegóły, zalecane jest źródło - drupal-x. xx / zawiera / formularz.inc ). Konstruktor używa automatycznego generowania atrybutów HTML i dzięki temu może po powrocie wykryć, który przycisk został naciśnięty ,i działać zgodnie z tym (można je skonfigurować do uruchamiania oddzielnych procesów).

Poza kreatorem formularzy, Drupal dzieli działania "usuwania" danych na oddzielne adresy URL/formularze, prawdopodobnie z powodów wymienionych w oryginalnym poście. To wymaga pewnego rodzaju wyszukiwania / aukcji krok (groan inny formularz! ale jest przyjazny dla użytkownika) jako wstęp. Ale ma to tę zaletę, że eliminuje problem "Prześlij wszystko". Duży formularz z danymi służy do jego zamierzonego celu, tworzenia/aktualizacji danych (lub nawet akcji "merge").

Innymi słowy, jednym ze sposobów obejścia problemu jest podzielenie formularza na dwa, a następnie problem znika(a metody HTML można również poprawić poprzez POST).

 1
Author: Rob Crowther,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-23 04:57:52

Użyj iframe dla zagnieżdżonego formularza. Jeśli chcą udostępnić pola, to... nie jest zagnieżdżona.

 0
Author: Dan Rosenstark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-05 00:21:35

Moim rozwiązaniem jest, aby przyciski wywoływały funkcje JS, które piszą, a następnie przesyłają formularze poza głównym formularzem

<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
Author: scotweb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-24 05:32:45

Jeśli naprawdę nie chcesz używać wielu formularzy( jak Jason sugests), użyj przycisków i narzędzi obsługi 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>

A następnie w javascript (używam jQuery tutaj dla łatwości) (chociaż jest to dość przesada dla dodawania niektórych obsługi onclick)

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

Również Wiem, to sprawi, że połowa strony nie będzie działać bez javascript.

 -1
Author: Pim Jager,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-28 10:23:33

W odpowiedzi na pytanie zamieszczone przez Yar w komentarzu do jego własnej odpowiedzi, przedstawiam trochę JavaScript, który zmieni rozmiar iframe. W przypadku przycisku formularza można bezpiecznie założyć, że ramka iframe będzie w tej samej domenie. To jest kod, którego używam. Będziesz musiał zmienić matematykę / stałe dla własnej strony:

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;
    }
}

Więc nazwij to tak:

<iframe ... frameborder="0" onload="if(window.parent && window.parent.resizeIFrame){window.parent.resizeIFrame(this);}"></iframe>
 -1
Author: Nicholas Shanks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-09 07:46:50

Właśnie wymyśliłem fajny sposób na zrobienie tego z 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
Author: mordy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-15 18:43:57

Cóż, jeśli przesyłasz formularz, przeglądarka wysyła również nazwę i wartość input submit. So what yo can do is

<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>

Więc po stronie serwera wystarczy poszukać parametru rozpoczynającego ciąg znaków "action:" a reszta podpowiada jaką akcję wykonać

Więc po kliknięciu na przycisk Zapisz przeglądarka wysyła coś w stylu foo=asd&action: save=Save

 -1
Author: Lauri Lüüs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-22 13:19:43

Obejrzałem problem, dodając pole wyboru w zależności od tego, jaką formę osoba chciała zrobić. Następnie użyj 1 przycisku, aby przesłać cały formularz.

 -1
Author: Gregws,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-23 05:58:46

Alternatywnie możesz przypisać formularz actiob w locie...może nie jest to najlepsze rozwiązanie, ale na pewno łagodzi logikę po stronie serwera...

<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>
 -2
Author: Qiniso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-27 13:46:17