11521 sujets

JavaScript, DOM et API Web HTML5

Bonjour,

Je suis actuellement en train de créer un formulaire où un utilisateur peut remplir des données qui seront insérés dans une base de donnée.

Cependant, il existe deux types de champs : les champs uniques (comme le nom du produit) et des champs potentiellement multiples (par exemple des liens vers une image du produit, ...). Mon but est de pouvoir permettre à l'utilisateur d'avoir plusieurs champs pour certains éléments du formulaire.
Au niveau du stockage des données $_POST, cela se fait simplement à l'aide d'un tableau:
<input type="text" name="array[]" />

On appelle les fonctions via $_POST['array'][0].

Avec du code JS, j'ai inséré dans mon formulaire un bouton rajoutant un champ à compléter :

<table>
	<tr>
		<td>Element à un seul champ</td>
		<td><input type="text" name="elt1" value="<?php echo "Element 1"; ?>"/></td>
	</tr>
	<tr>
		<td>Tableau 1</td>
		<td id="champ_1"><input type="text" name="tab1[]"></td>
		<td><button type="button" onclick="addField()" >+</button></td> <!-- Ancienne méthode -->
	</tr>
	<tr>
		<td>Tableau 2</td>
		<td id="champ_2"><input type="text" name="tab2[]" value="<?php "Tableau 2" ?>"/></td>
		<td><button type="button" onclick="addField('<?php echo $champ_2; ?>')" >+</button></td> <!-- Nouvelle méthode -->
	</tr>
</table>


var div = document.getElementById('champ_1'); // champ_1 représente l'id du tr où se situe le bouton
function addInput(name){
	var input = document.createElement("input");
	input.name = name;
	div.appendChild(input);
}
function addField() {
	addInput("tab1[]");
}


