Come superare la limitazione della nidificazione dei moduli HTML?


199

So che XHTML non supporta tag di moduli nidificati e ho già letto altre risposte qui su Stack Overflow in merito a questo argomento, ma non ho ancora trovato una soluzione elegante al problema.

Alcuni dicono che non ne hai bisogno e che non riescono a pensare a uno scenario se questo fosse necessario. Beh, personalmente non riesco a pensare a uno scenario in cui non ne ho avuto bisogno.

Vediamo un esempio molto semplice:

Stai creando un'app blog e hai un modulo con alcuni campi per la creazione di un nuovo post e una barra degli strumenti con "azioni" come "Salva", "Elimina", "Annulla".

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

Il nostro obiettivo è scrivere il modulo in un modo che non richiede JavaScript , solo un semplice vecchio modulo HTML e inviare pulsanti.

Poiché l'URL dell'azione è definito nel tag Modulo e non in ogni singolo pulsante di invio, la nostra unica opzione è quella di pubblicare su un URL generico e quindi iniziare "se ... quindi ... altro" per determinare il nome del pulsante che è stato inviato. Non molto elegante, ma la nostra unica scelta, dal momento che non vogliamo fare affidamento su JavaScript.

L'unico problema è che premendo "Elimina", verranno inviati TUTTI i campi del modulo sul server anche se l'unica cosa necessaria per questa azione è un input nascosto con il post-id. Non è un grosso problema in questo piccolo esempio, ma ho moduli con centinaia (per così dire) di campi e schede nelle mie applicazioni LOB che (a causa dei requisiti) devono presentare tutto in una volta e in ogni caso questo sembra molto inefficiente e uno spreco. Se l'annidamento dei moduli fosse supportato, sarei almeno in grado di racchiudere il pulsante di invio "Elimina" all'interno del suo modulo con solo il campo post-ID.

Si può dire "Basta implementare" Elimina "come collegamento anziché inviare". Ciò sarebbe sbagliato in così tanti livelli, ma soprattutto perché le azioni con effetti collaterali come "Elimina" qui, non dovrebbero mai essere una richiesta GET.

Quindi la mia domanda (in particolare a coloro che affermano di non aver bisogno di nidificare i moduli) è: cosa fai? C'è qualche soluzione elegante che mi manca o la linea di fondo è davvero "O richiede JavaScript o invia tutto"?


17
È divertente, ma XHTML consente l'annidamento indiretto dei moduli secondo la nota interessante anderwald.info/internet/nesting-form-tags-in-xhtml - (X) HTML non consente i moduli di annidamento come "form> form", ma consente "form> fieldset> form ", il validatore W3 afferma che è valido, ma i browser hanno bug con tale annidamento.
Nishi,


correzione del linkrot per il link al commento di @ Nishi: web.archive.org/web/20170420110433/http://anderwald.info/…
albert

Risposte:


53

Lo implementerei esattamente come descritto: inviare tutto al server e fare un semplice if / else per verificare quale pulsante è stato cliccato.

