Migliora progressivamente una forma in una forma modale

Con qualcosa di importante come un modulo di contatto, si desidera che funzioni correttamente per tutti i visitatori, anche se il JavaScript è in difficoltà. Come lo gestisci se vuoi usare un modulo modale (pop-up)? La risposta è un miglioramento progressivo; iniziare con baseline, funzionalità utilizzabile; quindi aumenta l'esperienza dell'utente per coloro che dispongono di browser per supportarlo.


Step 1: Decidi gli obiettivi del progetto

Prima di iniziare qualsiasi viaggio, aiuta (la maggior parte delle volte) ad avere una destinazione. L'obiettivo di questo progetto è di ottenere un collegamento standard a una pagina contenente un modulo di contatto e abilitare tale modulo a pop-up nella pagina corrente in una finestra di dialogo modale.

Ci sono diversi motivi per questo approccio:

  • Se l'utente ha disabilitato JavaScript, vengono inviati normalmente alla pagina del modulo di contatto.
  • È necessario mantenere solo una versione del modulo.
  • Il contenuto aggiuntivo (il modulo) può essere caricato in modo asincrono.

Passaggio 2: elenca gli strumenti

Scrivere questo da zero in JavaScript grezzo sarebbe un sacco di codice. Fortunatamente per noi, ci sono strumenti esistenti che possiamo sfruttare per semplificare il compito. Questo tutorial si basa su:

  • jQuery
  • Interfaccia utente di jQuery
  • jQuery UI Stylesheets (CSS)

Per rendere questo codice il più possibile riutilizzabile, scriveremo un plug-in. Se non hai familiarità con la creazione di un plug-in, puoi ottenere un'introduzione dall'articolo di Jeffrey Way qui su Nettuts +. La funzionalità modale verrà dal $ .dialog di jQuery-UI.


Passaggio 3: progettare l'interfaccia plug-in

Seguiremo il modello normale per un plug-in jQuery: chiamando il plug-in su un selettore e impostando le opzioni tramite array. Quali opzioni sono necessarie? Ci saranno opzioni sia per la finestra modale che per il plug-in stesso. Ci aspettiamo che il plug-in venga chiamato su un'ancora e lo applichiamo nel codice.

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

Esaminando le opzioni

Contenitore: In questo modo l'utente del plug-in specificherà l'ID del modulo sulla pagina remota. Il link stesso specifica la pagina, ma l'opzione contenitore ci consentirà di recuperare la parte pertinente. Questo sarà il solo opzione richiesta quando si chiama il plug-in.

Modale, ridimensionabile, larghezza, titolo: Queste opzioni verranno passate insieme al $ .dialog dell'interfaccia utente di jQuery. I valori sopra sono valori predefiniti e il plug-in funzionerà correttamente senza che nessuno di questi sia impostato quando viene chiamato $ .popUpForm.

beforeOpen, onSuccess, onError: Queste sono tutte richiamate e si aspettano una funzione. La funzione verrà passata all'oggetto per il collegamento su cui è stato fatto clic come 'this' e al container a cui è indirizzato quel collegamento. Le callback sono progettate per consentire funzionalità personalizzate per gli utenti di un plug-in. L'impostazione predefinita per questi callback sarà una funzione vuota.

Il codice minimo richiesto per utilizzare il plug-in sarebbe quindi simile a questo:

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

Sembra semplice, vero? Quando chiami un plug-in come questo, il codice del plug-in viene chiamato con una raccolta jQuery di tutti gli elementi DOM che corrispondono al selettore, che sarà disponibile nella variabile speciale 'this'.


Step 4: Lo scheletro del plug-in

