11521 sujets

JavaScript, DOM et API Web HTML5

Bonsoir à tous...

J'ai trouvé un code Javascript que j'utilise pour vérifier si un formulaire a été modifié avant
de quitter la page par le button "annuler"...

J'ai deux problèmes pas bien graves, mais j'aimerai comprendre et les résoudre :
- Si je clique sur un bouton radio pour revenir ensuite à la sélection précédente (donc aucun
changement), la div d'alerte m'informe que le formulaire a été modifié ! ! !
(je n'ai aucun problème sur le textaera et les input par contre !)

- Si je modifie un champ, puis je clique sur annuler, la div d'alerte m'informe que le formulaire
a été modifié (normal !), je clique sur annuler pour retourner au formulaire et efface ma
modification, si je clique une nouvelle fois sur annuler, la div d'alerte m'informe toujours que
le formulaire a été modifié ! ! !

J'espère avoir été clair, voici le code : Smiley biggrin

<body onload="init()">
    <form name="myform">
        <input type="radio" class="spyme" name="type" value="particulier" checked = "checked" onClick="changeImage('images/particulier.png');"/>Particulier
        <input type="radio" class="spyme" name="type" value="entreprise" onClick="changeImage('images/entreprise.png');"/>Entreprise
        <input type="texte" class="spyme" name="name" placeholder="Nom" />	
    </form><!--myform-->
    <div id="alertes">
        <div id="box_alerte">
	    <div class="titre">
                <h4>Avertissement</h4></div>
		<div class="message">
                    Cet enregistrement a été modifié et vos modifications vont être annulées. Êtes-vous sûr(e) de vouloir quitter cette page?
                </div>
		<div class="boutons">
		       <a href="clients.php">
                            <div class="bouton">
                                OK
                            </div><!--bouton-->
			</a>
			<a href="#">
                            <div class="bouton2" onclick="annuler2()">
			        ANNULER
			    </div><!--bouton-->
			</a>
		</div>			
	</div><!--box_alerte-->
</div><!--alertes-->



</body>


<script>
/* DÉCLARATION DES VARIABLES */
var alertes = document.getElementById('alertes');
var modif=0;

/* ANNULATION SAISIE FORMULAIRE AVEC VERIF PERTE DES MODIF*/
function annuler(){
	if (modif==1){
		alertes.style.display = "block";
	}
	else {
		document.location.href="clients.php";
	}	
}


function annuler2(){
	alertes.style.display = "none";
}


/* VERIFICATION D'UNE MODIFICATION DU FORMULAIRE AVANT ANNULATION */ 
function init(){
	var tabObj=document.forms['myform'].elements
	var i=0;
	while(tabObj[i]){
		if (tabObj[i].className=='spyme'){
			tabObj[i].onchange=function(){
				modif=1;
			}
	   	}
	   	i++
	}
}
</script>




Merci d'avance pour votre aide ! [/i][/i][/i]
Modifié par etienne69 (19 Jun 2016 - 19:53)
Évidemment, ta variable modif ne repasse jamais à 0 une fois qu'elle a été mise à 1. Donc ce n'est pas possible que tu n'obtiennes pas d'alerte dès lors que tu as effectué une quelconque modification, et aucune contre-modification n'est suceptible d'annuler la modification et faire repasser modif à 0.


Pour une solution plus robuste, plutôt que d'écouter l'évènement change, tu devrais comparer les valeurs actuelles avec les valeurs par défaut (comparer value et defaultValue pour chaque champ) lorsque l'utilisateur demande une annulation. Annulation qui devrait d'ailleurs être symbolisée par un bouton reset et interceptée grâce à un évènement reset.

Par ailleurs, tu devrais utiliser true et false à la place de 1 et 0; ça ne change rien au problème mais ça simplifie la compréhension.
Modifié par QuentinC (20 Jun 2016 - 10:40)
Merci QuentinC d'avoir pris le temps de lire mon code et de me répondre !

Hélas, mon faible niveau (proche de 0) en JavaScript me permettra certainement de
corriger "false/true" mais pour le reste ça va être plus chaud : je ne vois pas bien comment
modifier tout ça !

Mon idée : une boucle qui récupérerait la valeur de chaque élément du formulaire au chargement
de la page (stockage dans un tableau ?) et comparerait ces valeurs avec celles contenues
dans ces mêmes champs au onclick du reset ?

T'aurais pas un début de piste ou une idée de structure à me donner pour que je me
creuse les méninges dessus ? Smiley langue
etienne69 a écrit :
Merci QuentinC d'avoir pris le temps de lire mon code et de me répondre !
Hélas, mon faible niveau (proche de 0) en JavaScript me permettra certainement de
corriger "false/true" mais pour le reste ça va être plus chaud : je ne vois pas bien comment
modifier tout ça !
Mon idée : une boucle qui récupérerait la valeur de chaque élément du formulaire au chargement
de la page (stockage dans un tableau ?) et comparerait ces valeurs avec celles contenues
dans ces mêmes champs au onclick du reset ?
T'aurais pas un début de piste ou une idée de structure à me donner pour que je me
creuse les méninges dessus ? Smiley langue

Bon... javascript n'est pas ma tasse de thé et je prends ton cas de figure comme un exercice.
Ci-dessous un bout de code HTML / CSS / javascript permettant de :
- créer un formulaire avec un sélecteur (type) et une zone de texte (nom)
- détecter chaque modification
- passer en vert (données non modifiées) ou en rouge (données modifiées) la zone de statut
Je précise que tout ceci n'est qu'un bout d'essai et n'est certainement pas à prendre comme vérité absolue. D'autres approches sont probablement (certainement) envisageables.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Démo</title>
<style type="text/css">
*
{
font-size:inherit;
}
body
{
font-family:Arial,sans-serif;
font-size:1em;
}
form
{
max-width:50rem;
margin:1rem auto;
}
legend
{
padding:0 0.5rem;
}
label
{
display:inline-block;
width:10rem;
white-space:nowrap;
text-align:right;
}
label::after
{
padding:0 0.4rem;
content:":";
}
.field
{
margin:0.6rem  0.4rem;
}
.buttons
{
margin:1rem 0.5rem;
}
#status
{
padding:0.4rem;
color:white;
border:2px solid gray;
}
#status.unchanged
{
background-color:green;
border-color:green;
}
#status.updated
{
background-color:red;
border-color:red;
}
</style>
<script type="text/javascript">
var backup = "";
function getBean()
{
var bean={};
bean['type'] = document.getElementById('typeField').value;
bean['name'] = document.getElementById('nameField').value;
return bean;
}
function isUpdated()
{
var text = JSON.stringify(getBean());
return backup !== text;
}
function check()
{
var element = document.getElementById('status');
if (isUpdated())
{
element.className = 'updated';
element.innerHTML = 'Données modifiées.';
}
else
{
element.className = 'unchanged';
element.innerHTML = 'Données non modifiées.';
}
}
// IMPORTANT :
// L'appel de la méthode check() sur l'événement onreset sur la balise FORM
// ne fonctionne pas correctement car l'exécution a lieu AVANT que les champs
// de saisie n'aient été effectivement réinitialisés. La fonction ci-dessous
// force d'abord la réinitialisation puis appelle ensuite la méthode check()
// pour réinitialiser le statut du formulaire.
function performReset()
{
var element = document.getElementById('formID');
element.reset();
check();
}
function installHandlers()
{
var element = document.getElementById('formID');
element.onreset = check;
element = document.getElementById('typeField');
element.onchange = check;
element.onblur = check;
element = document.getElementById('nameField');
element.onchange = check;
element.oninput = check;
element.onblur = check;
element = document.getElementById('cancelButton');
element.onclick = performReset;
}
function init()
{
backup = JSON.stringify(getBean());
check();
installHandlers();
}
</script>
</head>
<body onload="init()">
    <form id="formID" action="#myform" method="post">
		<fieldset>
			<legend>Données :</legend>
			<div class="field">
				<label for="typeField">Type</label>
				<select id="typeField">
					<option value="P" selected="selected">Particulier</option>
					<option value="E">Entreprise</option>
				</select>
			</div>
			<div class="field">
				<label for="nameField">Nom</label>
				<input id="nameField" type="text" placeholder="Nom"/>	
			</div>
		</fieldset>
		<div class="buttons">
			<button id="okButton" type="submit">Valider</button>
			<button id="cancelButton" type="reset">Annuler</button>
		</div>
		<fieldset onload="init">
			<legend>Statut données</legend>
			<div id="status" class="unchanged">
				Données non modifiées.
			</div>
		</fieldset>
    </form>
</body>
</html>

Le principe est assez simple et suit celui énoncé par Quentin.
En règle générale, lorsqu'on veut comparer un statut modifié / non modifié sur un formulaire de saisie, en Java ou autre langage, on procède de la façon suivante :
- création d'un backup avec l'objet initial
- comparaison avec un objet construit à partir des données à l'instant T
Ce principe nécessite toutefois de faire attention à certains champs qui, par définition, peuvent varier constamment (ex. résultats de calculs...) et fausser la comparaison si on ne prend pas la peine de les exclure du backup et de l'ob.jet récupéré.
Copie / colle ce bout de code dans ton traitement de texte favori et lance le dans le navigateur. Normalement il se suffit à lui-même et n'a besoin d'aucune librairie ou ressource extérieure.
Test OK sur Firefox.
Attention au traitemen du "reset" et de l'événement qui lui est associé (cf. commentaire dans le code).
Absence de bug(s) non garantie Smiley ravi .
a écrit :
Mon idée : une boucle qui récupérerait la valeur de chaque élément du formulaire au chargement
de la page (stockage dans un tableau ?) et comparerait ces valeurs avec celles contenues
dans ces mêmes champs au onclick du reset ?


L'idée est intuitivement bonne. Mais en réalité en grande partie ça existe déja avec les propriétés defaultValue des différents champs.
Merci sepecat de l'effort considérable fait en me donnant tout ce code...
Je vais le décortiquer pour comprendre mais ça fait trop de nouvelles commandes
d'un coup pour moi (JSON.stringify(getBean()), et beaucoup de fonctions imbriquées
les unes dans les autres ! je te rappelle que je débute Smiley smile

Je l'ai testé en tous cas et effectivement il faudrait que je modifie la partie reset mais
cela me semble trop compliqué pour moi si je ne comprends pas certaines parties...

J'avais pensé que ce serait plus simple en fait...

Pour ne pas griller les étapes et comprendre ce que j'écris plutôt que de faire
un bête copier/coller, j'ai écris cela :


<body onload="init()">
    Nom : <input type="text" id="test" value="EXEMPLE">
    <button type="button" onclick="valider()">Valider</button>

    <script>
        function init() {
            x = document.getElementById("test").defaultValue; 
        } 

        function valider() {
            var y = document.getElementById("test").value;
            if (y!=x) {
                alertes.style.display = "block";
            }
            else {
                document.location.href="clients.php";
            }
        }
    </script>

</body>


Question :
- Pourquoi si je mets var devant x dans function init() j'ai une erreur dans mon code ?
(ReferenceError: x is not defined)

- Si je pars sur cette structure, il faut ensuite entrer tous les éléments de mon
formulaire à l'init() dans des variables que je stocke dans un tableau que je compare
aux variables contenues dans le formulaire au clic du bouton annuler ?

Dès qu'une variable est différente, on sort de la boucle pour afficher le message ?

Je comprends un peu le principe si c'est ça qu'il faut faire, mais de là à l'écrire,
ça va être chaud !

Les "value" seront alimentées par php et les données contenues dans ma base de données...
Modifié par etienne69 (21 Jun 2016 - 19:46)
etienne69 a écrit :
Merci sepecat de l'effort considérable fait en me donnant tout ce code...
Je vais le décortiquer pour comprendre mais ça fait trop de nouvelles commandes
d'un coup pour moi (JSON.stringify(getBean()), et beaucoup de fonctions imbriquées
les unes dans les autres ! je te rappelle que je débute Smiley smile
Je l'ai testé en tous cas et effectivement il faudrait que je modifie la partie reset mais
cela me semble trop compliqué pour moi si je ne comprends pas certaines parties...
J'avais pensé que ce serait plus simple en fait...
Pour ne pas griller les étapes et comprendre ce que j'écris plutôt que de faire
un bête copier/coller, j'ai écris cela :

&lt;body onload="init()"&gt;
    Nom : &lt;input type="text" id="test" value="EXEMPLE"&gt;
    &lt;button type="button" onclick="valider()"&gt;Valider&lt;/button&gt;

    &lt;script&gt;
        function init() {
            x = document.getElementById("test").defaultValue; 
        } 

        function valider() {
            var y = document.getElementById("test").value;
            if (y!=x) {
                alertes.style.display = "block";
            }
            else {
                document.location.href="clients.php";
            }
        }
    &lt;/script&gt;

&lt;/body&gt;

Question :
- Pourquoi si je mets var devant x dans function init() j'ai une erreur dans mon code ?
(ReferenceError: x is not defined)
- Si je pars sur cette structure, il faut ensuite entrer tous les éléments de mon
formulaire à l'init() dans des variables que je stocke dans un tableau que je compare
aux variables contenues dans le formulaire au clic du bouton annuler ?
Dès qu'une variable est différente, on sort de la boucle pour afficher le message ?
Je comprends un peu le principe si c'est ça qu'il faut faire, mais de là à l'écrire,
ça va être chaud !
Les "value" seront alimentées par php et les données contenues dans ma base de données...

Si tu es totalement débutant en javascript, la démarche la plus adaptée serait de :
- recopier le code dans un éditeur de texte
- charger la page dans un navigateur web
- appeler l'éditeur de code (généralement touche F12) et passer en mode pas à pas pour suivre l'avancement / exécution de chaque fonction lorsque tu modifie une valeur
Comme je l'indiquais en préambule, javascript n'est pas mon univers actuel et je devrais m'y plonger le moment venu pour mon générateur HTML, mais c'est encore prématuré.
Ce que je peux te fournir, par contre, c'est la façon dont fonctionne le programme, ses principes et points appelant une attention particulière :
a) principe
Une fois la page HTML chargée (événement onload de la balise BODY), la fonction init est appelée afin de :
- sauvevargder au format JSon (JSON.stringify) le bean (objet mémoire) stockant la valeur de chaque champ de saisie (récupération du bean = méthode getBean)
- appeler la méthode check pour initialiser le statut de la zone d'information
- installer les gestionnaires d'événement (méthode installHandlers) qui détecteront chaque changement sur les champs de saisie
b) méthode getBean()
Comme indiqué supra, crée un objet en mémoire (bean) et lui associe une propriété (nom / valeur) correspondant à chaque champ du formulaire.
Le bean est retourné par la fonction (instruction return).
c) méthode installHandlers()
En HTML, on peut déclarer des attributs onXXXX (ex. "onchange") au niveau des balises et indiquer un nom de fonction javascript devant être appelé lorsque cet événement se déclenche.
On peut également garder le code HTML "propre", c'est à dire sans attributs onXXXX et déclarer ces gestionnaires d'événement via javascript.
C'est ce qui est fait ici.
onfocus = se déclenche lorsque le champ de saisie reçoit la focalisation claiver
onchange = se déclenche lorsque la donnée dans le champ est modifiée
on blur = se déclenche lorsque le champ de saisie perd la focalisation clavier
etc.
Si j'en crois la litérature disponible sur le sujet, il est conseillé de procéder ainsi plutôt que de mettre des attributs onXXXX sur les balises HTML... mais pour être franc, c'est plutôt cette dernière forme qui est la plus utilisée sur les sites web.
d) méthode isUpdated()
Cette méthode va récupérer au format JSon (JSON.stringify) le nouveau bean correspondant aux valeurs présentes dans les champs de saisie à un instant T. Ce bean va ensuite être comparé à celui qui a été précédemment créé par la méthode init() et stocké dans la variable globale backup. Si les deux chaînes au format JSon sont égales, la fonction retourne la valeur booléenne false. Dans le cas contraire, la valeur booléenne true est retournée et indique, lors de la mise à jour du statut, que les données sont différentes par rapport à celles initialement placées dans les champs de saisie lors de l'affichage du formulaire.
A noter que le code que je t'ai fourni n'est pas optimisé. Il aurait été préférable, par exemple, de ne pas utiliser une variable locale text et de procéder par comparaison directe :
return backup !== JSON.stringify(getBean());

