Améliorer progressivement un formulaire en un formulaire modal

Avec quelque chose d'aussi important qu'un formulaire de contact, vous voulez qu'il fonctionne correctement pour tous les visiteurs, même pour JavaScript. Comment gérez-vous cela si vous souhaitez utiliser un formulaire modal (pop-up)? La réponse est l'amélioration progressive; commencer par la base, fonctionnalité utilisable; puis augmentez l'expérience utilisateur pour ceux qui ont des navigateurs pour le supporter.


Étape 1: Décider des objectifs du projet

Avant de commencer un voyage, il est utile (la plupart du temps) d’avoir une destination. L'objectif de ce projet est de créer un lien standard vers une page contenant un formulaire de contact et de permettre à ce formulaire de s'afficher sur la page en cours dans une boîte de dialogue modale..

Il y a plusieurs raisons à cette approche:

  • Si l'utilisateur a désactivé JavaScript, il est envoyé comme d'habitude à la page du formulaire de contact..
  • Une seule version du formulaire doit être conservée.
  • Le contenu supplémentaire (le formulaire) peut être chargé de manière asynchrone.

Étape 2: Lister les outils

Écrire cela à partir de zéro en JavaScript brut serait beaucoup de code. Heureusement pour nous, il existe des outils existants que nous pouvons utiliser pour faciliter la tâche. Ce tutoriel s'appuie sur:

  • jQuery
  • jQuery UI
  • Feuilles de style jQuery UI (CSS)

Pour rendre ce code aussi réutilisable que possible, nous allons écrire un plug-in. Si vous n’êtes pas familiarisé avec la création d’un plug-in, vous pouvez vous familiariser avec l’article de Jeffrey Way sur Nettuts +. La fonctionnalité modale viendra du $ .dialog de jQuery-UI.


Étape 3: Conception de l'interface du plug-in

