Med noe like viktig som et kontaktskjema, vil du at det fungerer riktig for alle besøkende - selv om JavaScript utfordres. Hvordan håndterer du dette hvis du vil bruke en modal (pop-up) skjema? Svaret er progressiv forbedring; start med baseline, brukbar funksjonalitet; Deretter øker brukeropplevelsen for de som har nettlesere for å støtte den.
Før du starter en reise, hjelper det (flest ganger) å få en destinasjon. Målet med dette prosjektet er å ta en standard lenke til en side som inneholder et kontaktskjema og aktivere det skjemaet for å pop-up på gjeldende side i en modal dialog.
Det er flere grunner for denne tilnærmingen:
For å skrive dette fra grunnen i rå JavaScript ville det være mye kode. Heldigvis for oss er det eksisterende verktøy vi kan utnytte for å gjøre oppgaven enklere. Denne opplæringen er avhengig av:
For å gjøre denne koden så gjenbrukbar som mulig, skriver vi en plugin-modul. Hvis du ikke er kjent med å lage en plugin, kan du få en introduksjon fra Jeffrey Way's artikkel her på Nettuts +. Den modale funksjonaliteten kommer fra jQuery-UIs $ .dialog.
Vi skal følge det vanlige mønsteret for en jQuery-plugin-modul: ring inn plugin-modulen på en velger og innstillingsalternativer via array. Hvilke alternativer er det nødvendig? Det vil være alternativer både for modalvinduet og for plug-in selv. Vi skal forvente at plugin-modulen skal kalles på et anker, og håndheve det i koden.
$ ('a.form_link'). popUpForm (container: ", modal: true, resizeable: falsk, bredde: 440, tittel: 'Website Form', førOpen: funksjon (container) , onSuccess: , onError: funksjon (container) );
Container: Slik vil plugin-brukeren spesifisere ID-en til skjemaet på den eksterne siden. Linken spesifiserer siden, men beholderalternativet tillater oss å hente den aktuelle delen. Dette vil være bare nødvendig alternativ når du kaller plugin-modulen.
Modal, Resizeable, Bredde, Tittel: Disse alternativene vil alle bli sendt videre til jQuery UIs $ .dialog. Verdiene ovenfor er standard og plugin-modulen kjører fint uten at noen av disse blir satt når $ .popUpForm kalles.
førOpen, onSuccess, onError: Disse er alle tilbakeringinger, og forventer en funksjon. Funksjonen vil bli sendt objektet for lenken som ble klikket som "dette" og beholderen som den linken er målrettet mot. Tilbakeringinger er utformet for å tillate tilpasset funksjonalitet for brukerne av en plugin. Standard for disse tilbakeringingene vil være en tom funksjon.
Minimumskoden som kreves for å bruke plugin-modulen, vil da se slik ut:
$ ('a.form_link'). popUpForm (container: '#form_id');
Det virker enkelt, ikke sant? Når du kaller en plugin-modul som denne, kalles plugin-koden med en jQuery-samling av alle DOM-elementene som passer til selgeren, som vil være tilgjengelig i den spesielle variabelen 'dette'.
De fleste jQuery-plugin-modulene følger et veldig lignende mønster. De iterate over gruppen av selektorer og gjør hva de gjør. Jeg har en grunnleggende plug-in "disposisjon" jeg vanligvis jobber fra, og den passer her pent inn. Dette ville være starten på plugin-filen din, popUpForm.jquery.js.
(funksjon ($) $ .fn.popUpForm = funksjon (alternativer) // Standard og valg var standard = container: ", modal: true, resizeable: false, width: 440, tittel: 'Website Form', førOpen : funksjon (container) , onSuccess: funksjon (container) , onError: funksjon (container) ; var opts = $ .extend (, standardinnstillinger, alternativer); self.each // Det virkelige arbeidet skjer her. // Inne i omfanget av denne funksjonen refererer dette til et enkelt // DOM-element i jQuery-samlingen (ikke et jQuery obj));) (jQuery);
Koden er pakket inn i en selvutførende funksjon, og legger seg til jQuery ved hjelp av $ .fn navneområdet. Identifikatoren som følger $ .fn er metodenavnet du vil bruke til å påkalle det.
Vi følger også gode kodingspraksis ved å sende eksplisitt i jQuery-variabelen. Dette vil hindre oss i å komme i trøbbel hvis plugin-modulen brukes på en side med andre JavaScript-rammer, hvorav noen bruker $ som en variabel.
Deretter opprettes en rekke standardverdier, og disse standardverdiene vil bli brukt hvis de ikke er definert når plugin-modulen kalles. Linjen umiddelbart etter standardinnstillingene sammenføyer passet i alternativene med standardene og lagrer dem alle i optisk rekkefølge.
Endelig er det opprettet en sløyfe for iterating over jQuery-samlingen som identifiseres av selgeren når plugin-modulen kalles ... Mens sjansene er i de fleste situasjoner vil det være et enkelt element (et anker), det vil fortsatt håndtere flere koblinger med en enkelt ring - forutsatt at de alle legger samme skjema.
en viktig ting å forstå er at verdien av den spesielle variabelen 'dette' endres når vi går inn i self.each-løkken; Det er en spesiell jQuery-metode som er laget for å gjøre looping DOM-samlinger enklere. Tilbakeringingsfunksjonen bruker konteksten til det nåværende DOM-elementet, så variabelen 'dette' refererer til det elementet i sløyfen.
Du kan se i et veldig enkelt eksempel hvordan "dette" refererer til en jQuery-samling av jQuery-objekter i plugin-funksjonens omfang, men innenfor hver loop betyr "dette" et enkelt, ikke-jQuery DOM-element.
Koden for de neste avsnittene er alle inneholdt i selve blokken av skjelettet. Hva gjør vi nå? For hvert jQuery-element som sendes inn, vil det være flere trinn å ta:
Før vi gjør noe av det, vil vi imidlertid legge til en linje med kode innenfor tilbakeringingen, helt øverst
var $ dette = $ (dette);
Dette er mer enn bare bekvemmelighet; variablen "dette" vil gå utenfor omfanget i noen lukkinger i hver krets, og vi skal nå tilgang til det nåværende objektet senere. Siden vi nesten alltid vil ha det som et jQuery-objekt, lagrer vi det som en.
$ .popUpForm skal bare operere på ankerkoder, og ankeretiketten må ha en href-verdi slik at vi vet hvor du skal hente skjemaet fra. Hvis en av disse betingelsene ikke er oppfylt, skal vi forlate elementet alene. Den andre linjen i vår "tarm" vil være:
hvis (! $ this.is ('a') || $ this.attr ('href') == ") return;
Noen hater flere returpunkter i en funksjon, men jeg har alltid funnet å ha en i begynnelsen, kan gjøre en funksjon mer lesbar, i motsetning til å bruke en if (tilstand) for å pakke resten av funksjonen. Performance klok, de er identiske.
Metoden $ .load har god funksjonalitet som tillater en samtale å spesifisere og ID for bare å feste en del av et hentet dokument. Skriptet vil ikke legge den returnerte HTML direkte til DOM, fordi $ .load bare overskriver, den legger ikke til.
var SRC = $ this.attr ('href') + "+ opts.container; var formDOM = $ ("") .load (SRC, function ()
Den variable opts.container har IDen til skjemaelementet på den eksterne siden. Den andre linjen laster denne eksterne siden, og legger skjemaet og innholdet til en div, hvorav hele er lagret i variabel formDOM. Legg merke til at $ .load inkluderer tilbakeringing (funksjonen) - vi vil bruke formDOM inne i tilbakeringingen.
Innenfor $ .load tilbakeringing, vil koden legge ved skjemaet, overstyrer klikkhendelsen til ankeret og tilsidesette innleveringshendelsen av skjemaet.
Skjemaets HTML er lagret i formDOM-variabelen på dette punktet, og det er enkelt å legge det til den eksisterende siden.
$ ( '# PopUpHide') føye (formDOM.);
ID #popUpHide refererer til en skjult div som vil knyttes til siden av plugin-modulen. For å gi den div vil den følgende linjen bli lagt til på toppen av plug-in. Hvis det allerede eksisterer, gjenskaper vi det ikke.
$ ("# popUpHide"). lengde || $ ('') .AppendTo (' body ') css (.' Display', 'ingen');
Nå som skjemaet er skjult trygt på vår side, er det på tide å bruke et anrop til $ .dialog-metoden for å lage skjemaet. De fleste oppsettparametrene er hentet fra plugin-modulen. Alternativet 'autoopen' er hardkodet fordi vi vil at dialogboksen skal åpnes når lenken klikkes, og ikke når dialogboksen er opprettet.
// Opprett og lagre dialogboksen $ (opts.container) .dialog (autoOpen: false, width: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title);
Hvis vi stoppet her, ville pluggen ikke gjøre mye. Lenken vil fortsatt ta oss til neste side. Oppførselen vi ønsker er for koblingen for å åpne dialogen.
$ this.bind ('klikk', funksjon (e) e.preventDefault (); opts.beforeOpen.call ($ denne [0], opts.container); $ (opts.container) .dialog ('open') ;);
Den første linjen i denne klikkbehandleren er svært viktig. Det stopper lenken fra å laste den nye siden når den klikkes.
Den andre linjen er vår "beforeOpen" tilbakeringing. Variabelen opts.beforeOpen inneholder en funksjonsreferanse - så mye er åpenbart. .Call-metoden brukes til å påkalle funksjonen på en måte der vi kan gi kontekst - "denne" variabelen for den funksjonen. Det første argumentet som blir passert, blir "dette" til den kalt funksjonen.
Når en funksjon har tilgang til variabelen 'dette', er det noen kontrakter JavaScript har med programmøren som vi skal vedlikeholde.
For å opprettholde denne kontrakten, sender vi $ dette [0] i stedet for $ dette. $ dette [0] representerer et enkelt, ikke-jQuery DOM-objekt.
For å forstå dette litt bedre, forestill deg følgende tilbakeringingsfunksjon:
opts.beforeOpen = funksjon (container) // Gir verdien av lenken du nettopp klikket på varsling ('Den eksterne siden er' + this.href); // Gir idbeholderen tilordnet denne lenkevarslingen ('Og beholderen er' + container);
Lenkeklikk er ikke den eneste standardadferansen som skal overstyre. Vi ønsker også at skjemaet skal sendes via AJAX, slik at den vanlige formen for ukjente hendelser må forhindres og ny oppførsel kodes.
$ (opts.container) .bind ('send', funksjon (e) e.preventDefault (); ajaxSubmit (););
Igjen bruker vi preventDefault () for å stoppe hendelsen, og i dette tilfellet legger du til en ny funksjon for å håndtere skjemainnsendelsen. AjaxSubmit () -koden kan gå direkte i tilbakeringingen, men den har blitt flyttet til en ny funksjon for lesbarhet.
Denne funksjonen vil bli lagt til umiddelbart etter slutten av self.each loop (ikke bekymre deg, du vil se hele plugin-koden i ett skudd på litt). Det tar skjemaet, sender det til et eksternt skript og brenner de aktuelle tilbakekallingene.
Det første trinnet er å få skjemaet som et jQuery-objekt, og for å bestemme formens metode, enten GET eller POST.
funksjon ajaxSubmit () var form = $ (opts.container); var metode = form.attr ('metode') || 'FÅ';
Hvis du husker, lagret vi skjemaets ID i opts.container. Neste linje kontrollerer skjemaet for en metode, og tildeler 'GET' hvis ingen metode er tilstede. Dette stemmer overens med HTML som bruker GET som standard på skjemaer hvis ingen metode er spesifisert.
Bruk $ .ajax-metoden til å sende inn skjemaet:
$ .ajax (type: metode, url: form.attr ('action'), data: form.serialize (), suksess: funksjon () $ (opts.container) .dialog ('close'); onSuccess.call ($ this [0], opts.container);, feil: funksjon () $ (opts.container) .dialog ('close'); opts.onError.call ($ dette [0] .container););
URL-alternativet bestemmes av handlingsattributtet til skjemaetiketten. Dataene blir produsert ved å bruke serialiseringsmetoden på jQuery-objektet som inneholder skjemaet.
Suksess- og feilalternativene er $ .ajax tilbakeringinger, som vi igjen bruker ved å ringe våre tilbakeringinger, på samme måte som før tilbakeringingen ble påkalt.
Vi lukker også dialogboksen for både suksess og feilhåndterere.
Som en gjennomgang, la oss se på koden vi har skrevet så langt som helhet, inkludert noen nyttige kode kommentarer:
(funksjon ($) var alog = window.console? console.log: alert; $ .fn.popUpForm = funksjon (alternativer) // KRAV en beholder hvis (! options.container) alert ('Container Option Required' ); return; // Gi oss et sted å legge til skjemaer $ ("# popUpHide"). lengde || $ ('') .AppendTo (' body ') css (.' Display', 'ingen'); // Standard og alternativer var standard = container: ", modal: true, resizeable: false, width: 440, tittel: 'Website Form', beforeOpen: funksjon (container) , onSuccess: onError: funksjon (container) ; var opts = $ .extend (, standardinnstillinger, alternativer); // "Denne" i hver krets refererer til det enkelte DOM-elementet // av jQuery-samlingen vi er for øyeblikket opererer på this.each (funksjon () / * Vi ønsker å beholde verdien 'dette' tilgjengelig for $ .load * tilbakeringing * / var $ this = $ (dette); / * vi vil bare behandle et element hvis det er en link og * har en href-verdi * / hvis (! $ this.is ('a') || $ this.attr ('href') == ") return; / * For en $ .load ) -funksjonen, param er url etterfulgt av * ID-velgeren for delen av siden for å ta tak i * / var SRC = $ this.attr ('href') + "+ opts.container; / * hendelsesbindingen er ferdig i samtalen tilbake i tilfelle * skjemaet ikke laster, eller brukeren klikker lenken før * er modal klar * / var formDOM = $ ("") .load (SRC, funksjon () // Legg til siden $ ('# popUpHide') .append (formDOM); // Opprett og lagre dialogboksen $ (opts.container) .dialog (autoOpen: false , bredde: opts.width, modal: opts.modal, resizable: opts.resizeable, title: opts.title); / * stopper det normale skjemaet innsending; måtte komme etter * opprette dialogboksen ellers skjemaet eksisterer ikke * ennå å sette en hendelsesbehandler til * / $ (opts.container) .bind ("send", funksjon (e) e.preventDefault (); ajaxSubmit ($ this [0]);); // lage en bindende for linken overført til plug-in $ this.bind ("klikk", funksjon (e) e.preventDefault (); opts.beforeOpen.call ($ denne [0], opts.container); $ .container) .dialog ('open');;;); funksjon ajaxSubmit (anchorObj) console.log (anchorObj); var form = $ (opts.container); var metode = form.attr 'metoden'), data: form.serialize (), suksess: funksjon () $ (opts.container) .dialog ('close'); opts.onSuccess.call (anchorObj, opts.container ); , feil: funksjon () opts.onError.call (anchorObj, opts.container); ); ) (jQuery);
Denne koden skal alle lagres i en fil som heter popUpForm.jquery.js
Det første trinnet i plugin-bruken vil være å inkludere alle nødvendige avhengigheter på HTML-siden din. Personlig foretrekker jeg å bruke Google CDN. Filene som er på et eget domene, kan hjelpe siden med å laste hastighet, og serverne er raske. Det øker også sjansene for at en besøkende allerede har disse filene cached.
I HEAD OF HTML-dokumentet legger du til følgende:
Main.css-filen er for våre spesifikke stiler på nettstedet, alt annet er fra Googles CDN. Legg merke til at du kan bruke jQuery-UI-temaer fra CDN på denne måten.
Husk at vi bare vil påkalle plugin-modulen på koblinger som går til en formside. I den elektroniske demoen finnes skjemaene i form.html, og bare to koblinger går til den siden.
Samtalene er pakket inn i et dokument. Blokkering, slik at vi kan være sikre på at ankerelementene eksisterer før vi prøver å handle på dem. Den andre anropet, $ ('. Survey a') er et eksempel på minimumsbeløpet som er nødvendig for å bruke vår nye plugin. Det første eksemplet setter en tilbakeringing for både onSuccess og onError.
Hvis du har kommet så langt, og du opprettet eksempler og en side for å ringe dem fra, vil du legge merke til at skjemaet i modalet sannsynligvis er bra, styggt. Modalet selv er ikke dårlig, fordi vi bruker et jQuery-UI-tema. Men skjemaet i modal er for det meste ustylt, så vi bør gjøre noe for å gjøre det pent.
Det er noen ting å huske på når du lager stiler til bruk i en jQuery-UI-modal:
Ved hjelp av disse små biter av informasjon kan vi begynne å bruke stiler til skjemaet i modal. Først gir vi modal en bakgrunnsfarge vi er fornøyd med, og endrer også skrifttypen for tittellinjen.
.ui-dialog (bakgrunn: rgb (237, 237, 237); font: 11px verdana, arial, sans-serif; .ui-dialog .ui-dialogboks-tittellinje font: small-caps bold 24px Georgia, Times, serif;
Deretter ønsker vi å skille hvert element i skjemaet med linjer. Siden formstrukturen veksler h3s med divs som inneholder formelementer, legger vi til følgende regler:
.ui-dialog h3, .ui-dialog div border-top: 1px solid rgb (247.247.247); grensebunn: 1px solid rgb (212,212,212); polstring: 8px 0 12px 10px;
Og vi vil bare ha linjer mellom seksjonene, ikke helt øverst eller helt nederst.
.ui-dialog .puForm div: siste barn border-bottom: none; .ui-dialog .puForm h3: første barn border-top: none;
Kan ikke glemme å style h3s, og formelementene. Radioknappene må vises inline, så de er alle på rad.
.ui-dialog h3 font: 18px Georgia, Times, serif; margin: 0; .ui-dialog velg, .ui-dialog tekstarea, .ui-dialoginngang bredde: 76%; skjerm: blokk; .ui-dialog #rating input, .ui-dialog #rating label display: inline; bredde: automatisk;
Husk at disse stilene er spesifikke for dette prosjektet, og du må stille dine egne skjemaer avhengig av hvilken struktur du bruker. For å målrette mot skjemaelementene spesifikt kan du enten målrette etterkommere av .ui-dialog, eller å utforme hver enkelt skjema individuelt, inkludere stiler som kommer ned fra skjema-IDen du har med.
Det stilte skjemaet:
Så hva har vi egentlig gjort? Vi har tatt en vanlig lenke som fører til et kontaktskjema (eller skjemaer) og forårsaket at dette skjemaet lastes opp i en modal dialog, og sendes via ajax. For brukere uten javascript skjer ingenting og koblingene oppfører seg normalt, så vi har ikke stoppet noen fra å fylle ut skjemaene dine.
Hvis du klikker på undersøkelsen i demonstrasjonen, må du sende inn noe. Jeg legger opp resultatene i kommentarene for moro etter en uke eller så!