Cette instruction se lit de droite à gauche et signifie :
- récupère le bean actuel (getBean)
- convertit le au format JSon (JSON.stringify)
- compare le résultat avec la variable backup
- retourne true si différent / false si identique
e) méthode check()
- récupère le bloc HTML dédié à l'affichage du statut (document.getElementById('status'))
- teste si les données ont été modifiées (if (isUpdated()))
- change la classe CSS du bloc (element.className = 'XXXX';) pour indiquer le statut actuel
- change le texte du bloc (element.innerHTML = 'XXXX';) pour ce même statut
f) méthode performReset()
Cette méthode est un contournement, c'est à dire qu'elle n'est pas strictement requise par la logique qui précède mais est rendue indispensable par le comportement de javascript et/ou du navigateur.
En effet, lors des premiers tests, bien que le check soit censé être appelé sur le clic du bouton reset, la mise à jour n'intervenait qu'au deuxième clic sur ledit bouton.
Après recherche sur les forums, il est apparu que l'ordre d'exécution effectif était, par défaut :
- exécution de la méthode javascript
- réinitialisation des champs de saisie par le formulaire
Ordre inverse, donc, de celui auquel on aurait pu raisonnablement s'attendre...
Pour supprimer cet effet, le clic sur le bouton déclence la méthode, qui effectue :
- une réinitialisation forcée du formulaire (element.reset();)
- une exécution de la méthode d'actualisation du statut (check();)
Voilà donc pour le mode de fonctionnement de ce formulaire.
Avant toute chose, je t'invite à t'entraîner sur de petites fonctions javascript via les tutoriels en ligne, afin de bien comprendre ce qu'est une variable / une fonction / une valeur de retour.
Ensuite exécute ce code via l'outil de trace, comme indiqué supra, afin de voir, étape par étape, ce qui se passe.
N'étant pas un cador en javascript, je laisse les intervenants plus qualifiés sur ce langage fournir toutes les infos complémentaires qui pourraient s'avérer utiles.
Modifié par sepecat (22 Jun 2016 - 00:13)