E quindi implementerei una chiamata Javascript che si lega all'evento onsubmit del modulo che verificherebbe prima dell'invio del modulo e invierebbe solo i dati rilevanti al server (possibilmente attraverso un secondo modulo sulla pagina con l'ID necessario per elaborare la cosa come un input nascosto, oppure aggiorna la posizione della pagina con i dati che devi trasmettere come richiesta GET, oppure fai un post Ajax sul server, o ...).

In questo modo le persone senza Javascript sono in grado di utilizzare il modulo correttamente, ma il carico del server è compensato perché le persone che hanno Javascript stanno solo inviando il singolo pezzo di dati necessario. Concentrarsi solo sul supporto dell'uno o dell'altro limita davvero inutilmente le opzioni.

In alternativa, se stai lavorando dietro un firewall aziendale o qualcosa del genere e tutti hanno Javascript disabilitato, potresti voler creare due moduli e utilizzare un po 'di magia CSS per far sembrare che il pulsante Elimina sia parte del primo modulo.


15
l'originale OP non dichiarò javascript
Jason,

26
Il problema con questo metodo è che non è RESTful. Un salvataggio ed eliminazione corrispondono a diverse azioni sulla risorsa: "Salva" -> POST / my_resource (creazione di una nuova risorsa) "Salva" -> PUT / my_resource (modifica di una risorsa esistente) "Elimina" -> DELETE / my_resource (distruggere la risorsa) RESTfully parlando, il problema è che un POST dovrebbe creare una nuova risorsa. Certamente non dovrebbe mai cancellare una risorsa esistente.
user132447,

178

So che questa è una vecchia domanda, ma HTML5 offre un paio di nuove opzioni.

Il primo è separare il modulo dalla barra degli strumenti nel markup, aggiungere un altro modulo per l'azione di eliminazione e associare i pulsanti nella barra degli strumenti ai rispettivi moduli utilizzando l' formattributo.

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

Questa opzione è abbastanza flessibile, ma il post originale menzionava anche che potrebbe essere necessario eseguire diverse azioni con un unico modulo. HTML5 viene di nuovo in soccorso. Puoi utilizzare l' formactionattributo sui pulsanti di invio, in modo che pulsanti diversi nella stessa forma possano inviare a URL diversi. Questo esempio aggiunge semplicemente un metodo clone alla barra degli strumenti all'esterno del modulo, ma funzionerebbe allo stesso modo nidificato nel modulo.

<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

Il vantaggio di queste nuove funzionalità è che fanno tutto questo in modo dichiarativo senza JavaScript. Lo svantaggio è che non sono supportati sui browser più vecchi, quindi dovresti fare un po 'di polifilling per i browser più vecchi.


4
+1 - ma sarebbe bello sapere a cosa form=serve il supporto del browser - CanIUse non lo dice davvero. caniuse.com/#feat=forms
AKX

1
Esatto, questo è eccellente, ma ad oggi una funzionalità non supportata da IE lo squalifica ancora per l'uso "di produzione"
Jako

3
È buona norma utilizzare <input>per dichiarare i pulsanti. l' <button>elemento è stato introdotto 15 anni fa per questo scopo. Davvero persone.
Nicholas Shanks,

27
Il tuo punto è inutilmente controverso e irrilevante per la domanda del PO. L'OP ha chiesto informazioni sulla limitazione dell'annidamento dei moduli senza utilizzare JavaScript e la risposta offre una soluzione. Se usi <input>o <button>elementi è irrilevante, anche se i pulsanti sono generalmente più appropriati per le barre degli strumenti, come dici tu. A proposito, gli elementi dei pulsanti non sono supportati da tutti i browser da 15 anni.
shovavnik,

6
@AKX Sebbene sia una vecchia affermazione, sarai in grado di visualizzare il supporto per questa funzione qui: html5test.com/compare/feature/form-association-form.html . IE è un annullamento, la maggior parte degli altri browser desktop sembrano tollerabili.
grasso

16

puoi avere i moduli fianco a fianco sulla pagina, ma non nidificati. quindi utilizzare CSS per rendere tutti i pulsanti allineati graziosi?

<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
Sì e sembra troppo hacker. Inoltre, in una pagina molto grande (immagina schede e sottoschede e annidamento dei pulsanti di invio in diversi livelli di ambito) è quasi impossibile separare completamente la posizione dello schermo degli elementi dalla loro posizione nel documento.
gCoder,

3
bene, è sempre un equilibrio tra ciò che vuoi fare e ciò che puoi fare. sembra che queste siano le opzioni - un modulo gestito sul server o uno multiplo che diventa difficile da mantenere - scegli saggiamente :)
Jason,

3
Sì, purtroppo con tutte quelle limitazioni in html, mi limiterò a seguire ciò che è supportato e inviare l'intero modulo al server e successivamente utilizzare javascript in modo discreto per i client che lo supportano per intercettare l'invio del modulo e inviare solo ciò che è realmente necessario. Questo è il miglior compromesso.
gCoder,

13

HTML5 ha un'idea di "proprietario del modulo", l'attributo "modulo" per gli elementi di input. Permette di emulare moduli nidificati e risolverà il problema.


1
Qualche riferimento su questo degno link?
Dakab,

Vedi w3.org/TR/html5/forms.html#form-owner "Un elemento associato al modulo è, per impostazione predefinita, associato al suo elemento modulo antenato più vicino, ma, se è riassociabile, può avere un attributo del modulo specificato per sovrascrivere Questo."
Nishi,

11

Tipo di vecchio argomento, ma questo potrebbe essere utile per qualcuno:

