web-dev-qa-db-fra.com

Créer un tableau HTML où chaque TR est un FORM

J'essaie de créer un tableau où chaque ligne est un formulaire. Je veux que chaque entrée se trouve dans une division de table différente, mais j'ai tout de même besoin de cela, par exemple, toutes les premières entrées appartiennent à la même tête de table et ainsi de suite.

Ce que j'essaie de faire est une grille éditable, plus ou moins ceci:

<table>
    <tr>
        <form method="GET" action="whatever">
            <td><input type="text"/></td>
            <td><input type="text"/></td>
        </form>
    </tr>
    <tr>
        <form method="GET" action="whatever">
            <td><input type="text"/></td>
            <td><input type="text"/></td>
        </form>
    </tr>
</table>

Mais apparemment, je ne peux pas arranger les tags de cette façon (c'est ce que le validateur du W3c a dit).

Un bon moyen de faire ça?

53
vtortola

Si vous voulez une "grille éditable", c'est-à-dire une table semblable à une structure qui vous permet de transformer n'importe quelle ligne en formulaire, utilisez CSS qui imite la disposition de la balise TABLE: display:table, display:table-row, et display:table-cell.

Il n'est pas nécessaire de placer l'ensemble de la table dans un formulaire ni de créer un formulaire et un tableau distincts pour chaque ligne apparente de votre tableau.

Essayez ceci à la place:

<style>
DIV.table 
{
    display:table;
}
FORM.tr, DIV.tr
{
    display:table-row;
}
SPAN.td
{
    display:table-cell;
}
</style>
...
<div class="table">
    <form class="tr" method="post" action="blah.html">
        <span class="td"><input type="text"/></span>
        <span class="td"><input type="text"/></span>
    </form>
    <div class="tr">
        <span class="td">(cell data)</span>
        <span class="td">(cell data)</span>
    </div>
    ...
</div>

Le problème avec l'encapsulation de la totalité de la table dans un formulaire est que tous les éléments du formulaire seront envoyés lors de la soumission (c'est peut-être souhaité mais probablement pas). Cette méthode vous permet de définir un formulaire pour chaque "ligne" et d'envoyer uniquement cette ligne de données lors de l'envoi.

Enrouler une balise FORM autour d'une balise TR (ou de TR autour d'une FORM) a pour inconvénient que ce n'est pas du code HTML. Le formulaire permettra toujours de soumettre comme d'habitude, mais à ce stade, le DOM est cassé. Remarque: essayez d’obtenir les éléments enfants de votre FORM ou TR avec JavaScript, cela peut entraîner des résultats inattendus.

Notez que IE7 ne supporte pas ces styles de table CSS et que IE8 aura besoin d'une déclaration de doctype pour le faire passer en mode "standard": (essayez celui-ci ou quelque chose d'équivalent)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Tout autre navigateur prenant en charge display: table, display: table-row et display: table-cell devrait afficher votre tableau de données CSS de la même manière que si vous utilisiez les fonctions TABLE, TR et TD balises, la plupart le sont.

Notez que vous pouvez également imiter THEAD, TBODY, TFOOT en encapsulant vos groupes de lignes dans une autre DIV avec display: table-header-group, table-row-group et table-footer-group respectivement.

REMARQUE: La seule chose que vous ne pouvez pas faire avec cette méthode est colspan.

Regardez cette illustration: http://jsfiddle.net/ZRQPP/

85
Matthew

Si toutes ces lignes sont liées et que vous devez modifier les données tabulaires ... pourquoi ne pas simplement envelopper la table entière dans un formulaire et modifier GET en POST (sauf si vous savez que vous n'allez pas envoyer plus que le maximum de données qu'une demande GET peut envoyer).

(Cela suppose, bien sûr, que toutes les données soient au même endroit.)

<form method="POST" action="your_action">
<table>
<tr>
<td><input type="text" name="r1c1" value="" /></td>
<!-- ... snip ... -->
</tr>
<!-- ... repeat as needed ... -->
</table>
</form>
12
Sean Vieira

Si JavaScript est une option et que vous souhaitez éviter les tables imbriquées, incluez jQuery et essayez la méthode suivante.

Tout d'abord, vous devrez attribuer à chaque ligne un identifiant unique, comme suit:

<table>
  <tr id="idrow1"><td> ADD AS MANY COLUMNS AS YOU LIKE  </td><td><button onclick="submitRowAsForm('idrow1')">SUBMIT ROW1</button></td></tr>
  <tr id="idrow2"><td> PUT INPUT FIELDS IN THE COLUMNS  </td><td><button onclick="submitRowAsForm('idrow2')">SUBMIT ROW2</button></td></tr>
  <tr id="idrow3"><td>ADD MORE THAN ONE INPUT PER COLUMN</td><td><button onclick="submitRowAsForm('idrow3')">SUBMIT ROW3</button></td></tr>
</table>