Nous allons suivre le schéma normal pour un plug-in jQuery: appeler le plug-in sur un sélecteur et définir les options via array. Quelles sont les options nécessaires? Il y aura des options à la fois pour la fenêtre modale et pour le plug-in lui-même. Nous allons nous attendre à ce que le plug-in soit appelé sur une ancre et à l'appliquer dans le code.

 $ ('a.form_link'). popUpForm (container: ", modal: true, redimensionnable: false, width: 440, titre: 'Website Form', beforeOpen: function (container) , onSuccess: function (container) , onError: function (conteneur) );

Examiner les options

Récipient: Voici comment l'utilisateur du plug-in spécifiera l'ID du formulaire sur la page distante. Le lien lui-même spécifie la page, mais l'option conteneur nous permettra de récupérer la partie pertinente. Ce sera le seulement option requise pour appeler le plug-in.

Modal, redimensionnable, largeur, titre: Ces options vont toutes être transmises au $ .dialog de jQuery UI. Les valeurs ci-dessus sont des valeurs par défaut et le plug-in fonctionnera parfaitement sans qu'aucune de celles-ci ne soit définie lorsque $ .popUpForm est appelé..

beforeOpen, onSuccess, onError: Ce sont tous des rappels et attendent une fonction. L'objet du lien sur lequel le lien a été cliqué sera transmis à la fonction, ainsi que le conteneur auquel ce lien est destiné. Les rappels sont conçus pour permettre une fonctionnalité personnalisée aux utilisateurs d'un plug-in. La valeur par défaut pour ces rappels sera une fonction vide.

Le code minimum requis pour utiliser le plug-in devrait alors ressembler à ceci:

 $ ('a.form_link'). popUpForm (conteneur: '#form_id');

Cela semble simple, n'est-ce pas? Lorsque vous appelez un plug-in comme celui-ci, le code du plug-in est appelé avec une collection jQuery de tous les éléments DOM correspondant au sélecteur, qui sera disponible dans la variable spéciale 'this'..


Étape 4: Le squelette du plug-in

La plupart des plug-ins jQuery suivent un schéma très similaire. Ils parcourent le groupe de sélecteurs et font ce qu'ils font. J'ai en général un "contour" de plug-in de base, et il s'intégrera bien ici. Ce serait le début de votre fichier de plug-in, popUpForm.jquery.js.

 (function ($) $ .fn.popUpForm = fonction (options) // Valeurs par défaut et options var defaults = conteneur: ", modal: true, redimensionnable: false, largeur: 440, titre:" Formulaire de site Web ", beforeOpen : fonction (conteneur) , onSuccess: fonction (conteneur) , onError: fonction (conteneur) ; var opts = $ .extend (, valeurs par défaut, options); self.each (fonction ()  // Le travail réel se passe ici. // Dans le cadre de cette fonction, "ceci" fait référence à un seul élément // DOM de la collection jQuery (et non à un objet jQuery obj)); (jQuery);

Le code est encapsulé dans une fonction à exécution automatique et s’ajoute à jQuery à l’aide de l’espace de noms $ .fn. L'identifiant suivant $ .fn est le nom de la méthode que vous utiliserez pour l'appeler..

Nous suivons également de bonnes pratiques de codage en transmettant explicitement la variable jQuery. Cela nous évitera des ennuis si le plug-in est utilisé sur une page avec d'autres frameworks JavaScript, dont certains utilisent $ comme variable..

Ensuite, un tableau de valeurs par défaut est créé et ces valeurs par défaut seront utilisées si elles ne sont pas définies lors de l'appel du plug-in. La ligne qui suit immédiatement le tableau par défaut fusionne les options passées avec les valeurs par défaut et les stocke toutes dans le tableau opts..

Enfin, une boucle est créée pour parcourir la collection jQuery identifiée par le sélecteur lorsque le plug-in est appelé… Bien qu'il y ait des chances qu'il s'agisse d'un seul élément (une ancre), il gérera toujours plusieurs liens avec un seul call - en supposant qu'ils chargent tous le même formulaire.

Un important Ce qu'il faut comprendre, c'est que la valeur de la variable spéciale 'this' change lorsque nous entrons dans la boucle self.each; c'est une méthode spéciale de jQuery conçue pour faciliter la mise en boucle des collections DOM. La fonction de rappel utilise le contexte de l'élément DOM actuel. La variable 'this' se réfère donc à cet élément de la boucle..

Vous pouvez voir dans un exemple très simple comment «this» fait référence à une collection d'objets jQuery dans l'étendue de la fonction de plug-in jQuery, mais à l'intérieur de chaque boucle, «this» fait référence à un seul élément DOM non jQuery..


Étape 5: Démarrer les tripes

Le code des sections suivantes est entièrement contenu dans le bloc self.each de notre squelette. Qu'est-ce qu'on fait maintenant? Pour chaque élément jQuery passé, il y aura plusieurs étapes à suivre:

  • Assurez-vous que c'est un lien et qu'il va quelque part
  • Récupère la partie de la page distante spécifiée
  • Attachez le formulaire distant à la page et créez une boîte de dialogue masquée pour celui-ci.
  • Volez le lien pour qu'il crée notre pop-up
  • Gérer les soumissions de formulaire à la manière AJAX

Avant de faire quoi que ce soit, cependant, nous allons ajouter une ligne de code dans le rappel, tout en haut.

 var $ this = $ (this);

C'est plus que juste la commodité; la variable 'this' sortira de la portée de toutes les fermetures de chaque boucle et nous devrons avoir besoin d'accéder à l'objet actuel plus tard. Comme nous le souhaitons presque toujours comme un objet jQuery, nous le stockons comme tel..


Étape 6: Assurez-vous que l'élément est valide

$ .popUpForm ne fonctionnera que sur les balises d'ancrage, et la balise d'ancrage doit avoir une valeur href afin que nous sachions où récupérer le formulaire. Si l'une ou l'autre de ces conditions n'est pas remplie, nous allons laisser l'élément tranquille. La deuxième ligne de nos "tripes" sera:

 if (! $ this.is ('a') || $ this.attr ('href') == ") return;

Certaines personnes détestent plusieurs points de retour dans une fonction, mais j'ai toujours constaté que le fait de créer un point au début pouvait rendre une fonction plus lisible, par opposition à l'utilisation d'un if (condition) pour envelopper le reste de la fonction. Performance sage, ils sont identiques.


Étape 7: Récupérer le à partir de la page distante

La méthode $ .load a une fonctionnalité intéressante qui permet à un appel de spécifier un identifiant afin de ne joindre qu'une partie d'un document récupéré. Le script n'attachera pas le code HTML renvoyé directement au DOM, car $ .load ne fait que remplacer, il n'est pas ajouté..

 var SRC = $ this.attr ('href') + "+ opts.container; var formDOM = $ ("
") .load (SRC, function ()

La variable opts.container a l'ID de l'élément de formulaire sur la page distante. La deuxième ligne charge cette page distante et attache le formulaire et son contenu à une div, dont l'intégralité est stockée dans la variable formDOM. Notez que $ .load inclut un callback (la fonction) - nous utiliserons formDOM dans ce callback.


Étape 8: attachez le code HTML et créez le dialogue

Dans le rappel $ .load, le code va associer le formulaire, remplacer l'événement click de l'ancre et remplacer l'événement submit du formulaire..

Le code HTML du formulaire est stocké dans la variable formDOM à ce stade et il est facile de le joindre à la page existante..

 $ ('# popUpHide'). append (formDOM);

L'identifiant #popUpHide fait référence à une div cachée qui sera attachée à la page par le plug-in. Afin de fournir cette div, la ligne suivante sera ajoutée en haut du plug-in. S'il existe déjà, nous ne le recréons pas.

 $ ("# popUpHide"). length || $ ('
') .appendTo (' body '). css (' display ',' none ');

Maintenant que le formulaire est caché en toute sécurité sur notre page, il est temps d'utiliser un appel à la méthode $ .dialog pour créer le formulaire. La plupart des paramètres de configuration sont extraits de notre plug-in. L'option 'autoopen' est codée en dur car nous voulons que la boîte de dialogue s'ouvre lorsque le lien est cliqué, et non lorsque la boîte de dialogue est créée..

 // Crée et stocke le dialogue $ (opts.container) .dialog (autoOpen: false, width: opts.width, modal: opts.modal, redimensionnable: opts.resizeable, title: opts.title);

Étape 9: Ignorer la gestion des événements par défaut

Si nous nous arrêtions ici, le plug-in ne ferait pas grand chose. Le lien nous mènerait toujours à la page suivante. Le comportement que nous souhaitons est que le lien ouvre la boîte de dialogue.

 $ this.bind ('click', fonction (e) e.preventDefault (); opts.beforeOpen.call ($ this [0], opts.container); $ (opts.container) .dialog ('open') ;);

La première ligne de ce gestionnaire de clics est très importante. Il empêche le lien de charger la nouvelle page quand il est cliqué.

La deuxième ligne est notre rappel «beforeOpen». La variable opts.beforeOpen contient une référence de fonction - cela est évident. La méthode .call est utilisée pour appeler la fonction de manière à ce que nous puissions fournir un contexte - la variable 'this' pour cette fonction. Le premier argument passé devient 'this' à la fonction appelée.

Quand une fonction a accès à la variable 'this', il y a des contrats que JavaScript a avec le programmeur que nous devrions maintenir.

  • La variable 'this' doit être l'objet sur lequel la fonction agit
  • La variable 'this' est un objet DOM unique

Afin de maintenir ce contrat, nous passons $ this [0] au lieu de $ this. $ this [0] représente un seul objet DOM non jQuery.

Pour aider à mieux comprendre ceci, imaginez la fonction de rappel suivante:

 opts.beforeOpen = function (conteneur) // Donne la valeur du lien sur lequel vous venez de cliquer alerte (La page distante est '+ this.href); // Donne le conteneur id affecté à cette alerte de lien ('Et le conteneur est' + conteneur); 

Le clic sur le lien n'est pas le seul comportement par défaut à remplacer. Nous souhaitons également que le formulaire soit soumis via AJAX. Par conséquent, il faut empêcher l'événement onsumbit de forme normale et coder un nouveau comportement..

 $ (opts.container) .bind ('submit', fonction (e) e.preventDefault (); ajaxSubmit (););

De nouveau, nous utilisons preventDefault () pour arrêter l'événement, et dans ce cas, nous ajoutons une nouvelle fonction pour gérer la soumission du formulaire. Le code ajaxSubmit () peut aller directement dans le rappel, mais il a été déplacé vers une nouvelle fonction pour plus de lisibilité..


Étape 10: Traitement des soumissions de formulaire, style AJAX

Cette fonction serait ajoutée immédiatement après la fin de la boucle self.each (ne vous inquiétez pas, vous verrez tout le code du plug-in d'un coup en un instant). Il prend le formulaire, le soumet à un script distant et déclenche les rappels appropriés..

La première étape consiste à obtenir le formulaire sous forme d'objet jQuery et à déterminer la méthode du formulaire, GET ou POST..

 fonction ajaxSubmit () var form = $ (opts.container); var method = form.attr ('method') || 'OBTENIR';

Si vous vous en souvenez, nous avons stocké l'ID du formulaire dans opts.container. La ligne suivante vérifie le formulaire pour une méthode et affecte «GET» si aucune méthode n'est présente. Ceci est cohérent avec HTML qui utilise GET par défaut sur les formulaires si aucune méthode n'est spécifiée.

Utilisez la méthode $ .ajax pour soumettre le formulaire:

 $ .ajax (type: méthode, url: form.attr ('action'), données: form.serialize (), succès: function () $ (opts.container) .dialog ('close'); opts. onSuccess.call ($ this [0], opts.container);, erreur: function () $ (opts.container) .dialog ('close'); opts.onError.call ($ this [0], opté .récipient);  );

L'option URL est déterminée à partir de l'attribut d'action de la balise de formulaire. Les données sont générées à l’aide de la méthode serialize sur l’objet jQuery contenant le formulaire..

Les options success et error sont des callbacks $ .ajax, que nous utilisons à notre tour pour appeler nos callbacks, de la même manière que le callback beforeOpen a été appelé..

Nous fermons également la boîte de dialogue pour les gestionnaires de succès et d'erreur.


Étape 11: L'ensemble du plug-in

En guise de révision, examinons le code que nous avons écrit dans son ensemble, y compris quelques commentaires de code utiles:

 (function ($) var. alog = window.console? console.log: alert; $ .fn.popUpForm = function (options) // OBLIGE un conteneur if (! options.container) alert ('Container Option Required' ); return; // Donnez-nous un endroit où joindre les formulaires $ ("# popUpHide"). length || $ ('
') .appendTo (' body '). css (' display ',' none '); // Valeurs par défaut et options var defaults = conteneur: ", modal: true, redimensionnable: false, largeur: 440, titre: 'Formulaire de site Web', beforeOpen: function (conteneur) , onSuccess: function (conteneur) , onError: function (conteneur) ; var opts = $ .extend (, valeurs par défaut, options); // Le "this" dans la boucle each fait référence à l'élément DOM unique // de la collection jQuery que nous sommes actuellement. en opérant sur this.each (function () / * Nous voulons garder la valeur 'this' disponible pour le $ .load * callback * / var $ this = $ (this); / * nous voulons seulement traiter un élément si c'est un lien et * a une valeur href * / if (! $ this.is ('a') || $ this.attr ('href') == ") return; / * Pour un $ .load ( ), le paramètre est l’url suivi de * le sélecteur d’ID pour la section de la page à saisir * / var SRC = $ this.attr ('href') + "+ opts.container; / * la liaison d’événement est effectuée dans le rappel au cas où le chargement de * form échouait ou que l'utilisateur cliquait sur le lien avant que * le modal soit prêt * / var formDOM = $ ("
") .load (SRC, function () // Ajouter à la page $ ('# popUpHide'). append (formDOM); // Créer et stocker le dialogue $ (opts.container) .dialog (autoOpen: false , width: opts.width, modal: opts.modal, redimensionnable: opts.resizeable, title: opts.title); / * arrête la soumission normale du formulaire; doit venir après * la création du dialogue sinon le formulaire n'existe pas * Pas encore de mettre un gestionnaire d’événements sur * / $ (opts.container) .bind ("submit", fonction (e) e.preventDefault (); ajaxSubmit ($ this [0]);); // créer un liaison pour le lien transmis au plug-in $ this.bind ("click", fonction (e) e.preventDefault (); opts.beforeOpen.call ($ this [0], opts.container); $ (opts .container) .dialog ('open');););); function ajaxSubmit (anchorObj) console.log (anchorObj); var form = $ (opts.container); var method = form.attr ( 'méthode' || 'GET'; $ .ajax (type: méthode, url: form.attr ('action'), données: form.serialize (), succès: function () $ (opts.container) .dialog ('close'); opts.onSuccess.call (anchorObj, opts.container ) , error: function () opts.onError.call (anchorObj, opts.container); ); ) (jQuery);

Ce code doit tous être enregistré dans un fichier nommé popUpForm.jquery.js


Étape 12: Configuration du plug-in

La première étape de l'utilisation du plug-in consiste à inclure toutes les dépendances requises sur votre page HTML. Personnellement, je préfère utiliser le CDN de Google. Les fichiers se trouvant sur un domaine distinct peuvent contribuer à la vitesse de chargement des pages et les serveurs sont rapides. De plus, cela augmente les chances qu'un visiteur ait déjà ces fichiers en cache.

Dans l'en-tête du document HTML, ajoutez les éléments suivants:

    

Le fichier main.css concerne les styles spécifiques à notre site. Tout le reste provient du CDN de Google. Notez que vous pouvez même utiliser les thèmes jQuery-UI du CDN de cette manière..


Étape 13: invocation du plug-in

N'oubliez pas que nous souhaitons uniquement appeler le plug-in sur des liens menant à une page de formulaire. Dans la démonstration en ligne, les formulaires sont contenus dans form.html, et deux liens seulement mènent à cette page..

 

Les appels sont encapsulés dans un bloc document.ready afin que nous puissions être sûrs que les éléments d'ancrage existent avant d'essayer d'agir sur eux. Le deuxième appel, $ ('. Survey a'), est un exemple du montant minimum requis pour utiliser notre nouveau plug-in. Le premier exemple définit un rappel pour onSuccess et onError.


Étape 14: Mise en forme du modal

Si vous en êtes à ce stade et que vous avez créé des exemples de formulaires et une page pour les appeler, vous remarquerez que le formulaire dans le modal est probablement, eh bien, moche. Le modal en lui-même n'est pas mauvais, car nous utilisons un thème jQuery-UI. Mais la forme à l'intérieur du modal est en grande partie non stylée, nous devrions donc faire quelques efforts pour le rendre joli.

Il convient de garder à l’esprit certaines choses lors de la création de styles à utiliser dans un modal jQuery-UI:

  • Le modal lui-même n'est qu'un enfant de l'élément BODY de la page
  • Les contenus du modal sont tous des enfants d'un div de la classe 'ui-dialog'

En utilisant ces petites informations, nous pouvons commencer à appliquer des styles à la fiche dans le modal. Tout d'abord, nous donnons au modal une couleur de fond qui nous convient, puis nous modifions également la police de caractères de la barre de titre..

 .dialogue ui arrière-plan: rgb (237,237,237); police: 11px verdana, arial, sans-serif;  .ui-dialog .ui-dialog-titlebar font: gras minuscules 24px Georgia, Times, serif; 

Ensuite, nous voulons séparer chaque élément du formulaire avec des lignes. Puisque la structure de formulaire alterne h3s avec des div contenant des éléments de formulaire, nous ajoutons les règles suivantes:

 .ui-dialogue h3, .ui-dialogue div border-top: 1px solide rgb (247,247,247); bordure-bas: 1px solide rgb (212, 212, 212); remplissage: 8px 0 12px 10px; 

Et nous voulons seulement des lignes entre les sections, pas tout en haut ou en bas.

 .ui-dialog .puForm div: last-child border-bottom: none;  .ui-dialog .puForm h3: premier-enfant border-top: none; 

N'oublions pas de styler les h3 et les éléments de formulaire. Les boutons radio doivent s’afficher en ligne pour qu’ils soient tous alignés..

 .ui-dialog h3 font: 18px Georgia, Times, serif; marge: 0;  .ui-dialog select, zone de dialogue .ui, entrée de .ui-dialog width: 76%; bloc de visualisation;  .ui-dialog #rating input, .ui-dialog #rating label display: inline; largeur: auto; 

N'oubliez pas que ces styles sont spécifiques à ce projet, vous devrez donc personnaliser vos propres formulaires en fonction de la structure que vous utilisez. Pour cibler spécifiquement les éléments de formulaire, vous pouvez cibler les descendants de .ui-dialog ou attribuer un style à chaque formulaire individuellement, en incluant des styles descendant de l'ID de formulaire que vous avez inclus..

La forme stylée:


Étape 15: Conclusion

Alors qu'est-ce qu'on a vraiment fait? Nous avons pris un lien normal menant à un formulaire de contact (ou formulaires) et l'avons chargé dans un dialogue modal, puis soumis via ajax. Pour les utilisateurs sans javascript, rien ne se passe et les liens se comportent normalement. Nous n'avons donc empêché personne de remplir vos formulaires..

Si vous cliquez sur le lien de sondage dans la démo, assurez-vous de soumettre quelque chose. Je posterai les résultats dans les commentaires pour m'amuser après environ une semaine!