http://i.imgur.com/j2PHrfd.gif
(Le + reste sur la dernière ligne, je ne sais pas trop pourquoi mais ce n'est pas le sujet ici)

Actuellement la fonction JS crée un champ dans le tableau 1 (c'est écrit en dur). Ma question est de savoir comment insérer un argument à ma fonction JS de manière à ce qu'elle dépende du bouton (Nouvelle méthode indiquée dans tableau 2).

J'ai essayé en rajoutant un argument à la fonction addField() mais l'id définissant la position du nouveau champ est déclaré dans div en dehors de la fonction. En insérant l'initialisation de cette variable dans la fonction le bouton "+" n'a plus aucun effet.

function addInput(name){
	var input = document.createElement("input");
	input.name = name;
	div.appendChild(input);
}
function addField(position) {
	var div = document.getElementById(position);
	addInput("tab1[]");
}


De même, j'en profite pour demander si vous avez des idées sur comment faire pour générer plusieurs champs qui sont liés. Par exemple si quelqu'un veut rajouter un lien, en cliquant sur "+" il faudrait faire apparaître un nouveau champ sur la ligne "lien_url" et sur la ligne "lien_nom".

Pour résumer :

Analyse
- Je travaille sur un formulaire
- J'ai inséré un bouton "+" permettant de rajouter un champ
- Je cherche à rendre la fonction JS dépendante du bouton (qu'en appuyant sur Button1, il sache qu'on travaille avec Button1 et pas Button2)

Infos
- L'id définit là où doit se situer le nouveau champ. Il est placé dans <td> avant le bouton afin de rajouter un champ après le premier champ, mais avant le bouton. En le plaçant dans <tr> il se mettait après le bouton.
- À terme certains champs contiendront plusieurs informations, il faut donc un bouton "+" sur une des deux lignes qui rajoute un champ pour les deux (nom du lien + url du lien par exemple).
- Le Form est placé dans une <table> afin d'aligner correctement les éléments mais il est possible de le faire dans un div (c'est pourquoi la variable dans la fonction JS s'appelle initialement div)

Édition, problème résolu :

<table>
	<tr>
		<td>Element à un seul champ</td>
		<td><input type="text" name="elt1" value="<?php echo "Element 1"; ?>"/></td>
	</tr>
	<tr>
		<td>Tableau 1</td>
		<td id="champ_1"><input type="text" name="tab1[]" value="<?php "Tableau 2" ?>"/></td>
		<td><button type="button" onclick="addField('tab1[]',champ_1)" >+</button></td>
	</tr>
	<tr>
		<td>Tableau 2</td>
		<td id="champ_2"><input type="text" name="tab2[]" value="<?php "Tableau 2" ?>"/></td>
		<td><button type="button" onclick="addField('tab2[]',champ_2)" >+</button></td>
	</tr>
</table>

var champ1 = document.getElementById('champ_1');
var champ2 = document.getElementById('champ_2');
function addInput(name,champ){
	var input = document.createElement("input");
	input.name = name;
	champ.appendChild(input);
}
function addField(nom,champ) {
	addInput(nom,champ);
}

Modifié par Ara (16 Jul 2015 - 15:45)
Salut,

En lisant le début de ta demande, je ne voyais pas d'où sortait la variable "div" dans la fonction addInput() et du coup ça ne pouvait pas fonctionner.

Pourquoi as-tu 2 fonctions ? addField ne fait qu'appeler addInput sans aucune modification... Autant appeler directement ta fonction addInput dans ton html (même arguments).
La variable div est déclarée avant addInput :


<script type="text/javascript" >
	var div = document.getElementById('champ_1');
	function addInput(name,champ){
		var input = document.createElement("input");
		input.name = name;
		champ.appendChild(input);
	}
	function addField(nom,champ) {
		addInput(nom,champ);
	}
</script>


Ces deux fonctions ont été trouvées sur ce site. Je pense en effet qu'il est possible de les fusionner, cependant lorsque j'ai essayé de mettre la variable div à l'intérieur de la fonction addField cela n'a pas fonctionné :


function addInput(name,champ){
	var input = document.createElement("input");
	input.name = name;
	champ.appendChild(input);
}
function addField(nom,champ) {
	var div = document.getElementById('champ_1');
	addInput(nom,champ);
}


J'ai réussi à "résoudre" le problème en assignant à plusieurs variables le rôle de div (une pour chaque champ multiple). Ce n'est pas géré dynamiquement mais ça fonctionnait...

Jusqu'à ce que je complexifie la chose : à un bouton "Ajout" est associé x champs et l'utilisateur doit pouvoir renseigner y fois ces champs :

http://i.imgur.com/2Fw6hr7.gif

Ici par exemple, en cliquant sur le "+" d'une section (Fiche technique, schéma, vidéo) on rajoute un champ pour chaque élément de la section.

<table>
	<thead>
		<th>Fiche Technique</th>
		<td><button type="button" onclick="addField('tab_FT[]',champ_FT)" >+</button></td>
		<!-- tab_FT[] tableau de string : ["nom_FT[]", "url_FT[]"] -->
	</thead>
	<tr>
		<td>Nom</td>
		<td id="champ_FT"><input type="text" name="nom_FT[]" value="<?php echo "Element 1"; ?>"/></td>
	</tr>
	<tr>
		<td>Url</td>
		<td id="champ_FT"><input type="text" name="url_FT[]" value="<?php echo "Element 2"; ?>"/></td>
	</tr>
</table>


On suppose ici que tab_FT[] contient un tableau contenant les noms de chacun des éléments de sa section (pour pouvoir ensuite traiter les données dans un forEach dans la fonction JS)
var champFT = document.getElementById('champ_FT');
var champS = document.getElementById('champ_S');
var champV = document.getElementById('champ_V');

function addField(tab_nom,champ) {

	tab_nom.forEach(function(name) 
	{
		var input = document.createElement("input");
		input.name = name;
		champ.appendChild(input);
	});


La syntaxe d'un tableau en JS serait :
var tableau = ["nom_FT[]", "url_FT",];
tableau.forEach(function(name)
{
	[...]
});


Je me dis que si je parviens à stocker dans le premier argument de chaque bouton un tableau listant tous les noms de la section ("nom_FT[]", "url_FT[]" ici) (en temps que variable PHP, écrite en dur pour faire simple) cela devrait fonctionner. Cependant je n'arrive pas à construire ce tableau (problème de syntaxe ? Sans doute, mais peut-être pas que). Même en utilisant une variable dans le programme JS censé être égal à ce tableau, je n'arrive pas à faire apparaître de nouveaux champs.
Vous devriez essayer la méthode clone.

J'ai déjà utilisé ça souvent, et ça permet de copier une ligne ou un pan entier de document en toute simplicité.

IL faut juste faire attention aux noms et aux id pour que ça reste cohérent (gare aux doublons d'id !), et aussi au fait que les gestionnaires d'évènements associés aux éléments clonés ne sont pas clonés, mais sinon c'est extrêmement pratique quand l'utilisateur doit pouvoir saisir un nombre potentiellement infini de fois la même série d'informations similaires. Un clic sur "+" clone la dernière ligne et met à jour les id/name, et un clic sur "-" si on le propose vire la ligne correspondante et met à jour les id/name suivants.

Si on est suffisament malin, il n'y a que les id à mettre à jour et même pas besoin de toucher aux name.

Ca me paraît être un fonctionnement somme toute assez classique.
JENCAL a écrit :
<td><button type="button" onclick="addField('tab_FT[]',champ_FT)" >


c'est quoi champ_FT ?


Champ_FT est censé être l'ID définissant l'endroit où on doit insérer les nouveaux champs. Ici en appuyant sur le bouton lié à fiche technique, il doit générer deux champs (un pour le nom, un pour l'url) à droite des champs existants.

Pour la méthode clone je n'ai jamais travaillé avec des objets jusqu'à présent. Il faut que je regarde comment cela fonctionne, merci !

Édition : a priori je pense partir sur un système plus simple à implémenter dans un premier temps : le formulaire affiche n+1 champs pour chaque section (n étant le nombre de champs initialement renseignés dans la BDD pour la section en question). L'utilisateur ne pourra rajouter qu'un seul bloc d'information entre chaque validation mais cela permet d'éviter l'utilisation du bouton "+".

A priori même si la BDD a des "trous" dans ses ID cela ne doit pas poser de problème puisqu'on utilise un foreach sur la requête SQL.
SELECT nom_FT, url_FT
FROM `Produit`
INNER JOIN `Fiche_technique` ON foreign_FT = id_P;

SELECT nom_S, url_S
FROM `Produit`
INNER JOIN `Schema` ON foreign_S = id_P;
[...]


Cependant je pense que la meilleure solution reste celle de QuentinC.
Modifié par Ara (15 Jul 2015 - 10:05)
a écrit :
Édition : a priori je pense partir sur un système plus simple à implémenter dans un premier temps : le formulaire affiche n+1 champs pour chaque section (n étant le nombre de champs initialement renseignés dans la BDD pour la section en question). L'utilisateur ne pourra rajouter qu'un seul bloc d'information entre chaque validation mais cela permet d'éviter l'utilisation du bouton "+".


Dans un premier temps, ça sera effectivement plus simple. Mais ergonomiquement c'est quand même moins bon.

Des alternatives au bouton "+" pourraient être d'ajouter un nouveau bloc
1 - Dès que l'utilisateur commence à remplir le dernier bloc, i.e. dès le moment où le dernier bloc n'est plus vierge
2 - Quand l'utilisateur appuie sur Tab et qu'il se trouve dans le dernier champ, à condition qu'il soit rempli; un peu à la manière des tableaux word.

Cela dit, ça ne change rien à la méthode que je préconise pour faire apparaître les nouveaux champs. Seuls les déclencheurs changent dans ces scénarios alternatifs.
Modifié par QuentinC (15 Jul 2015 - 22:51)
De même, la suppression se fait finalement via un bouton "submit" avec un name précis ($key contient l'id de la ligne à supprimer).
<td><input type="submit" name="suppr_D[<?php echo $key; ?>]" value=" X " onclick="return confirm('Voulez vous vraiment supprimer cette colonne ?');"/></td>


Je traite donc les modifications des données en même temps que je supprime la ligne, ce qui permet de ne pas perdre les données modifiées dans d'autres cases par exemple (si l'utilisateur remplit une case et en supprime une autre en même temps). Là encore, le "must" serait d'utiliser AJAX afin de modifier l'affichage de la page et de la BDD sans avoir à recharger la page. Cependant là encore ça me semble un peu technique et je compte utiliser cette méthode, au moins temporairement.
Merci de l'aide apportée !
La seule chose à faire très attention quand on utilise plusieurs boutons submit pour le même formulaire, c'est l'action par défaut déclenchée quand on presse enter. En général c'est toujours le premier bouton dans l'ordre du DOM.

Si le premier bouton est un bouton submit qui commande de supprimer, gare aux surprises ! d'autant plus que la boîte de confirmation via onclick ne s'ouvrira même pas.
Bonjour,

je viens de lire ton message est j'avais effectivement le problème que tu rapportes : en validant via la touche Enter cela activait le bouton Supprimer et non Submit.

Cependant j'étais averti du problème car l'alerte "Voulez vous vraiment supprimer cette donnée" apparaissait correctement (alors qu'elle est définie en onclick et non onsubmit !).

J'ai simplement rajouté un bouton "submit" que j'ai mis en hidden en haut de mon formulaire pour forcer le SUBMIT via Enter.


<input type="submit" value="Valider" style="display:none;" />
<!-- [...] -->
<input type="submit" name="suppr_FT[<?php echo $key; ?>]" value=" X " onclick="return confirm('Voulez vous vraiment supprimer cette colonne ?');" />
<!-- [...] -->
<input type="submit" value="Valider" />


Par contre je ne sais pas pourquoi il m'avertissait quand même de l'action onclick...
Modifié par Ara (20 Jul 2015 - 16:37)
a écrit :
Cependant j'étais averti du problème car l'alerte "Voulez vous vraiment supprimer cette donnée" apparaissait correctement (alors qu'elle est définie en onclick et non onsubmit !).


C'est peut-être le cas avec certains navigateurs, mais pas tous. Alors méfiance.