Come qualcuno ha menzionato sopra, è possibile utilizzare il modulo fittizio. Ho dovuto superare questo problema qualche tempo fa. All'inizio mi sono completamente dimenticato di questa restrizione HTML e ho appena aggiunto i moduli nidificati. Il risultato è stato interessante: ho perso la mia prima forma dal nidificato. Quindi si è rivelato essere una sorta di "trucco" per aggiungere semplicemente un modulo fittizio (che verrà rimosso dal browser) prima dei moduli nidificati effettivi.

Nel mio caso si presenta così:

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

Funziona bene con Chrome, FireFox e Safari. IE fino a 9 (non sono sicuro circa 10) e Opera non rileva i parametri nella forma principale. $ _REQUEST globale è vuoto, indipendentemente dagli input. Le forme interne sembrano funzionare bene ovunque.

Non ho ancora testato un altro suggerimento descritto qui: fieldset attorno a forme nidificate.

EDIT : Frameset non ha funzionato! Ho semplicemente aggiunto il modulo principale dopo gli altri (non più moduli nidificati) e ho usato il "clone" di jQuery per duplicare gli input nel modulo al clic del pulsante. Aggiunto .hide () a ciascuno degli input clonati per mantenere invariato il layout e ora funziona come un incantesimo.


Ha funzionato perfettamente per me. Stavo lavorando su una pagina asp.net, che aveva un modulo tutto compreso. Avevo un modulo nidificato interno da utilizzare per il plug-in di convalida jQuery ( github.com/jzaefferer/jquery-validation ), e mentre funzionava perfettamente in FireFox e IE, non funzionava in Chrome con 'TypeError: Cannot call method method' element 'of non definito'. Si è scoperto che la chiamata di inizializzazione validate () non andava a buon fine perché non riusciva a trovare il modulo interno, poiché Chrome l'ha rimosso dal blocco di HTML aggiunto usando jQuery.html (). L'aggiunta di un piccolo modulo fittizio prima ha fatto sì che Chrome abbandonasse quello reale.
John - Not A Number