Ensuite, incluez la fonction suivante dans votre JavaScript pour votre page.

<script>
function submitRowAsForm(idRow) {
  form = document.createElement("form"); // CREATE A NEW FORM TO DUMP ELEMENTS INTO FOR SUBMISSION
  form.method = "post"; // CHOOSE FORM SUBMISSION METHOD, "GET" OR "POST"
  form.action = ""; // TELL THE FORM WHAT PAGE TO SUBMIT TO
  $("#"+idRow+" td").children().each(function() { // GRAB ALL CHILD ELEMENTS OF <TD>'S IN THE ROW IDENTIFIED BY idRow, CLONE THEM, AND DUMP THEM IN OUR FORM
        if(this.type.substring(0,6) == "select") { // JQUERY DOESN'T CLONE <SELECT> ELEMENTS PROPERLY, SO HANDLE THAT
            input = document.createElement("input"); // CREATE AN ELEMENT TO COPY VALUES TO
            input.type = "hidden";
            input.name = this.name; // GIVE ELEMENT SAME NAME AS THE <SELECT>
            input.value = this.value; // ASSIGN THE VALUE FROM THE <SELECT>
            form.appendChild(input);
        } else { // IF IT'S NOT A SELECT ELEMENT, JUST CLONE IT.
            $(this).clone().appendTo(form);
        }

    });
  form.submit(); // NOW SUBMIT THE FORM THAT WE'VE JUST CREATED AND POPULATED
}
</script>

Alors qu'avons-nous fait ici? Nous avons attribué à chaque ligne un identifiant unique et inclus un élément dans la ligne pouvant déclencher la soumission de l'identifiant unique de cette ligne. Lorsque l'élément de soumission est activé, un nouveau formulaire est créé et configuré de manière dynamique. Ensuite, en utilisant jQuery, nous clonons tous les éléments contenus dans <td> 's de la ligne qui nous a été passée et ajoute les clones à notre formulaire créé dynamiquement. Enfin, nous soumettons ledit formulaire.

9
b_laoshi

Vous pouvez avoir des problèmes avec la largeur de colonne, mais vous pouvez les définir explicitement.

<table>
  <tr>
    <td>
       <form>
         <table>
           <tr>
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
           </tr>
         </table>
       </form>
     </td>
    </tr>
  <tr>
    <td>
       <form>
         <table>
           <tr>
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
             <td></td> 
           </tr>
         </table>
       </form>
     </td>
    </tr>
  </table>

Vous voudrez peut-être aussi en faire un formulaire unique, puis utiliser jQuery pour sélectionner les éléments de formulaire dans la ligne souhaitée, les sérialiser et les soumettre en tant que formulaire.

Voir: http://api.jquery.com/serialize/

En outre, il existe de très jolis plugins de grille: http://www.google.com/search?q=jquery+grid&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en- US: officiel & client = firefox-a

9
Eli

