Skip to content

Commit d348f55

Browse files
committed
Merge pull request #6 from feugy/master
Et un post, un !
2 parents 4ac1263 + 5f50e39 commit d348f55

File tree

1 file changed

+151
-0
lines changed

1 file changed

+151
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
---
2+
layout: post
3+
title: Les subtilités des formulaires de login en JS
4+
author: feugy
5+
tags: [javascript, login, formulaire, chrome, firefox, jQuery]
6+
published: false
7+
---
8+
9+
Quoi de plus simple qu'un formulaire de login ?
10+
En HTML, certes, mais lorsque qu'on parle d'application riche en javascript, c'est une autre histoire.
11+
12+
Si vous ne comprenez pas pourquoi le navigateur ne retient pas vos logins, ne propose pas le mot de passe associé, ou ne réagit pas à la touche entrée, lisez la suite !
13+
14+
Cet article est le résultat de **l'étude empirique du comportement des navigateurs**.
15+
Il est donc sujet à caution: tout dépends de votre navigateur et de sa version.
16+
J'espère simplement vous aider à comprendre et éviter les principaux chausse-trappes, sans avoir recours à l'installation d'un plugin :)
17+
18+
19+
## Mais ce n'est qu'un formulaire !
20+
21+
Hélas non... Les formulaires de login (un champs <tt>text</tt> + un champ <tt>password</tt>) sont détectés par le navigateur, qui va les traiter différemment des autres champs textuels.
22+
23+
C'est là le premier problème :
24+
>**Le formulaire doit être présent dans le DOM au chargement de la page.**
25+
26+
Oui, vous avez compris : si vous construisez votre formulaire directement en JS, ou si vous utilisez un template que vous accrochez une fois la page chargée, votre formulaire sera ignoré par certains navigateurs.
27+
28+
L'astuce que j'utilise consiste à:
29+
30+
* inclure le formulaire de login dans ma page index.html
31+
* le masquer avec un style CSS <tt>display:none</tt>.
32+
* le déplacer par la suite en javascript là où il doit apparaitre dans le rendu
33+
34+
**index.html**
35+
36+
<div id="loginStock" style="display:none">
37+
<form id="formLogin">
38+
<input type="text" name="username"/>
39+
<input type="password" name="password"/>
40+
</form>
41+
</div>
42+
43+
**login.js**
44+
45+
$('.loginContainer', this.element).append($('#loginStock > *'));
46+
47+
48+
## Dois-je mettre un champ de type <tt>submit</tt> ?
49+
50+
S'il n'y a que les champs de type <tt>text</tt> et <tt>password</tt>, le navigateur ne retiendra pas le mot de passe.
51+
52+
>**Le champ de type <tt>submit</tt> est indispensable.**
53+
54+
Même s'il est masqué (le bouton n'est pas encore bien skinnable, même en CSS 3), il doit être présent.
55+
56+
**index.html**
57+
58+
<div id="loginStock" style="display:none">
59+
<form id="formLogin">
60+
<input type="text" name="username"/>
61+
<input type="password" name="password"/>
62+
<input type="submit" style="display:none"/>
63+
</form>
64+
<a href="#" class="submit"></a>
65+
</div>
66+
67+
**login.js**
68+
69+
$('.loginContainer .submit').click(function(event){
70+
// Déclenche la soumission du formulaire.
71+
$('#formLogin').submit();
72+
// Annule la propagation du Click sur le lien, pas de la soumission du formulaire !
73+
return false;
74+
}).button({label:'log in !'});
75+
76+
77+
## Doit-on stopper la propagation de l'événement <tt>submit</tt> ?
78+
79+
Le problème se pose si vous ne réalisez pas un "vrai" POST lorsque l'utilisateur déclenche la soumission du formulaire.
80+
Dans la majorité des applications riches, l'authentification se fait via une API, et donc un appel Ajax.
81+
82+
On se branche alors sur l'événement submit, et on annule sa propagation.
83+
84+
Seulement, en stoppant l'événement de soumission, le navigateur ne retient pas le login et le mot de passe.
85+
86+
>**Pour que le navigateur mémorise le couple login/mot de passe, l'événement <tt>submit</tt> doit se propager.**
87+
88+
Cela implique:
89+
90+
* que le formulaire pointe sur une véritable url, sinon, une vilaine 404 apparaitra dans votre console
91+
* que le résultat de la soumission soit routée dans une iframe (désolé pour les puristes :)), sans quoi, toute la page se rechargera
92+
93+
Donc généralement, mon service web propose une API POST qui ne fait rien et renvoie une page vide, et j'utilise une iframe masquée.
94+
95+
**index.html**
96+
97+
<div id="loginStock" style="display:none" method="post" action="/api/login/noop" target="postFrame">
98+
<form id="formLogin">
99+
<input autofocus="autofocus" type="text" name="username"/>
100+
<input autocomplete="on" type="password" name="password"/>
101+
<input type="submit" style="display:none"/>
102+
</form>
103+
<a href="#" class="submit"></a>
104+
</div>
105+
<iframe name="postFrame" class="hidden"></iframe>
106+
107+
**login.js**
108+
109+
$('#formLogin').submit(function(event){
110+
// Ici mon appel ajax, et surtout, ne pas renvoyer false ni invoquer event.stopPropagation().
111+
});
112+
113+
114+
### Une petite astuce : lorsque l'authentification échoue.
115+
116+
A partir du moment où l'événement <tt>submit</tt> est propagé, et qu'une réponse HTTP est reçue, le navigateur conservera bien le mot de passe et le login.
117+
118+
Mais nous pouvons tirer parti de ce comportement, lorsque l'authentification échoue, par exemple si l'utilisateur est inconnu, ou le mot de passe erroné.
119+
120+
Ainsi, mon appel Ajax est toujours **synchrone** (parfois ça sert !), et s'il échoue, alors dans ce cas, j'annule la propagation de l'événement <tt>submit</tt> pour ne pas conserver les mauvais identifiants.
121+
122+
123+
124+
## La touche entrée ne soumet pas toujours mon formulaire...
125+
126+
Hélas oui, c'est un comportement étrange de certains navigateurs : un formulaire sera automatiquement soumit si on appuie sur entrée dans un de ses champs, sauf si le bouton submit est invisible !
127+
128+
Donc il faut eviter le <tt>display:none</tt> ou le <tt>visibility:hidden</tt> sur le champ.
129+
Personnellement, je lui affecte une taille de zéro. Vous pouvez aussi le positionner en absolu, en dehors de la zone visible, ou le mettre en dessous d'un autre élément avec le <tt>z-index</tt>
130+
131+
**index.html**
132+
133+
<div id="loginStock" style="display:none" method="post" action="/api/login/noop" target="postFrame">
134+
<form id="formLogin">
135+
<input autofocus="autofocus" type="text" name="username"/>
136+
<input autocomplete="on" type="password" name="password"/>
137+
<input type="submit" style="width:0; height:0; border:0; padding:0"/>
138+
</form>
139+
<a href="#" class="submit"></a>
140+
</div>
141+
142+
143+
## Conclusion
144+
145+
Vous voyez ? quand je vous disais que ce n'était pas trivial...
146+
147+
Mais maintenant, vous avez toutes les clefs pour faire des formulaires qui exploitent le cache de login/mot de passe des navigateurs.
148+
149+
Espérons que dans un futur proche, les navigateurs harmonisent un peu plus leur fonctionnement, de manière à éviter tout ces tricks...
150+
151+
Damien.

0 commit comments

Comments
 (0)