Questo non sembra funzionare più. Firefox ha rimosso il secondo tag <form> e quindi ha terminato il mio modulo con il primo tag </form>. Ciò ha lasciato un tag </form> non valido nella parte inferiore della pagina e un modulo che non verrà inviato correttamente. :(
Tarquin

Molto utile per me. Sto aggiungendo un widget web che contiene un modulo in una pagina asp.net. Sorpreso da WebForm che genera sempre un modulo che racchiude tutto nel corpo HTML. Il modulo allegato diventa un mal di testa per il widget web. La forma fittizia sembra essere una soluzione rapida ed efficace per questa situazione. Funziona perfettamente su Chrome e Safari finora, non ho ancora testato Firefox. Grazie per averlo condiviso, mi hai fatto risparmiare un sacco di tempo!
JM Yang,

4

Penso che Jason abbia ragione. Se la tua azione "Elimina" è minima, creala da sola in una forma e allineala con gli altri pulsanti in modo da rendere l'interfaccia simile a una forma unificata, anche se non lo è.

O, naturalmente, ridisegna la tua interfaccia e lascia che le persone cancellino da qualche altra parte, il che non richiede loro di vedere l'enorme forma.


2

Un modo in cui lo farei senza javascript sarebbe quello di aggiungere una serie di pulsanti di opzione che definiscono l'azione da eseguire:

  • Aggiornare
  • Elimina
  • Qualunque cosa

Quindi lo script di azione eseguirà azioni diverse a seconda del valore del set di pulsanti di opzione.

Un altro modo sarebbe quello di mettere due moduli nella pagina come hai suggerito, ma non nidificati. Il layout può essere difficile da controllare sebbene:

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

Utilizzare il campo "task" nascosto nello script di gestione per diramare in modo appropriato.


1

Questa discussione è ancora interessante per me. Dietro il post originale ci sono "requisiti" che l'OP sembra condividere - vale a dire un modulo con retrocompatibilità. Come qualcuno il cui lavoro al momento della scrittura a volte deve supportare IE6 (e per gli anni a venire), lo scavo.

Senza spingere il framework (tutte le organizzazioni vorranno rassicurarsi sulla compatibilità / solidità e non sto usando questa discussione come giustificazione per il framework), le soluzioni Drupal a questo problema sono interessanti. Drupal è anche direttamente pertinente perché il framework ha da tempo una politica di "dovrebbe funzionare senza Javascript (solo se lo desideri)", cioè il problema del PO.

Drupal usa le sue funzioni form.inc piuttosto estese per trovare triggering_element (sì, questo è il nome nel codice). Vedi la parte inferiore del codice elencato nella pagina API per form_builder (se desideri approfondire i dettagli, si consiglia l'origine: drupal-x.xx / Includes / form.inc ). Il builder utilizza la generazione automatica degli attributi HTML e, tramite quello, può al ritorno rilevare quale pulsante è stato premuto e agire di conseguenza (questi possono essere impostati per eseguire anche processi separati).

Oltre al generatore di moduli, Drupal divide le azioni di "eliminazione" dei dati in URL / moduli separati, probabilmente per i motivi menzionati nel post originale. Ciò richiede una sorta di fase di ricerca / elenco ( gemere un altro modulo !, ma è facile da usare) come preliminare. Ma questo ha il vantaggio di eliminare il problema "invia tutto". La grande forma con i dati viene utilizzata per lo scopo previsto, la creazione / l'aggiornamento dei dati (o anche un'azione di "unione").

In altre parole, un modo per aggirare il problema è di devolvere il modulo in due, quindi il problema svanisce (e anche i metodi HTML possono essere corretti tramite un POST).


0

Utilizzare un iframeper il modulo nidificato. Se hanno bisogno di condividere i campi, allora ... non è proprio nidificato.


1
L'uso di un iframe è un'ottima idea, è solo un peccato che nella maggior parte dei casi avresti bisogno di JavaScript per ridimensionarlo (dal momento che non puoi sapere quale sarà la dimensione del testo dell'utente, quindi, quanto sarà grande il pulsante).
Nicholas Shanks,

@Nicholas, come puoi usare Javascript per ridimensionarlo? L'HTML con iframe non può parlare con l'iframe e viceversa, a meno che non si trovino nello stesso dominio.
Dan Rosenstark,

@Nicholas, grazie, volevo solo assicurarmi che fossero nello stesso dominio
Dan Rosenstark,

0

La mia soluzione è di fare in modo che i pulsanti chiamino le funzioni JS che scrivono e inviano i moduli senza il modulo principale

<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

Se davvero non vuoi usare più moduli (come suggerisce Jason), usa pulsanti e gestori 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>

E poi in javascript (uso jQuery qui per semplicità) (anche se è piuttosto eccessivo per l'aggiunta di alcuni gestori onclick)

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

Inoltre, so che metà della pagina non funzionerà senza javascript.


1
A meno che non abbia trascurato qualcosa, questo non è meglio del collegamento all'azione di eliminazione: il risultato finale sarà una richiesta GET all'azione di eliminazione (non valida). In effetti è un po 'più grossolano, dal momento che richiede JavaScript per realizzare qualcosa che un semplice tag link vecchio potrebbe fare.
Kyle Fox,

-1

In risposta a una domanda posta da Yar in un commento alla sua risposta, presento alcuni JavaScript che ridimensioneranno un iframe. Nel caso di un pulsante modulo, è sicuro supporre che l'iframe si trovi sullo stesso dominio. Questo è il codice che uso. Dovrai modificare la matematica / le costanti per il tuo sito:

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

Quindi chiamalo così:

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

-1

Ho appena trovato un bel modo di farlo con 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

Bene, se invii un modulo, il browser invia anche un nome e un valore per l'invio dell'input. Quindi quello che puoi fare è

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

quindi sul lato server basta cercare il parametro che avvia la stringa di larghezza "azione:" e la parte restante indica quale azione intraprendere

quindi quando fai clic sul pulsante Salva browser ti invia qualcosa come foo = asd & action: save = Save


-1

Ho risolto il problema includendo una casella di controllo a seconda della forma che la persona voleva fare. Quindi utilizzare 1 pulsante per inviare l'intero modulo.


2
Benvenuto in Stack Overflow . È meglio descrivere come questo risolve il problema, piuttosto che dire semplicemente quello che hai fatto. Vedi Come rispondere per suggerimenti su come creare ottime risposte su Stack Overflow. Grazie!
Qantas 94 Pesante,

-2

In alternativa, è possibile assegnare il modulo actiob al volo ... potrebbe non essere la soluzione migliore, ma sicuramente allevia la logica lato server ...

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

Perché? Lo sto chiedendo seriamente, perché non esiste un modo semplice per ottenere quale pulsante è stato cliccato. Stai dicendo che qualcosa come il metodo click () di jQuery dovrebbe essere usato per impostare il gestore onclick del pulsante? O che la gestione dell'evento clic prima dell'invio del modulo sia il problema?
Zack Morris,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.