La maggior parte dei plug-in di jQuery seguono uno schema molto simile. Loro iterano sul gruppo di selettori e fanno qualunque cosa essi facciano. Ho un "profilo" di plug-in di base da cui generalmente lavoro e che si adatta bene qui. Questo sarebbe l'inizio del tuo file plug-in, popUpForm.jquery.js.

 (function ($) $ .fn.popUpForm = function (options) // Defaults e options var defaults = container: ", modal: true, resizeable: false, width: 440, titolo: 'Website Form', beforeOpen : function (container) , onSuccess: function (container) , onError: function (container) ; var opts = $ .extend (, defaults, options); self.each (function ()  // Il REAL WORK si verifica qui. // Nell'ambito di questa funzione 'this' fa riferimento a un singolo elemento // DOM all'interno della raccolta jQuery (non a jQuery obj));) (jQuery);

Il codice è avvolto in una funzione autoeseguibile e si aggiunge a jQuery usando lo spazio dei nomi $ .fn. L'identificatore che segue $ .fn è il nome del metodo che userai per invocarlo.

Seguiamo anche buone pratiche di codifica passando esplicitamente nella variabile jQuery. Questo ci impedirà di metterci nei guai se il plug-in è usato su una pagina con altri framework JavaScript, alcuni dei quali usano $ come variabile.

Successivamente, viene creata una matrice di valori predefiniti e questi valori predefiniti verranno utilizzati se non vengono definiti quando viene chiamato il plug-in. La riga immediatamente successiva all'array defaults unisce le opzioni passate con i valori di default e le archivia tutte nell'array opts.

Infine, viene creato un loop per iterare sulla collezione jQuery identificata dal selettore quando viene chiamato il plug-in ... Anche se nella maggior parte dei casi è probabile che sarà un singolo oggetto (un'ancora), gestirà ancora più collegamenti con un singolo chiamata - assumendo che tutti caricino lo stesso modulo.

Un importante cosa da capire è che il valore della variabile speciale 'this' cambia quando entriamo nel ciclo self.each; è un metodo jQuery speciale progettato per rendere più facili le sequenze di loop DOM. La funzione di callback usa il contesto dell'elemento DOM corrente, quindi la variabile 'this' si riferisce a quell'elemento all'interno del ciclo.

Puoi vedere in un esempio molto semplice come "questo" si riferisce ad una collezione jQuery di oggetti jQuery nell'ambito della funzione plug-in, ma all'interno di ogni ciclo, "questo" si riferisce ad un elemento DOM non jQuery singolo.


Passaggio 5: iniziare il coraggio

Il codice per le prossime sezioni è tutto contenuto nel blocco self.each del nostro scheletro. Cosa facciamo adesso? Per ogni elemento jQuery passato, ci saranno diversi passaggi da eseguire:

  • Assicurati che sia un link e che vada da qualche parte
  • Recupera la parte della pagina remota specificata
  • Collega il modulo remoto alla pagina e crea una finestra di dialogo nascosta
  • Ruba il link in modo da creare il nostro pop-up
  • Gestire i moduli inviati allo stile AJAX

Prima di fare qualsiasi cosa, tuttavia, aggiungeremo una riga di codice all'interno del callback, in alto

 var $ this = $ (this);

Questo è più che semplice convenienza; la variabile 'this' andrà al di fuori del campo di applicazione in tutte le chiusure all'interno di ciascun ciclo e avremo bisogno di accedere all'oggetto corrente in seguito. Dal momento che quasi sempre lo vogliamo come oggetto jQuery, lo stiamo memorizzando come uno.


Passaggio 6: assicurarsi che l'elemento sia valido

$ .popUpForm funzionerà solo su tag di ancoraggio, e il tag di ancoraggio deve avere un valore href in modo da sapere da dove recuperare il modulo. Se una di queste condizioni non viene soddisfatta, lasceremo l'elemento da solo. La seconda linea delle nostre "budella" sarà:

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

Alcune persone odiano più punti di ritorno in una funzione, ma ho sempre trovato che uno all'inizio può rendere una funzione più leggibile, invece di usare un if (condizione) per avvolgere il resto della funzione. Per quanto riguarda le prestazioni, sono identici.


Passaggio 7: recuperare il da dalla pagina remota

Il metodo $ .load ha una bella funzionalità che consente a una chiamata di specificare e ID di allegare solo parte di un documento recuperato. Lo script non collegherà l'HTML restituito direttamente al DOM, poiché $ .load sovrascrive solo, non aggiunge.

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

La variabile opts.container ha l'ID dell'elemento modulo sulla pagina remota. La seconda riga carica questa pagina remota e allega il modulo e il suo contenuto a un div, la cui totalità è memorizzata nella variabile formDOM. Si noti che $ .load include un callback (la funzione) - useremo formDOM all'interno di tale callback.


Passaggio 8: collegare l'HTML e creare la finestra di dialogo

All'interno del callback $ .load, il codice allegherà il modulo, sostituirà l'evento click dell'ancora e sostituirà l'evento di invio del modulo.

L'HTML del modulo è memorizzato nella variabile formDOM a questo punto e collegarlo alla pagina esistente è facile.

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

L'id #popUpHide si riferisce a un div nascosto che verrà allegato alla pagina dal plug-in. Per fornire tale div, verrà aggiunta la seguente riga nella parte superiore del plug-in. Se esiste già, non lo ricreamo.

 $ ("# popUpHide"). lunghezza || $ ('
') .AppendTo (' body ') css (.' Display', 'none');

Ora che il modulo è nascosto in sicurezza sulla nostra pagina, è ora di usare una chiamata al metodo $ .dialog per creare il modulo. La maggior parte dei parametri di impostazione sono presi dal nostro plug-in. L'opzione 'autoopen' è hard coded poiché vogliamo che la finestra di dialogo si apra quando viene cliccato il link, e non quando la finestra di dialogo è stata creata.

 // Crea e memorizza la finestra di dialogo $ (opts.container) .dialog (autoOpen: false, width: opts.width, modal: opts.modal, ridimensionabile: opts.resizeable, titolo: opts.title);

Passaggio 9: Sovrascrivi la gestione degli eventi di default

Se ci fermassimo qui, il plug-in non farebbe molto. Il collegamento ci porterebbe ancora alla pagina successiva. Il comportamento che desideriamo è che il link apra la finestra di dialogo.

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

La prima riga di questo gestore di clic è molto importante. Interrompe il collegamento dal caricamento della nuova pagina quando viene fatto clic.

La seconda riga è il nostro callback 'beforeOpen'. La variabile opts.beforeOpen contiene un riferimento alla funzione, che è ovvio. Il metodo .call è usato per invocare la funzione in un modo in cui possiamo fornire il contesto - la variabile "this" per quella funzione. Il primo argomento passato diventa "questo" alla funzione chiamata.

Quando una funzione ha accesso alla variabile 'this' ci sono alcuni contratti che JavaScript ha con il programmatore che dovremmo mantenere.

  • La variabile "this" dovrebbe essere l'oggetto su cui agisce la funzione
  • La variabile "this" è un singolo oggetto DOM

Per mantenere quel contratto, passiamo $ this [0] invece di $ this. $ this [0] rappresenta un singolo oggetto DOM non jQuery.

Per aiutare a capire questo un po 'meglio, immagina la seguente funzione di callback:

 opts.beforeOpen = function (container) // Fornisce il valore del collegamento su cui hai appena fatto clic ('The remote page is' + this.href); // Fornisce il contenitore id assegnato a questo avviso di collegamento ('E il contenitore è' + contenitore); 

Il clic del collegamento non è l'unico comportamento predefinito da sostituire. Vogliamo anche che il modulo venga inviato tramite AJAX, quindi è necessario prevenire la normale forma di evento onsumbit e codificare un nuovo comportamento.

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

Ancora una volta, usiamo preventDefault () per interrompere l'evento, e in questo caso aggiungere una nuova funzione per gestire l'invio del modulo. Il codice ajaxSubmit () potrebbe andare direttamente nel callback, ma è stato spostato su una nuova funzione per la leggibilità.


Passaggio 10: gestire i moduli inviati, stile AJAX

Questa funzione verrebbe aggiunta immediatamente dopo la fine del ciclo self.each (non ti preoccupare, vedrai l'intero codice plug-in in un colpo solo in un attimo). Accetta il modulo, lo invia a uno script remoto e attiva le richiamate appropriate.

Il primo passo è ottenere il modulo come oggetto jQuery e determinare il metodo del modulo, GET o POST.

 function ajaxSubmit () var form = $ (opts.container); var method = form.attr ('metodo') || 'OTTENERE';

Se ricordi, abbiamo memorizzato l'ID del modulo in opts.container. La riga successiva controlla il modulo per un metodo e assegna 'GET' se non è presente alcun metodo. Questo è coerente con HTML che utilizza GET per impostazione predefinita sui moduli se non viene specificato alcun metodo.

Usa il metodo $ .ajax per inviare il modulo:

 $ .ajax (tipo: metodo, url: form.attr ('action'), data: form.serialize (), success: function () $ (opts.container) .dialog ('close'); opts. onSuccess.call ($ this [0], opts.container);, error: function () $ (opts.container) .dialog ('close'); opts.onError.call ($ this [0], opts .container););

L'opzione URL è determinata dall'attributo action del tag form. I dati vengono prodotti utilizzando il metodo serialize sull'oggetto jQuery che contiene il modulo.

Le opzioni di successo e di errore sono callback $ .ajax, che utilizziamo a nostra volta per chiamare i nostri callback, nello stesso modo in cui è stato richiamato il callback beforeOpen.

Stiamo anche chiudendo la finestra di dialogo sia per il successo che per i gestori degli errori.


Passaggio 11: l'intero plug-in

Come recensione, diamo un'occhiata al codice che abbiamo scritto finora nel suo insieme, inclusi alcuni utili commenti al codice:

 (function ($) var alog = window.console? console.log: alert; $ .fn.popUpForm = function (options) // RICHIEDI un contenitore if (! options.container) alert ('Container Option Required') ); return; // Forniscici un posto dove allegare i moduli $ ("# popUpHide"). length || $ ('
') .AppendTo (' body ') css (.' Display', 'none'); // Defaults e opzioni var defaults = container: ", modal: true, resizeable: false, width: 440, titolo: 'Website Form', beforeOpen: function (container) , onSuccess: function (container) , onError: function (container) ; var opts = $ .extend (, defaults, options); // "this" all'interno di ogni loop si riferisce al singolo elemento DOM // della collezione jQuery attualmente operando su this.each (function () / * Vogliamo mantenere il valore 'this' disponibile per $ .load * callback * / var $ this = $ (this); / * vogliamo solo elaborare un item se è un collegamento e * ha un valore href * / if (! $ this.is ('a') || $ this.attr ('href') == ") return; / * Per $ .load ( ), il parametro è l'url seguito da * il selettore ID per la sezione della pagina da prendere * / var SRC = $ this.attr ('href') + "+ opts.container; / * il binding dell'evento viene eseguito nella richiamata nel caso in cui il * modulo non si carica, o l'utente fa clic sul link prima * la modal è pronta * / var formDOM = $ ("
") .load (SRC, function () // Aggiungi alla pagina $ ('# popUpHide'). append (formDOM); // Crea e memorizza la finestra di dialogo $ (opts.container) .dialog (autoOpen: false , larghezza: opts.width, modale: opts.modal, ridimensionabile: opts.resizeable, titolo: opts.title); / * interrompe l'invio normale del modulo, deve venire dopo * creando il dialogo altrimenti il ​​modulo non esiste * ancora per mettere un gestore di eventi a * / $ (opts.container) .bind ("submit", function (e) e.preventDefault (); ajaxSubmit ($ this [0]);); // crea un vincolante per il collegamento passato al plug-in $ this.bind ("click", function (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 ( 'metodo') || 'GET'; $ .ajax (tipo: metodo, url: form.attr ('action'), data: form.serialize (), success: function () $ (opts.container) .dialog ('close'); opts.onSuccess.call (anchorObj, opts.container ); , errore: function () opts.onError.call (anchorObj, opts.container); ); ) (jQuery);

Questo codice dovrebbe essere salvato in un file chiamato popUpForm.jquery.js


Passaggio 12: impostazione del plug-in

Il primo passo nell'uso del plug-in sarebbe quello di includere tutte le dipendenze richieste sulla tua pagina HTML. Personalmente preferisco usare il CDN di Google. I file che si trovano su un dominio separato possono aiutare la velocità di caricamento della pagina e i server sono veloci. Inoltre, aumenta le possibilità che un visitatore abbia già questi file nella cache.

Nel HEAD del documento HTML, aggiungere quanto segue:

    

Il file main.css è per i nostri stili specifici del sito, tutto il resto proviene dal CDN di Google. Nota che puoi usare anche i temi dell'interfaccia utente jQuery dal CDN in questo modo.


Passaggio 13: richiamo del plug-in

Ricorda, vogliamo solo richiamare il plug-in sui link che vanno a una pagina del modulo. Nella demo online, i moduli sono contenuti in form.html e solo due link vanno a quella pagina.

 

Le chiamate sono racchiuse in un blocco document.ready in modo da poter essere sicuri che gli elementi di ancoraggio esistano prima di tentare di agire su di essi. La seconda chiamata $ ('. Survey a') è un esempio dell'importo minimo necessario per utilizzare il nostro nuovo plug-in. Il primo esempio imposta un callback per onSuccess e onError.


Step 14: Styling the Modal

Se sei arrivato così lontano e hai creato moduli di esempio e una pagina da cui chiamarli, noterai che il modulo nel modal è probabilmente, beh, brutto. La modale non è male, perché stiamo usando un tema di jQuery-UI. Ma la forma all'interno del modal è per lo più senza stile, quindi dovremmo fare qualche sforzo per renderlo bello.

Ci sono alcune cose da tenere a mente quando si creano gli stili da usare in un modal dell'interfaccia utente jQuery:

  • La modale stessa è solo un elemento secondario dell'elemento BODY della pagina
  • I contenuti del modale sono tutti figli di un div di classe 'ui-dialog'

Usando queste piccole informazioni possiamo iniziare ad applicare gli stili al modulo nel modale. Per prima cosa diamo al modale un colore di sfondo di cui siamo soddisfatti, e inoltre modifichiamo il carattere per la barra del titolo.

 .ui-dialog background: rgb (237,237,237); font: 11px verdana, arial, sans-serif;  .ui-dialog .ui-dialog-titlebar font: small-caps bold 24px Georgia, Times, serif; 

Successivamente, vogliamo separare ogni elemento nel modulo con le linee. Poiché la struttura del modulo alterna h3s con div contenenti elementi del modulo, aggiungiamo le seguenti regole:

 .ui-dialog h3, .ui-dialog div border-top: 1px solid rgb (247,247,247); border-bottom: 1px solid rgb (212,212,212); imbottitura: 8px 0 12px 10px; 

E vogliamo solo le linee tra le sezioni, non in alto o molto in basso.

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

Non dimentichiamo di dare uno stile agli h3 e agli elementi del modulo. I pulsanti radio devono essere visualizzati in linea in modo che siano tutti in fila.

 .ui-dialog h3 font: 18px Georgia, Times, serif; margine: 0;  .ui-dialog select, .ui-dialog textarea, .ui-dialog input width: 76%; blocco di visualizzazione;  .ui-dialog #rating input, .ui-dialog #rating label display: inline; width: auto; 

Ricorda, questi stili sono specifici di questo progetto, dovrai modellare le tue forme a seconda della struttura che usi. Per indirizzare gli elementi del modulo in modo specifico, puoi scegliere come target i discendenti di .ui-dialog, o per personalizzare ogni modulo individualmente, includendo gli stili che discendono dall'ID del modulo che hai incluso.

La forma in stile:


Step 15: Conclusione

Quindi cosa abbiamo veramente fatto? Abbiamo preso un collegamento normale che porta a un modulo di contatto (o moduli) e ha causato il caricamento di tale modulo in una finestra di dialogo modale e l'invio tramite ajax. Per gli utenti senza javascript, non accade nulla e i collegamenti si comportano normalmente, quindi non abbiamo impedito a nessuno di compilare i moduli.

Se fai clic sul link del sondaggio nella demo, assicurati di inviare qualcosa. Pubblicherò i risultati nei commenti per divertimento dopo circa una settimana!