Met iets dat zo belangrijk is als een contactformulier, wilt u dat het goed werkt voor alle bezoekers, zelfs de JavaScript-uitdaging. Hoe pak je dit aan als je een modaal (pop-up) formulier wilt gebruiken? Het antwoord is een progressieve verbetering; start met baseline, bruikbare functionaliteit; verhoog vervolgens de gebruikerservaring voor degenen die browsers hebben om dit te ondersteunen.
Voordat u een reis begint, helpt het (meestal) om een bestemming te hebben. Het doel van dit project is om een standaardlink naar een pagina met een contactformulier te maken en dat formulier in te schakelen op de huidige pagina in een modaal dialoogvenster..
Er zijn verschillende redenen voor deze aanpak:
Om dit vanaf nul te schrijven in raw JavaScript zou veel code zijn. Gelukkig zijn er voor ons bestaande tools die we kunnen gebruiken om de taak eenvoudiger te maken. Deze tutorial is gebaseerd op:
Om deze code zo herbruikbaar mogelijk te maken, schrijven we een plug-in. Als u niet bekend bent met het schrijven van een plug-in, kunt u een inleiding krijgen in het artikel van Jeffrey Way hier over Nettuts +. De modale functionaliteit zal afkomstig zijn van jQuery-UI's $ .dialog.
We gaan het normale patroon voor een jQuery plug-in volgen: de plug-in op een selector bellen en opties via array instellen. Welke opties zijn nodig? Er zijn opties voor zowel het modale venster als voor de plug-in zelf. We verwachten dat de plug-in wordt aangeroepen voor een anker en dat afdwingen in de code.
$ ('a.form_link'). popUpForm (container: ", modal: true, resize: false, width: 440, title: 'Website Form', beforeOpen: function (container) , onSuccess: function (container) , onError: function (container) );
Houder: Dit is hoe de plug-in gebruiker de ID van het formulier op de externe pagina zal specificeren. De koppeling zelf geeft de pagina aan, maar met de containeroptie kunnen we het relevante deel ophalen. Dit zal het zijn enkel en alleen vereiste optie bij het aanroepen van de plug-in.
Modal, Resizeable, Width, Title: Deze opties zullen allemaal worden doorgegeven aan de $ .dialog van jQuery UI. De bovenstaande waarden zijn standaardwaarden en de plug-in werkt prima zonder dat een van deze wordt ingesteld wanneer $ .popUpForm wordt aangeroepen.
beforeOpen, onSuccess, onError: Dit zijn allemaal callbacks en verwachten een functie. De functie krijgt het object doorgegeven voor de link waarop is geklikt als 'dit' en de container waarop die link is gericht. Callbacks zijn ontworpen om aangepaste functionaliteit voor de gebruikers van een plug-in mogelijk te maken. De standaardwaarde voor deze callbacks is een lege functie.
De minimale code die vereist is om de plug-in te gebruiken, ziet er dan als volgt uit:
$ ('a.form_link'). popUpForm (container: '#form_id');
Dat lijkt eenvoudig, toch? Wanneer u een plug-in als deze belt, wordt de code van de plug-in aangeroepen met een jQuery-verzameling van alle DOM-elementen die overeenkomen met de selector, die beschikbaar is in de speciale variabele 'this'.
De meeste plug-ins van jQuery volgen een zeer vergelijkbaar patroon. Ze herhalen de groep selectors en doen wat ze ook doen. Ik heb een eenvoudige "insteek" van de plug-in waar ik over het algemeen aan werk, en het zal hier mooi in passen. Dit zou het begin zijn van je plug-in-bestand, popUpForm.jquery.js.
(function ($) $ .fn.popUpForm = function (options) // Standaardinstellingen en opties var defaults = container: ", modal: true, resizeable: false, width: 440, title: 'Website Form', beforeOpen : functie (container) , onSuccess: functie (container) , onError: functie (container) ; var opts = $ .extend (, standaardinstellingen, opties); self.each (function () // Het ECHTE WERK gebeurt hier. // In het kader van deze functie verwijst 'dit' naar een enkel // DOM-element binnen de jQuery-verzameling (geen jQuery obj));) (jQuery);
De code is verpakt in een zelf-uitvoerende functie en voegt zichzelf toe aan jQuery met de $ .fn-naamruimte. De identifier achter $ .fn is de naam van de methode die u gebruikt om deze aan te roepen.
We volgen ook goede codeermethoden door de jQuery-variabele expliciet door te geven. Dit voorkomt dat we in de problemen komen als de plug-in wordt gebruikt op een pagina met andere JavaScript-frameworks, waarvan sommige $ gebruiken als een variabele.
Vervolgens wordt een array met standaardwaarden gemaakt en deze standaardwaarden worden gebruikt als ze niet worden gedefinieerd wanneer de plug-in wordt aangeroepen. De regel die onmiddellijk volgt op de standaard array, voegt de doorgegeven opties samen met de standaardwaarden en slaat ze allemaal op in de array opts.
Ten slotte wordt er een lus gemaakt voor iteratie over de jQuery-verzameling die door de selector wordt geïdentificeerd wanneer de plug-in wordt genoemd ... Hoewel de kansen in de meeste situaties een enkel item (een anker) zijn, zal het nog steeds meerdere links met een enkele behandelen oproep - ervan uitgaande dat ze allemaal hetzelfde formulier laden.
Een belangrijk ding om te begrijpen is dat de waarde van de speciale variabele 'dit' verandert wanneer we de lus self.each binnengaan; het is een speciale jQuery-methode die is ontworpen om DOM-collecties eenvoudiger te loopen. De callback-functie gebruikt de context van het huidige DOM-element, dus de variabele 'this' verwijst naar dat element binnen de lus.
Je ziet in een heel eenvoudig voorbeeld hoe 'dit' verwijst naar een jQuery-verzameling van jQuery-objecten in het bereik van de plug-infunctie, maar binnen de elke lus verwijst 'dit' naar een enkel, niet-jQuery DOM-element.
De code voor de volgende paar secties zit allemaal vervat in het blok self.each van ons skelet. Wat doen we nu? Voor elk ingeleverd jQuery-element moeten er verschillende stappen worden gezet:
Voordat we iets gaan doen, voegen we echter één regel code toe aan de callback, helemaal bovenaan
var $ this = $ (this);
Dit is meer dan alleen gemak; de variabele 'dit' zal buiten het bereik vallen in eventuele sluitingen binnen elke lus, en we zullen later toegang tot het huidige object nodig hebben. Omdat we het bijna altijd als een jQuery-object willen, slaan we het als één op.
$ .popUpForm werkt alleen op ankertags en de ankertag moet een href-waarde hebben zodat we weten waar het formulier vandaan gehaald moet worden. Als aan één van deze voorwaarden niet wordt voldaan, laten we het element met rust. De tweede regel van onze 'lef' zal zijn:
if (! $ this.is ('a') || $ this.attr ('href') == ") return;
Sommige mensen hebben een hekel aan meerdere retourpunten in een functie, maar ik heb altijd geconstateerd dat een functie aan het begin beter leesbaar kan zijn, in tegenstelling tot het gebruik van een if (voorwaarde) om de rest van de functie in te pakken. Prestaties verstandig, ze zijn identiek.
De methode $ .load heeft een mooie functionaliteit waarmee een aanroep kan worden opgegeven en een ID zodat alleen een deel van een opgehaald document wordt bijgevoegd. Het script koppelt de geretourneerde HTML niet rechtstreeks aan de DOM, omdat $ .load alleen overschrijft, maar niet wordt toegevoegd.
var SRC = $ this.attr ('href') + "+ opts.container; var formDOM = $ ("") .load (SRC, function ()
De variabele opts.container heeft de ID van het formulierelement op de externe pagina. De tweede regel laadt deze externe pagina en koppelt het formulier en de inhoud ervan aan een div, waarvan het geheel is opgeslagen in de variabele vormDOM. Merk op dat $ .load een callback bevat (de functie) - we zullen formDOM gebruiken binnen die callback.
Binnen de $ .load callback, zal de code het formulier toevoegen, de klikgebeurtenis van het anker opheffen en de submitgebeurtenis van het formulier negeren.
De HTML van het formulier wordt op dit moment opgeslagen in de variabele formDOM en het koppelen aan de bestaande pagina is eenvoudig.
$ ( '# PopUpHide') append (formDOM).;
De id #popUpHide verwijst naar een verborgen div die door de plug-in aan de pagina wordt gekoppeld. Om die div te bieden, wordt de volgende regel toegevoegd aan de bovenkant van de plug-in. Als het al bestaat, maken we het niet opnieuw.
$ ("# popUpHide"). lengte || $ ('') .AppendTo (' body ') css (.' Display', 'none');
Nu het formulier veilig is weggestopt op onze pagina, wordt het tijd om een aanroep naar de methode $ .dialog te gebruiken om het formulier te maken. Het merendeel van de set-up params zijn afkomstig van onze plug-in. De optie 'Autoopen' is hard gecodeerd, omdat we willen dat het dialoogvenster wordt geopend wanneer op de koppeling wordt geklikt, en niet wanneer het dialoogvenster wordt gemaakt.
// Maak en bewaar het dialoogvenster $ (opts.container) .dialog (autoOpen: false, width: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title);
Als we hier zouden stoppen, zou de plug-in niet veel doen. De link brengt ons nog steeds naar de volgende pagina. Het gedrag dat we willen is dat de link de dialoog opent.
$ this.bind ('klik', functie (e) e.preventDefault (); opts.beforeOpen.call ($ this [0], opts.container); $ (opts.container) .dialog ('open') ;);
De eerste regel van deze klikhandler is erg belangrijk. Het stopt de koppeling van het laden van de nieuwe pagina wanneer erop wordt geklikt.
De tweede regel is onze callback 'beforeOpen'. De variabele opts.beforeOpen bevat een functie-verwijzing - zoveel is duidelijk. De .call-methode wordt gebruikt om de functie aan te roepen op een manier waarop we context kunnen bieden - de 'dit' variabele voor die functie. Het eerste aangenomen argument wordt 'dit' voor de opgeroepen functie.
Wanneer een functie toegang heeft tot de variabele 'dit', zijn er enkele contracten die JavaScript heeft met de programmeur die we moeten onderhouden.
Om dat contract te behouden, geven we $ deze [0] in plaats van $ dit. $ this [0] staat voor een enkel, niet-jQuery DOM-object.
Om dit een beetje beter te begrijpen, stel je de volgende callback-functie voor:
opts.beforeOpen = function (container) // Geeft de waarde van de link waarop u zojuist op alert hebt geklikt ('De externe pagina is' + this.href); // Geeft de ID-container toegewezen aan deze linkwaarschuwing ('En de container is' + container);
De linkklik is niet het enige standaardgedrag dat moet worden overschreven. We willen ook dat het formulier wordt ingediend via AJAX, dus het normale formulier onsumbit-evenement moet worden voorkomen en nieuw gedrag gecodeerd.
$ (opts.container) .bind ('submit', functie (e) e.preventDefault (); ajaxSubmit (););
Nogmaals, we gebruiken preventDefault () om de gebeurtenis te stoppen en in dit geval een nieuwe functie toe te voegen om de formulierinzending af te handelen. De ajaxSubmit () -code kan direct in de callback worden gebruikt, maar is verplaatst naar een nieuwe functie voor leesbaarheid.
Deze functie wordt onmiddellijk na het einde van de loop van self.each toegevoegd (maak je geen zorgen, je ziet de volledige plug-in code in één keer in één keer). Het neemt de vorm aan, legt het voor aan een extern manuscript, en vuurt de aangewezen callbacks af.
De eerste stap is om het formulier als een jQuery-object te krijgen en om de methode van het formulier te bepalen, ofwel GET of POST.
function ajaxSubmit () var form = $ (opts.container); var methode = form.attr ('methode') || 'KRIJGEN';
Als u het zich herinnert, hebben we de ID van het formulier opgeslagen in opts.container. De volgende regel controleert het formulier voor een methode en wijst 'GET' toe als er geen methode aanwezig is. Dit komt overeen met HTML die standaard GET gebruikt op formulieren als er geen methode is opgegeven.
Gebruik de methode $ .ajax om het formulier in te dienen:
$ .ajax (type: methode, url: form.attr ('actie'), 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], kiest .container););
De URL-optie wordt bepaald op basis van het actiekenmerk van de formuliertag. De gegevens worden geproduceerd met behulp van de methode serialize op het jQuery-object met het formulier.
De succes- en foutopties zijn $ .ajax callbacks, die we op onze beurt gebruiken om onze callbacks te bellen, op dezelfde manier als de callback voor beforeOpen werd aangeroepen.
We sluiten ook de dialoog af voor zowel de succes- als de foutenbehandelaars.
Laten we als een overzicht kijken naar de code die we tot nu toe als geheel hebben geschreven, inclusief enkele handige opmerkingen over de code:
(functie ($) var alog = window.console? console.log: alert; $ .fn.popUpForm = function (options) // VEREIS een container als (! options.container) alert ('Container-optie vereist') ); return; // Geef ons ergens formulieren $ ("# popUpHide"). length || $ ('') .AppendTo (' body ') css (.' Display', 'none'); // Standaardinstellingen en opties var defaults = container: ", modal: true, resize: false, width: 440, title: 'Website Form', beforeOpen: function (container) , onSuccess: function (container) , onError: function (container) ; var opts = $ .extend (, standaardinstellingen, opties); // Het 'dit' binnen de elke lus verwijst naar het enkele DOM-item // van de jQuery-verzameling die we momenteel zijn werken op this.each (function () / * We willen de waarde 'this' beschikbaar houden voor de $ .load * callback * / var $ this = $ (this); / * we willen alleen een item verwerken als het is een link en * heeft een href waarde * / if (! $ this.is ('a') || $ this.attr ('href') == ") return; / * Voor een $ .load ( ) functie, de param is de url gevolgd door * de ID selector voor het gedeelte van de pagina om te pakken * / var SRC = $ this.attr ('href') + "+ opts.container; / * de gebeurtenisbinding is voltooid in de terugroep in het geval dat het * formulier niet laadt, of de gebruiker klikt op de link ervoor * de modale klaar is * / var formDOM = $ ("") .load (SRC, function () // Voeg toe aan de pagina $ ('# popUpHide'). append (formDOM); // Maak het dialoogvenster $ (opts.container) .dialog (autoOpen: false , width: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title); / * stopt de normale formulierinzending, moest komen na * het dialoogvenster maken anders bestaat het formulier niet * nog een gebeurtenishandler aan * / $ (opts.container) .bind ("submit", functie (e) e.preventDefault (); ajaxSubmit ($ this [0]);); // een a binding voor de link doorgegeven aan de plugin $ this.bind ("klik", functie (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 ( 'methode') || 'GET'; $ .ajax (type: methode, url: form.attr ('actie'), data: form.serialize (), success: function () $ (opts.container) .dialog ('close'); opts.onSuccess.call (anchorObj, opts.container ); , error: function () opts.onError.call (anchorObj, opts.container); ); ) (jQuery);
Deze code moet allemaal worden opgeslagen in een bestand met de naam popUpForm.jquery.js
De eerste stap bij het gebruik van de plug-in is om alle vereiste afhankelijkheden op uw HTML-pagina op te nemen. Persoonlijk gebruik ik liever het Google CDN. De bestanden die zich in een apart domein bevinden, kunnen de laadsnelheid van de pagina verhogen en de servers zijn snel. Het vergroot ook de kans dat een bezoeker deze bestanden al in de cache plaatst.
Voeg het volgende toe in de HEAD van het HTML-document:
Het bestand main.css is voor onze site-specifieke stijlen, de rest is afkomstig van het CDN van Google. Merk op dat je op deze manier zelfs jQuery-UI-thema's van het CDN kunt gebruiken.
Vergeet niet dat we alleen de plug-in willen gebruiken voor links die naar een formulierpagina gaan. In de online demo bevinden de formulieren zich in form.html en gaan slechts twee links naar die pagina.
De aanroepen zijn ingepakt in een document.ready-blok zodat we zeker kunnen zijn dat de ankerelementen bestaan voordat geprobeerd wordt ze op te volgen. De tweede aanroep, $ ('. Survey a') is een voorbeeld van het minimumbedrag dat nodig is om onze nieuwe plug-in te gebruiken. Het eerste voorbeeld stelt een callback in voor zowel onSuccess als onError.
Als je zover bent gekomen, en je hebt voorbeelden gemaakt van formulieren en een pagina om ze te callen, zou je merken dat de vorm in de modal waarschijnlijk, nou ja, lelijk is. Het modale zelf is niet slecht, omdat we een jQuery-UI-thema gebruiken. Maar de vorm binnen het modale is meestal onstabiel, dus we moeten ons best doen om het op te lossen.
Er zijn een paar dingen waar u rekening mee moet houden bij het maken van stijlen voor gebruik in een jQuery-UI-modaal:
Met behulp van deze kleine stukjes informatie kunnen we beginnen met het toepassen van stijlen op de vorm in het modale. Eerst geven we de modal een achtergrondkleur waar we blij mee zijn, en we passen ook het lettertype voor de titelbalk aan.
.ui-dialog background: rgb (237,237,237); lettertype: 11px verdana, arial, sans-serif; .ui-dialog .ui-dialog-titlebar font: small-caps bold 24px Georgia, Times, serif;
Vervolgens willen we elk item in het formulier scheiden met regels. Omdat de vormstructuur h3s afwisselt met div's die formulierelementen bevatten, voegen we de volgende regels toe:
.ui-dialog h3, .ui-dialog div border-top: 1px solid rgb (247,247,247); grensbodem: 1px vast rgb (212,212,212); opvulling: 8px 0 12px 10px;
En we willen alleen lijnen tussen de secties, niet helemaal bovenaan of onderaan.
.ui-dialog .puForm div: last-child border-bottom: none; .ui-dialog .puForm h3: first-child border-top: none;
Laten we niet vergeten om de h3s en de vormelementen te stylen. De keuzerondjes moeten inline worden weergegeven, zodat ze allemaal op een rij staan.
.ui-dialog h3 font: 18px Georgia, Times, serif; marge: 0; .ui-dialoogvenster selecteren, .ui-dialoogvenster textarea, .ui-dialoogvenster invoer width: 76%; weergave: blok; .ui-dialog #rating-invoer, .ui-dialoogvenster #rating label display: inline; width: auto;
Onthoud dat deze stijlen specifiek zijn voor dit project, je zult je eigen vormen moeten stylen afhankelijk van de structuur die je gebruikt. Als u de formulierelementen specifiek wilt targeten, kunt u afstammelingen van het u-dialoogvenster targeten of elk formulier afzonderlijk opmaken, stijlen opnemen die afwijken van het formulier-ID dat u heeft opgenomen.
De gestileerde vorm:
Dus wat hebben we echt gedaan? We hebben een normale link gemaakt naar een contactformulier (of formulieren) en hebben ervoor gezorgd dat dit formulier in een modaal dialoogvenster is geladen en via ajax is verzonden. Voor gebruikers zonder javascript gebeurt er niets en de links gedragen zich normaal, dus we hebben niet voorkomen dat iemand uw formulieren invult.
Als u op de enquêtekoppeling in de demo klikt, moet u iets indienen. Ik plaats de resultaten na een week of zo in de comments voor de lol!