Si toutes ces lignes sont liées et que vous devez modifier les données tabulaires ... pourquoi ne pas simplement envelopper la table entière dans un formulaire et modifier GET en POST (sauf si vous savez que vous ' ne pas envoyer plus que le maximum de données qu’une requête GET peut envoyer).

Je ne peux pas envelopper la table entière dans un formulaire, car certains champs d'entrée de chaque ligne sont input type = "fichier" et les fichiers peuvent être volumineux. Lorsque l'utilisateur soumet le formulaire, je souhaite POST uniquement les champs de la ligne actuelle, mais pas tous les champs de toutes les lignes pouvant contenir des fichiers volumineux inutiles, ce qui ralentit considérablement la soumission du formulaire.

J'ai donc essayé une imbrication incorrecte: tr/form et form/tr. Cependant, cela ne fonctionne que lorsque l'on n'essaie pas d'ajouter de nouvelles entrées de manière dynamique dans le formulaire. Les entrées ajoutées dynamiquement n'appartiendront pas à un formulaire imbriqué de manière incorrecte et ne seront donc pas soumises. (les entrées de formulaire/table valides dynamiquement sont soumises très bien).

L'imbrication de div [display: table]/form/div [display: table-row]/div [display: table-cell] a produit des largeurs non uniformes des colonnes de la grille. J'ai réussi à obtenir une mise en page uniforme en remplaçant div [display: table-row] par [display: table-row]:

div.grid {
    display: table;
}

div.grid > form {
    display: table-row;


div.grid > form > div {
    display: table-cell;
}
div.grid > form > div.head {
    text-align: center;
    font-weight: 800;
}

Pour que la mise en page soit correctement affichée dans IE8:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
...
<meta http-equiv="X-UA-Compatible" content="IE=8, IE=9, IE=10" />

Échantillon de sortie:

<div class="grid" id="htmlrow_grid_item">
<form>
    <div class="head">Title</div>
    <div class="head">Price</div>
    <div class="head">Description</div>
    <div class="head">Images</div>
    <div class="head">Stock left</div>
    <div class="head">Action</div>
</form>
<form action="/index.php" enctype="multipart/form-data" method="post">
    <div title="Title"><input required="required" class="input_varchar" name="add_title" type="text" value="" /></div>

Il serait cependant beaucoup plus difficile de faire fonctionner ce code dans IE6/7.

4
Dmitriy Sintsov

Si vous pouvez utiliser javascript et l'exiger strictement sur votre site Web, vous pouvez mettre des zones de texte, des cases à cocher et le contenu de votre choix sur chaque ligne de votre tableau et à la fin de chaque ligne, placez un bouton (ou un lien de la classe rowSubmit) "save". Sans aucune balise FORM. La forme qui sera simulée par JS et Ajax comme ceci:

<script type="text/javascript">
$(document).ready(function(){
  $(".rowSubmit").click(function()
  {
     var form = '<form><table><tr>' + $(this).closest('tr').html() + '</tr></table></form>';
     var serialized = $(form).serialize(); 
     $.get('url2action', serialized, function(data){
       // ... can be empty
     }); 
   });
});        
</script>

Qu'est-ce que tu penses?

PS: Si vous écrivez dans jQuery ceci:

$("valid HTML string")
$(variableWithValidHtmlString)

Il sera transformé en objet jQuery et vous pourrez le manipuler comme vous en avez l'habitude dans jQuery.

3
Racky

J'ai eu un problème similaire à celui posé dans la question initiale. J'étais intriguée par les divs appelées éléments de table (je ne savais pas que vous pouviez le faire!) Et je les ai lancées. Cependant, ma solution consistait à garder mes tables enveloppées dans des balises, mais à renommer chaque entrée et à sélectionner option pour devenir les clés du tableau, que je suis en train d'analyser pour obtenir chaque élément de la ligne sélectionnée.

Voici une seule ligne de la table. Notez que la clé [4] est l'ID de la ligne dans la base de données à partir de laquelle cette ligne de table a été extraite:

<table>
  <tr>
    <td>DisabilityCategory</td>
    <td><input type="text" name="FormElem[4][ElemLabel]" value="Disabilities"></td>
    <td><select name="FormElem[4][Category]">
        <option value="1">General</option>
        <option value="3">Disability</option>
        <option value="4">Injury</option>
        <option value="2"selected>School</option>
        <option value="5">Veteran</option>
        <option value="10">Medical</option>
        <option value="9">Supports</option>
        <option value="7">Residential</option>
        <option value="8">Guardian</option>
        <option value="6">Criminal</option>
        <option value="11">Contacts</option>
      </select></td>
    <td>4</td>
    <td style="text-align:center;"><input type="text" name="FormElem[4][ElemSeq]" value="0" style="width:2.5em; text-align:center;"></td>
    <td>'ccpPartic'</td>
    <td><input type="text" name="FormElem[4][ElemType]" value="checkbox"></td>
    <td><input type="checkbox" name="FormElem[4][ElemRequired]"></td>
    <td><input type="text" name="FormElem[4][ElemLabelPrefix]" value=""></td>
    <td><input type="text" name="FormElem[4][ElemLabelPostfix]" value=""></td>
    <td><input type="text" name="FormElem[4][ElemLabelPosition]" value="before"></td>
    <td><input type="submit" name="submit[4]" value="Commit Changes"></td>
  </tr>
</table>

Ensuite, en PHP, j'utilise la méthode suivante pour stocker dans un tableau ($ SelectedElem) chacun des éléments de la ligne correspondant au bouton d'envoi. J'utilise print_r() juste pour illustrer:

$SelectedElem = implode(",", array_keys($_POST['submit']));
print_r ($_POST['FormElem'][$SelectedElem]);

Cela semble peut-être compliqué, mais cela s’est avéré assez simple et a préservé la structure organisationnelle de la table.

1
NotQuiteAPro

Les tableaux ne sont pas faits pour ça, pourquoi ne pas utiliser <div> 's et CSS?

1
Harmen

J'appuie la suggestion de Harmen. Vous pouvez également insérer le tableau dans un formulaire et utiliser javascript pour capturer le focus de la ligne et ajuster l'action du formulaire via javascript avant la soumission.

1
Prescott

c'est aussi simple que de ne pas utiliser de table pour le balisage, comme le dit Harmen. Vous n’affichez pas de données après tout, vous collectez des données.

Je prendrai par exemple la question 23 ici: http://accessible.netscouts-ggmbh.eu/en/developer.html#fb1_22_5

Sur le papier, c'est bon comme ça. Si vous deviez afficher les résultats, ce serait probablement OK.
Mais vous pouvez le remplacer par ... 4 paragraphes avec une étiquette et une sélection (l'option serait l'en-tête de la première ligne). Un paragraphe par ligne, c'est beaucoup plus simple.

0
FelipeAls