Med något lika viktigt som en kontaktformulär vill du att den fungerar korrekt för alla besökare, även om JavaScript är utmanat. Hur hanterar du detta om du vill använda en modal (popup-blankett)? Svaret är progressiv förbättring; börja med baslinje, användbar funktionalitet; sedan öka användarupplevelsen för de som har webbläsare för att stödja den.
Innan du börjar en resa, hjälper det (oftast) att få en destination. Målet med detta projekt är att ta en standardlänk till en sida med en kontaktformulär och aktivera det formuläret för att popup på den aktuella sidan i en modal dialog.
Det finns flera anledningar till detta tillvägagångssätt:
Att skriva detta från grunden till rå JavaScript skulle vara mycket kod. Lyckligtvis för oss finns det befintliga verktyg vi kan utnyttja för att göra uppgiften enklare. Denna handledning bygger på:
För att göra den här koden så återanvändbar som möjligt skriver vi ett plug-in. Om du inte känner till att du skapar en plugin kan du få en introduktion från Jeffrey Ways artikel här på Webuts +. Den modala funktionaliteten kommer från jQuery-UIs $ .dialog.
Vi följer det vanliga mönstret för en jQuery-plugin-modul: anropa plugin-modulen på en väljare och inställningsalternativ via array. Vilka alternativ behövs? Det kommer att finnas alternativ både för modalfönstret och för plugin-modulen själv. Vi ska förvänta oss att plugin-modulen ska anropas på ett ankare och genomdriva det i koden.
$ ('a.form_link'). popUpForm (behållare: ", modal: true, resizeable: false, width: 440, title:" Webbformulär ", föreOpen: funktion (container) , onSuccess: , onError: funktion (container) );
Behållare: Så här kommer plugin-användaren att ange ID-en på formuläret på fjärrsidan. Länken själv anger sidan, men behållaralternativet låter oss hämta den relevanta delen. Detta kommer att vara endast krävs när du ringer plugin-modulen.
Modal, Ändra storlek, Bredd, Titel: Dessa alternativ kommer alla att skickas vidare till jQuery UIs $ .dialog. Värdena ovan är standard och plugin-modulen körs bra utan att någon av dessa ställs in när $ .popUpForm kallas.
beforeOpen, onSuccess, onError: Dessa är alla återuppringningar, och förväntar sig en funktion. Funktionen kommer att skickas objektet för länken som klickades som "detta" och den behållare som den länken riktas mot. Återuppringningar är utformade för att tillåta anpassad funktionalitet för användarna av en plug-in. Standard för dessa återuppringningar kommer att vara en tom funktion.
Minimikoden som krävs för att använda plug-in skulle då se ut så här:
$ ('a.form_link'). popUpForm (container: '#form_id');
Det verkar enkelt, eller hur? När du ringer ett plugin så här kallas pluginens kod med en jQuery-samling av alla DOM-elementen som matchar väljaren, som kommer att finnas tillgänglig i specialvariabeln 'this'.
De flesta jQuery-plugins följer ett mycket liknande mönster. De itererar över gruppen av selektörer och gör vad de än gör. Jag har en grundläggande plug-in "skiss" som jag vanligtvis arbetar med, och den passar in här snyggt. Detta skulle vara början på din plugin-fil, popUpForm.jquery.js.
(funktion ($) $ .fn.popUpForm = funktion (alternativ) // Standard och alternativ var standardvärden = behållare: ", modal: true, resizeable: false, width: 440, title:" Website Form " : funktion (container) , påSuccess: funktion (behållare) , onError: funktion (container) ; var uppts = $ .extend (, standardvärden, alternativ); self.each // Det verkliga arbetet händer här. // Inom ramen för denna funktion hänvisas det här till ett enda // DOM-element i jQuery-samlingen (inte en jQuery obj));) (jQuery);
Koden är invecklad i en självkörande funktion, och lägger sig till jQuery med namnet $ .fn namn. Identifieraren som följer $ .fn är det metodnamn du ska använda för att åberopa det.
Vi följer också bra kodningspraxis genom att uttryckligen överföra i jQuery-variabeln. Detta kommer att hindra oss från att komma i problem om plugin-modulen används på en sida med andra JavaScript-ramar, varav vissa använder $ som en variabel.
Därefter skapas en rad standardvärden, och dessa standardvärden kommer att användas om de inte definieras när plugin-modulen heter. Linjen som följer efter standardinställningarna kombinerar de överförda alternativen med standardvärdena och lagrar dem alla i optisk matris.
Slutligen skapas en slinga för att iterera över jQuery-samlingen identifierad av väljaren när plug-in heter ... Medan chansen är i de flesta situationer kommer det att vara ett enda objekt (ett ankare), det kommer fortfarande att hantera flera länkar med en enda samtal - förutsatt att de alla fyller samma blankett.
En Viktig sak att förstå är att värdet på den speciella variabeln 'this' ändras när vi går in i self.each-slingan; Det är en speciell jQuery-metod som är utformad för att göra DING-samlingar enklare. Återuppringningsfunktionen använder det aktuella DOM-elementets sammanhang, så variabeln "detta" avser det elementet i slingan.
I ett mycket enkelt exempel kan du se hur det här hänvisar till en jQuery-samling av jQuery-objekt i plug-in-funktionsomfånget, men inom varje loop betyder "detta" ett enda, icke-jQuery DOM-element.
Koden för de närmaste avsnitten är alla inrymda i self.each-blocket i vårt skelett. Vad gör vi nu? För varje jQuery-element som passerat, kommer det att finnas flera steg att ta:
Innan vi gör något, kommer vi dock att lägga till en rad kod inom återuppringningen, högst upp
var $ this = $ (detta);
Detta är mer än bara bekvämlighet; variabeln "this" kommer att gå utanför räckvidden i alla stängningar inom varje slinga, och vi behöver senare tillgång till det aktuella objektet. Eftersom vi nästan alltid vill ha det som ett jQuery-objekt lagrar vi det som ett.
$ .popUpForm kommer bara att fungera på ankare-taggar, och ankerkoden måste ha ett href-värde så vi vet var du ska hämta formuläret från. Om någon av dessa villkor inte är uppfyllda, kommer vi att lämna elementet ensam. Den andra raden av vår "tarm" kommer att vara:
om (! $ this.is ('a') || $ this.attr ('href') == ") return;
Vissa människor hatar flera returpunkter i en funktion, men jag har alltid funnit att ha en i början kan göra en funktion mer läsbar, i motsats till att använda en if (villkor) för att paketera resten av funktionen. Prestanda klokt, de är identiska.
Metoden $ .load har en bra funktionalitet som tillåter ett samtal att ange och ID för att bara bifoga en del av ett hämtat dokument. Skriptet bifogar inte den returnerade HTML direkt till DOM, eftersom $ .load bara skriver över, det lägger inte till.
var SRC = $ this.attr ('href') + "+ opts.container; var formDOM = $ ("") .load (SRC, function ()
Den variabla opts.container har ID för formelementet på fjärrsidan. Den andra raden laddar den här avlägsna sidan och fäster formuläret och dess innehåll till en div, vars hela är lagrad i variabelformen DOM. Observera att $ .load innehåller en återuppringning (funktionen) - vi använder formDOM inom den återuppringningen.
Inom $ .load återuppringning kommer koden att bifoga formuläret, åsidosätta ankars klickhändelse och åsidosätta inlämningshändelsen av formuläret.
Formens HTML är lagrad i formDOM-variabeln vid denna punkt, och det är enkelt att fästa den på den befintliga sidan.
$ (# PopUpHide) bifoga (formDOM).
ID #popUpHide avser en dold div som kommer att bifogas sidan av plugin-modulen. För att ge den div kommer följande rad att läggas till på toppen av plug-in. Om det redan existerar, återskapar vi det inte.
$ ("# popUpHide"). längd || $ ('') .AppendTo (' kropp ') css (.' Display', 'ingen');
Nu när formuläret är gömt säkert på vår sida, är det dags att använda ett samtal till $ .dialog-metoden för att skapa formuläret. De flesta av installationsparametrarna tas från vår plugin. Alternativet 'autoopen' är hårdkodat eftersom vi vill att dialogrutan ska öppnas när länken klickas, och inte när dialogrutan skapas.
// Skapa och lagra dialogrutan $ (opts.container) .dialog (autoOpen: false, width: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title);
Om vi slutade här skulle pluggen inte göra mycket. Länken skulle fortfarande ta oss till nästa sida. Det beteende vi önskar är att länken öppnar dialogen.
$ this.bind ('click', funktion (e) e.preventDefault (); opts.beforeOpen.call ($ this [0], opts.container); $ (opts.container) .dialog ('open') ;);
Den första raden av denna klickhanterare är mycket viktig. Det stoppar länken från att ladda den nya sidan när den klickas.
Den andra raden är vår "beforeOpen" återuppringning. Variabeln opts.beforeOpen innehåller en funktionsreferens - så mycket är uppenbart. .Call-metoden används för att aktivera funktionen på ett sätt där vi kan tillhandahålla kontext - variabeln för den funktionen. Det första argumentet som passeras blir "detta" till den uppringda funktionen.
När en funktion har tillgång till variabeln "detta" finns det några kontrakt JavaScript har med programmeraren som vi ska behålla.
För att upprätthålla det kontraktet skickar vi $ detta [0] istället för $ this. $ this [0] representerar ett enda, icke-jQuery DOM-objekt.
För att förstå detta lite bättre, föreställ dig följande återuppringningsfunktion:
opts.beforeOpen = funktion (container) // Anger värdet på länken du bara klickade på varning ('Den fjärranslutna sidan är' + this.href); // Ger den id-behållare som tilldelats denna länkvarning ('och behållaren är' + behållare);
Länkklicket är inte det enda standardbeteendet som åsidosätts. Vi vill också att formuläret ska skickas via AJAX, så det normala formuläret måste vara förhindrat och nytt beteende kodas.
$ (opts.container) .bind ('submit', funktion (e) e.preventDefault (); ajaxSubmit (););
Återigen använder vi preventDefault () för att stoppa händelsen, och i så fall lägger till en ny funktion för att hantera formulärinsändningen. AjaxSubmit () -koden kan gå direkt i återuppringningen, men den har flyttats till en ny funktion för läsbarhet.
Denna funktion skulle läggas omedelbart efter slutet av self.each loopen (oroa dig inte, du kommer se hela plug-in-koden i ett skott på bara en liten bit). Det tar formuläret, skickar det till ett fjärrskript och avfyrar lämpliga återuppringningar.
Det första steget är att få formen som ett jQuery-objekt och för att bestämma formens metod, antingen GET eller POST.
funktion ajaxSubmit () varform = $ (opts.container); var metod = form.attr ('metod') || 'SKAFFA SIG';
Om du kommer ihåg lagrade vi formulärets ID i opts.container. Nästa rad kontrollerar formuläret för en metod och tilldelar "GET" om ingen metod finns. Detta överensstämmer med HTML, som använder GET som standard på formulär om ingen metod anges.
Använd $ .ajax-metoden för att skicka formuläret:
$ .ajax (typ: metod, url: form.attr ('action'), data: form.serialize (), framgång: funktion () $ (opts.container) .dialog ('close'); onSuccess.call ($ this [0], opts.container);, fel: funktion () $ (opts.container) .dialog ('close'); opts.onError.call ($ this [0] .behållare); );
URL-alternativet bestäms av åtgärdsattributet för formtaggen. Uppgifterna produceras genom att använda serialiseringsmetoden på jQuery-objektet som innehåller formuläret.
Succes- och felalternativen är $ .ajax-återuppringningar, som vi använder för att ringa våra återkallelser, på samma sätt som den tidigareOpen-återuppringningen åberopades.
Vi stänger också dialogrutan för både framgångs och felhanterare.
Som en översikt, låt oss titta på koden vi har skrivit så länge som en helhet, inklusive några användbara kodkommentarer:
(funktion ($) var alog = window.console? console.log: alert; $ .fn.popUpForm = funktion (alternativ) // REQUIRE en behållare om (! options.container) alert ('Container Option Required' ); return; // Ge oss någonstans att bifoga formulär $ ("# popUpHide"). längd || $ ('') .AppendTo (' kropp ') css (.' Display', 'ingen'); // Standard och alternativ var standardvärden = behållare: ", modal: true, resizeable: false, width: 440, titel:" Website Form ", föreOpen: function (container) , onSuccess: onError: funktion (container) ; var opts = $ .extend (, standardvärden, alternativ); // "detta" inom varje loop avser den enda DOM-föremålet // av den jQuery-samling vi är närvarande fungerar på this.each (function () / * Vi vill behålla värdet "detta" tillgängligt för $ .load * callback * / var $ this = $ (detta); / * vi vill bara bearbeta ett objekt om det är en länk och * har ett href-värde * / om (! $ this.is ('a') || $ this.attr ('href') == ") return; / * För en $ .load ), parametern är url följt av * ID-väljaren för sektionen på sidan för att fånga * / var SRC = $ this.attr ('href') + "+ opts.container; / * händelsebindningen är klar i samtalet om * formuläret inte laddas eller användaren klickar på länken innan * modal är klar * / var formDOM = $ ("") .load (SRC, function () // Lägg till sidan $ ('# popUpHide'). append (formDOM); // Skapa och lagra dialogrutan $ (opts.container) .dialog (autoOpen: false , bredd: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title); / * stoppar det normala formuläret inlämnandet, måste komma efter * skapa dialogrutan annars finns inte formuläret * än att lägga en händelsehanterare till * / $ (opts.container) .bind ("submit", funktion (e) e.preventDefault (); ajaxSubmit ($ this [0]);); // skapa en bindande för länken skickad till plug-in $ this.bind ("click", funktionen (e) e.preventDefault (); opts.beforeOpen.call ($ this [0], opts.container); .container) .dialog ('open');););; funktion ajaxSubmit (anchorObj) console.log (anchorObj); var form = $ (opts.container); var metod = form.attr 'meta'), data: form.serialize (), framgång: funktion () $ (opts.container) .dialog ('close'); opts.onSuccess.call (anchorObj, opts.container ); , fel: funktion () opts.onError.call (anchorObj, opts.container); ); ) (jQuery);
Den här koden ska alla sparas i en fil som heter popUpForm.jquery.js
Det första steget i plugin-användningen skulle vara att inkludera alla nödvändiga beroenden på din HTML-sida. Personligen föredrar jag att använda Google CDN. Filerna som finns på en separat domän kan hjälpa sidan att ladda hastighet och servrarna är snabba. Det ökar också chansen att en besökare redan har dessa filer cachade.
Lägg till följande i huvudet i HTML-dokumentet:
Main.css-filen är för våra sajtspecifika stilar, allting är från Googles CDN. Observera att du även kan använda jQuery-UI-teman från CDN på det här sättet.
Kom ihåg att vi bara vill använda plugin-modulen på länkar som går till en formulärsida. I online-demo finns formulären i form.html, och endast två länkar går till den sidan.
Samtalen är inslagna i ett dokument. Redan blockera så vi kan vara säkra på att ankarelementen finns innan vi försöker agera på dem. Det andra samtalet, $ ('survey a') är ett exempel på det minsta belopp som behövs för att använda vår nya plugin. I det första exemplet ställs en återuppringning för både onSuccess och onError.
Om du har fått det här långt, och du skapade exempelformulär och en sida för att ringa dem från, så märker du att formuläret i modalet är förmodligen bra, fult. Modalet är inte dåligt, för vi använder ett jQuery-UI-tema. Men formuläret inom modal är mestadels ostylat, så vi borde göra en del ansträngningar för att göra det nätt.
Det finns några saker att tänka på när du skapar stilar för användning i en jQuery-UI-modal:
Med hjälp av dessa små bitar av information kan vi börja använda stiler till formuläret i modal. Först ger vi modal en bakgrundsfärg som vi är nöjda med, och ändrar också teckensnittet för titelfältet.
.ui-dialog (bakgrund: rgb (237 237 237); typsnitt: 11px verdana, arial, sans-serif; .ui-dialog .ui-dialogruta-titellinjen font: small-caps bold 24px Georgia, Times, serif;
Därefter vill vi separera varje objekt i formuläret med rader. Eftersom formulärstrukturen byter h3s med divs som innehåller formelement lägger vi till följande regler:
.ui-dialog h3, .ui-dialog div border-top: 1px solid rgb (247.247.247); gränsbotten: 1px solid rgb (212,212,212); vaddering: 8px 0 12px 10px;
Och vi vill bara ha linjer mellan sektionerna, inte längst upp eller längst ner.
.ui-dialog .puForm div: sista barn gränsbotten: ingen; .ui-dialog .puForm h3: första barn border-top: none;
Låt oss inte glömma att utforma h3s och formelementen. Radion knapparna måste visas inline så att de är alla i rad.
.ui-dialog h3 font: 18px Georgia, Times, serif; marginal: 0; .ui-dialog välj, .ui-dialogtextarea, .ui-dialoginmatning bredd: 76%; display: block; .ui-dialogruta #rating input, .ui-dialogruta #rating etikett display: inline; bredd: auto;
Kom ihåg att dessa stilar är specifika för det här projektet, du måste ställa dina egna formulär beroende på vilken struktur du använder. För att rikta in formulärelementen specifikt kan du antingen rikta efter ättlingar till .ui-dialogrutan, eller att utforma varje formulär individuellt, inkludera stilar som faller från det formulär-ID du har inkluderat.
Den stilade formen:
Så vad har vi verkligen gjort? Vi har tagit en vanlig länk som leder till en kontaktformulär (eller formulär) och orsakade att formuläret laddas upp i en modal dialog och skickar via ajax. För användare utan javascript händer ingenting och länkarna beter sig normalt, så vi har inte stoppat någon från att fylla i dina formulär.
Om du klickar på undersökningslänken i demo, var noga med att skicka in något. Jag lägger upp resultatet i kommentarerna för skojs skull efter en vecka eller så!