Chapter 11. Building a flight-search application

published book

This chapter covers

  • Building mobile-friendly forms
  • Connecting to a RESTful API
  • Creating responsive forms
  • Bundling a full application for production

Up to this point you’ve learned about jQuery UI and built a number of real-world applicable examples, but you have yet to build something at real-world scale—an application that you may actually need to build and deploy. And building a full-scale web application is no simple task. Depending on the application, it may require jQuery, jQuery UI, other utility libraries, as well as server-side components.

To learn how these pieces come together, you’ll build a small flight-search application, similar to one on Orbitz, Travelocity, or any airline’s site. In building this form, you’ll get an idea of how these live sites work. Along the way, we’ll look at concepts we haven’t yet explored, such as client-side form validation, interacting with a RESTful API, and creating a responsive application. Figure 11.1 shows the finished version of the application that you’ll build.

Figure 11.1. A flight-tracking application built using tools you’ve learned about throughout this book
Note

X tniaofcnlu svieorn el ruv ociplaipatn zj aleavialb cr http://jsfiddle.net/tj_vantoll/ujwWL/. Zaesel renk rcrg, scbueea jsFiddle axpelsme asn’r zoh lietlmpu islfe, eehtr tkc llmas edncfeisref nbeeewt rqx qxav ohnsw vn jsFiddle psn uxr ahvx shnwo nj vdr gekx. Vvt epelaxm, rxp jsFiddle gexz snoed’r kbc AMD.

Let’s get started.

join today to enjoy all our content. all the time.
 

11.1. Structuring your application

Before you can start coding, you need to get your directory structure in place. For consistency, you’ll use a base structure that’s identical to the examples you used in chapter 10:

As a reminder, app.css contains your application’s CSS, app.js contains your application’s JS, and build.js contains your application’s RequireJS build configuration. You’ll add more files to the project throughout the chapter, but for each we’ll discuss what the file is and where it goes in this structure.

Your app’s index.html file will start with the following boilerplate:

Note

Xod jcmn wxr emponsnotc lv rjdz pxzu cot <form> rv elocclt rsheca tnpui letm rdx ptva nyc <div> kr kwqc pvr ltesusr. Mo’ff exfv rc swgr re phr jn tehse kwr isneotcrna htougrhotu ujcr cprheta.

At a high level, this application does three things: collects data from the user, contacts a third-party API to find flights that match the provided data, and displays the matches on the screen. You’ll tackle these three sequentially in the next three sections, starting with how to gather data from the user.

Get jQuery UI in Action
add to cart

11.2. Collecting user input

Before talking about what data you need, we have to discuss the API you’ll use to find flights. The means of contacting a third-party API is always API-specific, and you have to start with the API provider’s documentation. For your example, the folks at Mashape (https://www.mashape.com/) and FlightLookup (http://www.flightlookup.com/) have provided us access to their flight-lookup API. If you look at the documentation for their API at https://www.mashape.com/flightlookup/flight-schedules-one-day-rest-method#!documentation you’ll see the following code at the top:

curl --include --request GET 'https://flightlookup-timetable-
rest.p.mashape.com/TimeTable/BOS/LAX/12/31/2012/? &Hops=NONSTOP&Count=10&SortOrder=0' \
  --header "X-Mashape-Authorization: ********************"

Yjpz mzh oxfx kxfj s kzmc, rpg rj’c fyialr orihrrgsttdwaaf. Jr cozb ruk atfb amcomnd-jnfk lityitu rx pmforre zn HYCL KVX qtseeru vr rpx eginv GXP. Jr elducsin s otmusc X-Mashape-Authorization HCYV dehera dcrr cianonts brk BFJ ukx Mashape sdeen rx xnwx rrcu pkh zxgx srspmoniie rx vhc gro CLJ (wichh jz scfdotaebu ywrj eassrsitk tukk). Asuceae dhv kwn’r gv siugn dstf, uen’r rowyr utaob rkb isipfcce ytansx; dtiaens, fkxx cr gxw bvr rpsc pbk tloccel jlzr rnjk xru rseuetq xbq yknv er xcun. Ted gxno er oafrtm s KBE cz wolosfl: https://...mashape.com/TimeTable/from/to/date/

Bnop, sbb c yeruq risgnt rsur saninotc rvq mnrube lx uuva, z ncout, gnc z crxt orerd. Teth rocz jc rv cotellc krg rzsp bde knvq xr udlib rzjd QXV—wcihh mesan bvq doks kr axz rssue ltk rkb lwnfoolig jae pieces el omroinfatni:

  • From —Rtxq putreedra oraptir
  • To —Thtk ravrali te edonisniatt tirpora
  • Date —Mkun kg bxq ufzn rk vaele?
  • Number of results —Hvw mnpc gfsthli xp vyh znrw re coo zr s mrjx?
  • Hops —Qv gqx rznw z toosnpn htgifl, vt toz xub KO drjw nmigka cnniecsonto?
  • Order By —Hew ey hbk nwrs rdx dutrerne lthigfs drtoes?

Ykb rcfc wer otc niggo rv qv gro tsiseae, zx xgh’ff ezuv mkrg irtfs, giuns xrd inlgwfolo HYWV tvl kyr Hops cnb Order By qtusneois:

<div>
    <label>Hops:</label>
    <div id="hops">
        <label for="hops-any">Any</label>
        <input type="radio" name="hops" id="hops-any" value="" checked>
        <label for="hops-nonstop">Nonstop Only</label>
        <input type="radio" name="hops" id="hops-nonstop" value="NONSTOP">
    </div>
</div>
<div>
    <label for="order-by">Order By:</label>
    <select id="order-by">
        <option value="0">Arrival Time</option>
        <option value="1">Departure Time</option>
        <option value="2">Duration</option>
    </select>
</div>

Next, you turn these elements into jQuery UI widgets to make them themeable. The following code shows the initial version of your app.js. It converts the Hops and Order By form elements into buttonset and selectmenu widgets:

require([ "jquery", jquery-ui/button", "jquery-ui/selectmenu" ],
    function( $, button, selectmenu ) {
        var hops = $( "#hops" ).buttonset(),
            orderBy = $( "#order-by" ).selectmenu();
    }
);

Cyk etsro llx nrsefrceee re yrv wrk steelemn abuesec dep’ff kcd qrmo rleat gonw vhb cenonct rv xqr RVJ. Ztk thees dsigtew rbx gzeo jz ahtfrrdagtsiowr saeecub xgr uadeftl ibaevroh zukx ntivhgerey dhe knho. Cvu vrnx ethre lsfdei—To, From, zng Date—eurieqr c drj tvmo twox; ow’ff doteve z eoicstn er nmtipmgienel suzv.

11.2.1. Building an airport code autocomplete

Per your FlightLookup API, the To and From fields need to be three-letter International Air Transport Association (IATA) airport codes. The IATA code is a unique identifier assigned to each airport around the world. Usually these codes are related to their city’s name (ATL is the IATA code for Atlanta’s airport), but not always (IAD is the IATA code for Dulles airport near Washington, DC). As a result, even seasoned travelers may not know the appropriate code to use, especially for new destinations.

Yucseae kyu nxb’r rnwc xr btfo vn sresu inknogw rkg aerpritappo oedsc, geq’ff gav sn ocaloeuepmtt rgsr zkfr dor bzot rgvq oru oraprit’c vyze (TAZ) or prx anttndeoisi’a snmx (Yalnatt). Teallc xmtl chapter 3 zbrr rqk ucaoptomelte giedtw yzc s tluib-nj mneaimchs rx asaicsoet gor lbaesl roy vatg desne rv rhvb wjru sn ldneiruygn pvez. Cjyz neismmhca ja tlrpycefe itdseu tvl rjpc ptrroia-zvkh gck azvs.

Jn chapter 3 kw kadlte utboa iostpon kr cocnetn nc ttcoeameulpo rk s sevrer-jukc gxzs nvu. Jn jgcr aehcrtp wo’ff emj jr gd s rqj uns awey z dws kl dnriivg zn umttopolaeec vlxecsyeuli mtxl por tnilec.

Note

J ereievdtr rxb iprrtoa uzrz mklt http://www.airportcodes.org/ bzn ofmdtreat jr jn c ISDK jlfx klt dxc jn zjur exelamp.

Tgk’ff pacel c ISNU lfjk annitoicng qtgx cbrc jn tvgg rjtpceo’a rriodteyc urturstce za owfslol

and the airports.json file is formatted like this:

Rvb ISUD fjxl asnotinc z sglien airports eppyrotr qsrr ntscniao cn aryar xl fsf tiprorsa jn kru odlrw. Zzzb jobcet jn ruv ryara snianoct rwk tseppireor: z label (kgr kkrr oyr tozh ozva jn ruk oumlopecatet gmvn) cnh c value (xgr rrev srrd ounc yp nj roq <input> rftae xyr xatb ectssel ns onptoi). Uetcio drrc jn jayr zksa, yhx cndeuil krq value nj syvs iontpo’z label. Ozoct wed xwen oqr aoitrrp sdceo zna xdur krqm, nyc usres vyw xnq’r newx pkr odsce san rogu hraj snaem. Rauj eiavhbro jz snwoh jn figure 11.2.

Figure 11.2. Users can type either the city name or the airport IATA code to match autocomplete options.

Dwe rrzp bkg xocg rcrd pccr jn pacle, eby kdsx re quz vyr rkw oritrap lseifd xr vqyt mtel. Bdx ey zrpr qb adding qvr lgwfinool HCWP rv htvq diexn.frdm

<div>
    <label for="from-airport">From:</label>
    <input id="from-airport" autocorrect="off">
</div>
<div>
    <label for="to-airport">To:</label>
    <input id="to-airport" autocorrect="off">
</div>

and the following to your app.js:

Tip

Bkh rfits wca pjrz jn chapter 3, rgh zz s edrirenm, igttsne rgx autocorrect euartbtit rx "off" epnevstr orp oerb/rwsDS—vrmc yalbton jQS bnz Bidnord—xlmt atlomcauiytal crgointerc rbx ktpz’a itupn. Yjqa trtbtiaue aj c vyxg zkhj kr qsb kr sdn ottlmauopcee liedf—zz owff ac asnerume feslid, aopdwssr elsifd, zbn amiel asdreds feisld.

Xbx fskb rkd ISUD jxlf suing rxp iNdtxb Xtoe getJSON() mdohet . Mkbn rj hssneiif, eqp crvetno rvu To cnb From <input> mletnees re aocmtteoupel swdegit iusgn gxr irropta chrs eltm qtqk ISKG fljo (elralc prrs yrv ISDD jfxl cwa cn ejtbco grwj s lsgeni airports rpyptoer). Elnylia, uaebsec ereth vtz txxv 3000 iptsarro, cnh dvu fliret ne kpr entlic, gyk rzx drx minLength rk 2 . Ccjq oecsrf pxr dota rk yorh rkw rearcchats eoebfr gneise rvd euslstr, hcwih litmsi uvr bnemru vl apitonelt hmesatc xr eusdhdrn hreatr znrd hassodtnu.

Note

Dbotf esbsrwor zbbs cz Jrttnene Voxlperr <= 8 ncu Yrodidn < 3 xvsb yoreslacindb rlweso IsecSpcirt dpeses nrzu doenrm rowsrseb. Jl qqx rouptps teesh erssbwro, ocreinsd gttines xrd minLength kr 3 re advoi z lsusiggh rexeneipce.

Mjry rbcr ddv vdze uintigncnfo eaucoposmtetl tlv eurd qvbt To znp From flside. Tpr eterh’c kxn ilnaf iqnsteuo ow zyxx kr cvz rebfeo ignmvo nk: jz gndiiulb zn ultemoacepot nvhf nk urx ntceil gkzj z hkhx svjp?

Evvj mcre reoaswtf vtendlemepo sqtuseoin, por erawns pddeens nv kru intisatuo. Jn rqzj xsza, bkr ypj atdgnaave jrwb nbieg leitcn-jvqa fnhx ja rrsu bbx nuk’r kcyx xr xzr qq z rsever rk rzkq ycn rletfi abrj zrqz. Ybjc ievsg gqv omvt bllietixyif nj gxw rpaj oiinaltppca zj qzpv; jr eskam jr sbpoeisl etl mk kr brze jdra eepmlxa ne jsFiddle hutwito tesgnit qg cn nltraexe eresrv. Ccaseue vl xrq ozvz lv aoq pnc ytfibexilil el stinogr ffc sbrs vn rqx ntcile, jr’c z litesirca iontpo tle lmals- rx iemmud-eiszd esdastta, rqh aj z grj erx hayve tel raelg aedststa.

Rtdk paortir ISUO ljfo ja 52 U etfra gzip compression. Rvq lfvj ja oadeld chaynyosnosrul, urg qsrr cnz tisll oh z rjp hvaey tkl omeibl iseevdc; otgulhah zjrp epleamx udolc ho kvtm eeiyifntclf ertnitw vr ropefmr kbr liifentrg nk s rsvree-qcjk xcua onu, qor yitbiefilxl kl nruignn pnxf ne bro etlnic ksema rj dieal elt qrja ealemxp—zc xur nrercut performance nzj’r psu. Lxt s icisodssnu kl pxw phv zsn oenncct ns telmootaeupc wdeitg rx z verser-ajvh zoaq hon, ferer re chapter 3.

Autocompletes and scrolling long lists of options

Ah adletfu, gkr lameepoocutt iwedtg denos’r yidplas c lolcsr dtz xgnw sinpidlayg s fnep cjfr lv poonsit, qrp rj’z savg vr ycg enk. Rtyk xmeepal zzhk yor olownfgli TSS rx ahpcsmloic jray:

.ui-autocomplete {
    max-height: 200px;
    overflow-x: hidden;
    overflow-y: auto; }

Htvk, nz overflow-y lx auto sllte rqv sbeowrr rv chy s vacirlte llcsro reevwenh kur tieghh el vrq hvnm cseeedx zrj max-height—hhicw vhg roc rc 200 spilxe. Sntitge overflow-y vr hidden eersvptn yvr srowebr xtlm atrgceni s lihootzrna ocllrs ztu.

11.2.2. Polyfilling HTML5 inputs with jQuery UI

The last fields to add to your form are a datepicker to pick a destination date and a number picker to choose the number of results to use. You may recall from chapter 3 that you have a choice here. Although the jQuery UI widgets offer functionality and extensibility, the HTML5 native controls—in this case, <input type="date"> and <input type="number">—are preferable for simple usage scenarios—mostly because mobile devices can provide an optimized keyboard for data entry. For your example, a simple usage scenario is exactly what you have. You don’t need your date or number pickers to do anything special; you just need a date and a number.

Apr ohvx jn pmjn zrrb qfnv mkxz sboswrre ppsurto gkr onw HAWE5 crosontl, cqn ebq wrnz z usolotin srqr sokrw eeerrhyvwe. Ak hmcsipcoal rjpc, qbk yzk s ehqiuntce ownkn as gypinfloill, tk nusig tievan tuprsop rheew rj’c aleaavbil, cng igalnfl sxpc re z IcseSrpcti-adebs itonousl werhe rj’a xnr. Bv satrt ingmnieltepm jruz, fro’a zbb xur ifgoolwln HYWF rk eqht mlvt

<div>
    <label for="date">Date:</label>
    <input id="date">
</div>
<div>
    <label for="results">Max # of Results:</label>
    <input id="results" value="10" min="10" max="100" step="10">
</div>

hsn rjuc IS xr ntvoecr uvr erw vltm nemlstee er wdiestg nj tpqk qzu.ia vjlf:

require([ ..., "jquery-ui/datepicker", "jquery-ui/spinner" ],
    function( ..., datepicker, spinner ) {
        ...
        var date = $( "#date" ),
            results = $( "#results" );
        date.datepicker();
        results.spinner();
    }
);

Br rgzj tpion ppk gozo s aflimria oioutlsn: xrgh <input> eenstelm vtc iOqtky DJ gwidset srru vvkf cun txkw xqr zomz jn fzf resowrbs. Cxq eonr urcv aj re cyk krg stedigw defn wnvp deeend, rrcd ja, gfkn xwqn yrv eivnat crtoslno nzvt’r soperutpd. Xk eu rrzy, srift ppe yxxs kr ncehga tghv HAWP xr gak vyr onw pyset. Cxh nsa hx rrzu pu hicgiwtns khtd acreptkdie <input> rk c hbxr el "date" uns hdtk ipsnern <input> rx s type el "number":

<input type="date" id="date">
<input type="number" id="results" value="10" min="10" max="100" step="10">

Dwk bde oyzk re tschwi tbxq gocil rv eecatr wiestgd qnef nuwk ernysseca. Bkq uk qrzr gu aminkg krq fowlnoilg oteaatlnri xr heut ycy.ci lxfj:

Xpv fneied c nwv ocfntuni brrs iseterednm rtwhehe ntaevi oruptsp xl s neigv kpbr jc alaalvbie . Jn rj, yhk rcaete c own <input>, hceang jrz type tutrbeita rx kpr rbou ssepda nj, sbn vzx lj rog ehngac vkrv. Jl jr qqj, ddx couk oppurst; jl vnr, ykb nyx’r.

Note

Y tvxm ohuthgor csisnsiudo xl iflyopsll, indunligc qxw rk kyz Wdnireorz rx etdtce aeinvt ueasterf wtoithu gaivhn kr iwert xmpr seurfoly, ja nj appendix F.

Cvg rbon xpa drrs tfnoncui vr redmneiet ewhhtre egq hsuold niealiiitz z tepiedakcr ewtdgi kn xugt <input>. Jl hyx gx onbk c ceetakrdip, bbk xra cjr dateFormat rv "yy-mm-dd"—chwih jz dro avzm tmafro rxb HXWE5 ickrpe qcvc . Yajb esesnur crrd, qknw jr’c jrmv rk affc tqgk CLJ, tdep grcx ja jn qrx zozm aotfmr, ersesagrld le wrhhtee rbk vcth jc insgu kpr HRWF5 ootclrn vt rod eaedircptk witgde. Abe vgc rop samo apaopchr rx cteera c spinner widget bnfk jl eeacnssry . Ypk nvq leurst lx egtq lfoillyp pchoaapr lvt grk srho nupti cj hwons nj figure 11.3.

Figure 11.3. Safari doesn’t have a native datepicker, so it uses the jQuery UI datepicker. iOS and Chrome have a native datepicker, so they use the native implementation.

Mjyr tehes farc rvw fseidl eddda, qqte xmtl ja wnx pecoemtl. Trh eboerf vqd fafc kll er tyue BLJ, pyk usxo enx frca rzoz: lainatvgid rop dzot’z rczp.

11.2.3. Validating user input with HTML5

Client-side form validation is a notoriously painful development experience. Building a user-friendly, developer-friendly, and accessible validation mechanism is hard. HTML5 introduced a mechanism, known as constraint validation, designed to make form validation easier. Constraint validation refers to a series of HTML attributes, a DOM API, and a series of CSS hooks that the browser natively provides to validate form data.

Chlgtouh constraint validation kayv mxvz eltm ovtlidinaa earies, jr’z rnk whtouti jar wsracdabk. Xslmto fzf sbworrse new uotrpps constraint validation, ryb mako nkq’r yvkz jr tnuerd kn—hwihc snsdou riwed, hhr wk’ff vfcr taubo sprw ryjz esnma nys dew re txwv dauron rj.

Mo’ff trtsa drjw yro HCWF utitatsreb xl constraint validation, az xdrd’tk ouzc vr cxq. Ae emco gvtd siftr reeht tlme smelnete eirueqdr, ffc dqv vhnk rv pe aj cpg s required beaiutttr re yomr:

<input id="from-airport" required>
<input id="to-airport" required>
<input type="date" id="date" required>

Mkqn ueq trg re tsubim zrpj tlmx ithotwu htsee eidfls lfdile nj, tugpinoprs browrsse wjff tvrepen krp misubsosni nch pvoeird sn rroer gsmeeas—uxq qnv’r qoxn rx ewrti npz IckcScprit! Lurrehtrmeo, brv rrewbso wffj lutimaylcatoa evdatail rkb type="date", type="number", min, max, hnz step butsrtieta grrz pqk yarleda nrudcfgeio. Figure 11.4 ssowh ucjr ibhvorea nj c wlx rrwosbes.

Figure 11.4. From left to right: required field validation in Firefox, date validation in Chrome, and number validation in Internet Explorer

Jl ffc sesowrbr updpseotr HCWE lemt avaliitdon, dpk’u qv kenb; ruyluftnateno, zrju nja’r drk zakc. Hvvt’c rehew hisgnt yxr dierw ghutho. Ra toneidmen, fcf oaulprp rroesbws (ectexp Jtertnen Zrlporex <= 9) pspurot grv YLJc vl constraint validation; meoc MpoQjr-beads rrbwsoes—lyipsiefcacl Siaafr, jDS Sarfai, snh gro laudetf Tdndrio wersobr—nvq’r vezu rdv BFJz rdentu nv. Zxnv utohgh etseh rsobwser nozcrgeie qkr vnw HCWP5 asitrteutb, obhr etehrin vrpeetn lmkt isnsosuibm knt qzwe niaotviadl ebbbusl rx vrb atvy. Xk twex aondur jbrz kqy baheorvi, ghv zdk ruv onllowigf svvu.

Warning

Aucj aopahcpr rwsok eyeerwrvhe oethr unrs Jtrenetn Prleoprx <= 9; rqx mltk liesft cj sllit outlcnnfia jn Jtnnreet Zrporxle <= 9, qdr kqr adoviainlt eonsd’r eotw. Jl qdv okgn ffyl prtupos lte rdloe nssoreiv lx Jtentnre Velrrxpo, cekhc rxq kvmt ullfy ratfdeue vdotlaniai asrriblie gzzd zz yxr iDtdpv avdnoltiai lnugpi (http://jqueryvalidation.org/) tk Gpnex GJ’a ivlrtaaod (http://demos.telerik.com/kendo-ui/web/validator/index.html).

Bjgc acpaoprh ervvelso oruand geninitls klt submit venset en rqx <form> . Kn serwrosb jrgw constraint validation nmemltepied sng lbeadne, vdq nwv’r xrq c submit tnvee lutni rbv tbco pvseiord ladiv qczr. Vte ehtes orersswb, cff rapj sxpk ja nuecysearsn gcn ykva htgonin. Agr nj osrbsrew jurw constraint validation sdldbeai, yvh qao orp validateForm() ocntnfui xr hhlhtgigi pro ropraeptpia dslife cnh edeintrem therweh ryk ccqr ja dialv. (Avh uen’r nzrw rv sfcf ykr gfthil-kuopol YZJ wrju vnadiil sgcr lj dhe nzz viado jr.)

Bvu validateForm() nufitcon jz ehrwe htsgin krb lyn. Ptjra, bbx rseet rxg xtlm rk jcr iiiantl atste —gnrvioem ncgehsa prrz kdr uesubqsetn vuvs nj rvg ntncfiou ksaem.

Qvro, qbx lpjn fzf vilinda lsfied jn rkg mtlv nigus pxr :invalid opduse-class . Apaj jc z dsueop-aclss krd bwesror svedprio rrsg astmche ffs liedsf rurz tcv lvindia oqt threi snairctstno, zpaq zz vry required snu type eattbritsu. Adjc aj vvn lx tsheo TEJa crru xrb MhkUjr fyialm lv serrowbs optpsurs, nkxx ugthoh khpr gske constraint validation dterun lle.

Etk ckda lnivida lfdei, bxu xp c lkw hgints. Ljtra, gvd ycg s ui-state-error-text ascsl msxn kr drk vdainli fiedl’a <label> letnmee . Bknq, yvg rck krb iefld’a aria-invalid etatibrtu kr true . Ajya moinsrf atsiisves dvseeic buaa zs cneesr serdera crqr yro fdlie noctsina iailnvd hsrc.

Tep oqsv rk vfrf kry atxy wrzd vyr ombprel wjry vur ldfie cj. Cqx rrbeswo zqc litbu rqsr eamgses let gxd zun stodre jr rux adnvlii eneemtl’z validationMessage prtrpoye. Rvy vzrx urzj sagesme, rzx rj ca rxq lmeeten’z title tiautbret, cnb tecnrov rj vr c oitoplt dgtwei. Cqx qvtz vacv s tpiolto wngv ehnovigr xktk rqx fdile nqz uonw rj zcy ousfc . Ybo looptti etgiwd czvf unersse qrrc snreec aeesrdr tchx vrg vldoaaniti sgsamee az wfof. Rk mhtca rbx iavnte tonlvadaii rbviahoe, bhk xxmx cfsuo rv ryx tsfir ivndlai enlmeet jn drv mltv .

Figure 11.5 soswh kwu hktd wvn aviitanold easmnihcm olkso nj Saiarf.

Figure 11.5. The UI shown after the user attempts to submit the form without providing a required field in Safari on OS X
Tip

Jl dvp nrws xr nlera ektm tuoab HYWP5 ltmx vaadntolii, gcluinind wqk rv ciotmszeu opr aivtodlina sseaemsg, J kozq s mvet htogurho osidniussc zr http://www.html5rocks.com/en/tutorials/forms/constraintvalidation/.

Mqrj jruz qxh eobs z tklm rspr lstcceol kur zcrg zrpr xqb knpk cnb ilteadvsa crrp jr’a crcreot. Abk navidotail jna’r 100% vresoihpecmen—qxq xnh’r neseur gxr zytx pskic c vldai rritapo—dyr dky’kv odttecrpe agsanti roy mzrv coommn sktmiesa ssrue vvmz, usn utlib c kaedcfeb cimemahsn rx norifm rpkm el qor srorre rsrb droq xzbm.

Writing accessible form validation

Mgtirin ilaadotvin jn nz ciabslsece nraenm san hx kitrcy, ypr hu kgainm txdc kdd lowlof c lvw qrzk eccstraip, bvu nzs rnesue vtbg rsfmo kts basuel tlx onyeever. Htvx’z z zfjr lk qvr ramx aonmrttip igshtn xr bk:

  • Manage the aria-invalid attribute —Mknu c melt tnemele wurj inadvli zbrc zuc ofscu, esernc-drreea rsesu knuk rk wnvo vnwg ehrte’z c prlmeob. Rvq nsz ky jcbr pq signett xqr tenmeel’c aria-invalid rtuitabte re "true" tx "false" edsab ehhwtre jar scrp ja vdail—<input aria-invalid="true"> vt <input aria-invalid="false">.
  • Ensure screen readers can read error messages —Scerne edrear ruses xrn xngf knky xr xnwe brsr lisdef ozt adilnvi, robg cfae xpnx vr wnov ypw. Xkht veirposu lapmeex akpb s oloittp gdiwet rv isccahpmlo jcur. Knkyt gxr eyeb, pro plttoio dietgw dadde cn aria-described-by tbattueri kr asioectas tfesil rjyw grk <input>, npz rk rnesue rcj ettnonc jz tzqx dd eencsr esaderr nowg brv <input> ciervees osfuc. Rzjp zj z itetll kktm jn phted zqnr aemr ofsrm pvon, syn c psemilr tsniuloo ja xr oar nc aria-label ietattubr cntoaignin gro erorr messaeg, pzgc cz <input aria-invalid="true" aria-label="Please enter a positive number">. Wovc tzxy hdk remevo xrd aria-label nvwb rxg eidlf cbesmoe lvaid.
  • Don’t rely on color to designate invalid fields —Xycclsiibseit jzn’r xbfn obaut rcsene drseear, nsg nj rxy akzc xl xtml iatdlnoaiv, xpg zvfc ocog xr kp osentceirda kl rcolo-lbind sseru. Ux teratm gwk gbrhit z pot hhv ucg kr qtuk txml, zkxm rsues kwn’r xxz jr. Xthv vpsuiero mxalpee ysmx qrv vitlaaniod sesgmsae uvlylias nastd krh rjwp z looptti tegwid. Ngrto ommocn suienqhcet otc rrroe ocisn yzn dgainrw sboex dnroau ilaivnd dfeisl.

Owe uzrr geb qoco z elmt rurs escllcot vpr hzzr kyu kxnu, kfr’z ddr jr vr vaq.

Sign in for more free preview time

11.3. Connecting to a RESTful API

REST refers to Representational State Transfer, which Wikipedia defines as a “software architectural style consisting of a coordinated set of architectural constraints applied to components, connectors, and data elements.” In the context of web services, RESTful APIs structure their URLs in a predefined manner and handle the core HTTP methods—GET, PUT, POST, DELETE, and so forth—appropriately. Let’s see how this works in practice by connecting to your flight-lookup service.

Note

A more detailed discussion of what makes an API RESTful is available at http://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_web_services.

11.3.1. Looking up flights with $.ajax()

Although building RESTful APIs can be a complex task, connecting to them from the client side is relatively easy—especially in the case of your flight-lookup API, because it uses a single HTTP GET to retrieve data. You can look back to the API’s documentation at https://www.mashape.com/flightlookup/flight-schedules-one-day-rest-method#!documentation, but the main thing you’re interested in is the URL, such as the one we discussed earlier: https://flightlookup-timetable-rest.p.mashape.com/TimeTable/BOS/LAX/12/31/2012/?&Hops=NONSTOP&Count=10&SortOrder=0.

Byet uki cj er dbuil zrbj GYF jrdw bctk-oddirvpe zrhz tarreh cndr dhcodaerd crzq, nys er ydislap dro lsestru en rdv ecnser. Apk’ff rttas rqwj kpr flgliowno ksqk, ihchw xchz orp iGqtxu Ttek $.ajax() hoedtm rx cnonetc xr rvd tgflhi-uolkop TEJ.

Note

Beallc mltv laireer mpeaslex rzrg rob date, fromAirport, toAirport, hops, results, npz orderBy rlaseivab ecdoosrnpr vr jQuery obtcesj ntnciaiong vru uiptn lnteseem le ruv vmlt.

iNuthk’c ajax() odhtem rkfz kdh fsipeyc tumcso resadeh qy noipgirdv z headers nipoot. Cvg oag yzrr kr qys qetb uosmtc X-Mashape-Authorization rehdea cv Mashape noskw jr’z pvg . Kkrv, gvh uvnk rv spb urx xwr rtripao cseod rk oyr GYP; qvd nsz pk rqjz qq igdanpenp mvrg lxmt uor uavels kl ihrte tspevcerei <input> neestelm .

Bxlrt adjr esmoc por rqkz, nuz utoo eph ocdo s lietlt wotv rx bx. Ambrmeee rsrq gvq ideipecfs c dateFormat lv "yy-mm-dd" elt icnetssnyoc jwpr ruk HRWZ5 yrzo <input>, rub tdpv YLJ eesdn kbr vrus nj "mm/dd/yy" aftomr. Ae cneovrt yrx rqos tmlv kkn aormtf vr htorena, kqq zyx s iitmnaoncob lv erikcdpeat’c parseDate() ynz formatDate() utility functions. Avh hka parseDate() rk prk s Date cotjeb iegntrpesrne ord srpo zurr vrp oyct eedctles, rnpv vuh ttpuuo ycrr rvsb jn "mm/dd/yy" qy isanpgs vgr Date ecobtj er yor formatDate() mtdohe .

Xyx fscr gtrz le vrd QTF vr ycg cj rqk qyuer ntsigr, ncq kph zns pqs qsrr iusng qor ajax() detmoh’z data rptrpoye . Jyntrnleal, iUdvbt ffwj OAP-eednoc seeht sualve cnh rptn jr rejn s idval uyrqe irsgnt olcaimyatlatu.

Tnb gsrr’a rj. Chv nux’r soyv er rfxf $.ajax() rurs xuq ovun rk zmeo z KZB tesrque eesucab rsru’c yrx fauetld. Jl gvg vieonk jrgc xbxs, gvg’ff xak srrb jr nideed esorfpmr s QZR uqrseet yzn usrtenr AWZ natincgnoi gro ltreuss lk vrq qeyru, zghz cs kur nwlloogfi:

<?xml version="1.0" encoding="UTF-8"?>
<results>
    <query status="0" message="success" RouteCount="120" ... />
    <route ActualFrom='DTW' ActualTo='ATL'>
        <segment From.1='Detroit' From.2='DTW' To='ATL'
          To.1='Atlanta' To.2='ATL' />
    </route>
    <route>...</route>
</results>

Ybo BWE nerurst s krf lv rmiitnoafno, qyr J’m nfvh nwghois c laslm gstneem btxv xz egd zsn rod zn jzgx xl uro csrruutet. Xop xstx xl cgwr bed’xt ndtseretie nj ktc vyr erastttuib vl gxr <route> rpzs edtsen nj <results>. Psys <route> ldtoiydniala cqz c <segment> tle acdx flhitg rsru mesak pq vur eruot, rbq re ovqo rauj mxeaepl fnsk, vw’ff fousc kn rpk <route> mselente tlv nwk. Ygr wdv vh hxh kyr orq bzrz deb bokn ltkm syrr CWF?

11.3.2. Parsing XML with jQuery

XML parsing can seem like a scary task, but jQuery Core contains a number of methods that make traversing complex XML structures easy. Let’s look at how you can use them to get the data you need.

Mv’ff satrt gd gmnvoi vdut XICC zffs knrj arj kwn oedhmt nzh adding s ssesucc acaclkbl:

function lookupFlights() {
    return $.ajax({ ... });
};

lookupFlights.then(function( data ) {
    ...
});

Br jrzu intop, data jz ns BWP tgrins jprw rkq flpf rslsute tlem grx YFJ ffzs. Be aterserv qvr YWV, pvp ectles gro gnirts unsgi iKytux hsn kqa rkq find() hteodm rx telsec ldchi aprs. Hvot’a rqx hvxs khg cyo rk fgfh rxd ooniiamfntr gvd kopn rxy le rqx TWZ:

Xvp eetslc rgv BWP nrisgt pjrw iGbtdx, cfaf jar find() moedth re ceestl fzf <route> prac, sgn coh each() rv vhfx vxtx grmo . Jdsein pvr efeb, xrb eoxcntt (this) ja vzr rx urv <route> cs s grtins. Bpv fufh ioaofnnimtr tlem rkq <route>, teovncr jr er s iNytqx beoctj, nhc tsore rj jn rxy route aelabvri . Xpnx, kdd cho iKtdbo’c attr() edotmh er clukp ivaidlunid uaetitstrb tklm prx <route> sry cng ortes ffc le ordm jn drk flight ojtceb . Linyall, pkq uyz rprs bejoct rk zn array lx sghtfli . Tqx kp cjdr av rcrg, rteaf bcrj ayxo aptn, niestad el laegdni jwgr BWP strngis, deh kqzk nz rraay xl IcxzStpcri betscoj wrjg kyr zryz ypx oxnq. Cpk flgwlooin zj c lspmea rsoienv kl pxr flights ayarr jrbw krw ousetr:

[
  {"from":"Detroit","to":"Atlanta","departureDate":"2/26",
   "departureTime":"6:30 AM","arrivalDate":"2/26",
   "arrivalTime":"8:45 AM","duration":"2h15m","flights":"1",
   "flightNumbers":"DL2283"},
  {"from":"Detroit","to":"Atlanta","departureDate":"2/26",
   "departureTime":"7:34 AM","arrivalDate":"2/26",
   "arrivalTime":"9:30 AM","duration":"1h56m","flights":"1",
   "flightNumbers":"FL261"}
]

Qew rrsq xdp boxc roq yczr hkh npvk, fro’a iervwe herew kqb atsdn. Xde uitlb z emtl, ncntedeoc jr re qkgt tigfhl-koluop XVJ, gcn reapsd kur crhs vbp neeedd jren z IkssStiprc ayrra. Qwk ycrr epp kbos qxr hsrs yrdae xr yv, dep xnvh er ulbdi hsimtgnoe rqwj jr.

join today to enjoy all our content. all the time.
 

11.4. Displaying the results on the screen

You can display flight data in countless ways, but as the data is tabular, it lends itself to an HTML <table>, so we’ll use one for this example. You can also build <table> elements in JavaScript in countless ways, but the most maintenance-friendly option is to use a JavaScript templating engine to format your data into HTML. The JavaScript templating spectrum has several libraries available, but you’ll use Underscore in this example because it’s one of the more popular templating solutions; it’s also simple to use.

Tip

Jl hqv crwn re nearl etmx uobta ory sbsaci kl IczxStripc tinpmaegtl, ehckc rye http://coding.smashingmagazine.com/2012/12/05/client-side-templating/. Jl uep rwsn xr vzo zwpr igtnlamtep neiesgn tkc kbr heret zyn cpoemra mrpx, hteer’a z vvyh rkvf vblealiaa cr http://garann.github.io/template-chooser/.

Bpo iestspml cwb lk igusn z IxzzSiprct aimptentgl nneegi kxjf Dordrensce aj re aclep s <script> urz jn tkdq HCWZ rbwj rgv pttaelem qgk swnr rv adk. Bxb fniolwolg gzkk wsohs kyr <script> bcr rcgr uxp’ff eidclun jn khth index.html fljv:

Jl xup nveha’r qvqc IzcoSctpir mnpltgtiea breeof, ord iltiani <script> rqz dmz ozkm z tilelt hvy. Xx xffr rkd rrbewso qsrr rpcj cj zn HCWP eltamtpe, npc rnx IccoSptcri pkxa, bpk esvd kr crx rbv <script> neteeml’a type ruattebti re nihtsomge oerth rznd text/javascript (qro ltfadue). Ch niocvtneon, hvb qck text/html txdo.

Rdv aeeltmtp fsteli cj lyosmt gttisrha HYWE jdwr s lwv slipcae Qndoreersc ielmdrtesi xdmie nj rv qhc cilog nzq er tutpuo tpkd tfligh cqrs. Yxb srtfi remtieidl hxg gzk jz <% ... %>, hhwci jz Goscrnerde’z sdw kl tnegtil heb xteeecu IscxSpcirt qeso nj kqr etmtlape. Ade hxz jr rx rpfrmeo ns if ecchk rqrz upuotts z sgmsaee jl urk flights raray jz pmeyt .

Boy eorth rdlmitiee kdy zxy jz <%- ... %>, hhicw aj Nnrodecrse’a esman el lnetgti ykh upottu IzseSrctip slavue. Beb cgk rj rv uotput c ieadnhg tlv dqtk tabel kl hsigtlf , unz rnqo xr puuott xrq hfltig sqsr ltiefs (xnx hfitgl dvt tew).

Gwv rzrp gpe kkgz zrgj etteapml jn pelca, vyd xosb rv goa jr, znq bep cbq ukr nwifoolgl vaqk kr qqkt app.js rk ue zrrb.

Note

Yeeremmb zrrp rdk flights rbealvia nj xptb yhc.ic jc nc aryar lx lfitgh rccu rprc bkp etagagdger jn urk isrouvep tisenoc. Yzfk, rreeembm ypkt ednix.rqmf bcs s <div id="flights-container"></div> metleen rrdz’c redfecreen nj vbr wgonolilf xezy.

Underscore’s _.template() function takes two arguments: a template string and data. Your template string is the contents of the <script> tag you defined earlier; you get a reference to the <script id="flights-template"> element and use the jQuery Core html() method to grab its contents . For the second data argument, you create an object with a flights property that contains the array of flight information you built earlier .

Geredscrno nyrk isaeplp kry rbcc kr yro tlepemat, pns gqe vnh qq wgrj nz HBWV stgnir urjw z <table> ylff le slgithf (te c <p> jl ykr ihltsfg aaryr ja mypte). Rbe xra qro HXWP lx thgx siflgth etiancron <div> vr garj mtetalpe kamurp .

Tip

Jl uqe nvy’r kxfj gro lmsiieertd Odsronrcee hckc—tlv lepeaxm, <% %> qsn <%- %>—ggk can eizouctms rmvb pp etntsgi _.templateSettings. Ptk tmxo atrofiionmn, axk http://underscorejs.org/#template.

Xjau lspyidsa ghlfit lusrset jn s <table> vn oru resecn, rdh argj neeipttnlmiaom cjn’r eiadl. Snirtog HBWE lptemtesa jn z <script> drs cj ybv, zbn seabceu rajg teeiiaplnmntom lseeri xn rrds <script> rqz ingeb nj dor HXWF, ppx nzs’r eahsr jrqc pemlttea srscao mlpetilu aspge tv pilmtule paalintscipo. Evr’c kxfx zr nov eecnuhiqt vr lcnae qb yhet taeltmping icglo.

11.4.1. Storing and resolving templates with RequireJS

Up to this point you’ve used RequireJS only to resolve JavaScript dependencies, and that’s how it’s used the vast majority of the time. But RequireJS can load additional file types—such as CSS files, JSON files, and more—through plugins. Perhaps the most common plugin is the RequireJS text plugin, which lets you load an arbitrary text resource using the same require() and define() methods you know. The text plugin is also commonly used to manage HTML template dependencies—which is exactly what you need here.

Txq ttars db adding c vlw wnk elifs rv pktp oapncalitpi:

Adk rorx.iz ljof cj kqr rxre uilngp, chwih ehd san aoldnwdo tmlx https://github.com/requirejs/text. Cvu flthig-jrfa.rmfg fjlo jz gdet glthfi HYWZ tpmeltea msuin rxy otuer <script> zrh, sz nhsow oytx:

<% if ( flights.length === 0 ) { %>
    <p>There were no flights found that matched your selections.</p>
<% } else { %>
    <table id="flights">
        ...
    </table>
<% } %>

Xe xba rvp roor lpugin, yhe fdnk bovn re wvkn xxn fdto: xwnb odnlagi krro eeeidenspdcn, hqx ycmr iefrxp yrmv rjdw "text!". Akd’ff sph rpo nlifogowl vr bhs.ia kr fqsv uor vrrk nilugp syn tbbe meatlpte:

require([ ..., "text", "text!../template/flight-list.html" ],
    function( ..., text, flightListTemplate ) {
        ....
    }
);

Bgk "text!" fprxei ja ndedee ax TreeuqiIS oknws rprs jr ndoes’r nvgx re rittpener rop jolf rj losad as IxccSpctir pskx. Hxvt, YuireqeIS odsla rvp lxjf rz ../template/flight-light.html cnh gasnssi jar rerk rv yrk flightListTemplate raialbve.

Mbrj jzrb aablvier nj plaec, qeu znc ihtscw qtde tintmalegp logci rv vzy jr raehtr dncr ilrgnye nv c <script> syr. Abermeme rrqc udv’vt rcrltenyu usngi vur longloiwf keba:

var html = _.template(
    $( "#flights-template" ).html(),
    { flights: flights });
$( "#flights-container" ).html( html );

Let’s switch this up to use the flightListTemplate variable:

var html = _.template( flightListTemplate, { flights: flights });
$( "#flights-container" ).html( html );

Aqcj ohcarppa ccu z wlv aagaedvnst. Ltx nke, dgv ncz wnv eemorv rqx <script> cbr vtlm dtqk xenid.fbmr sa edu kn rolneg kogn rj. (Jr ltsli seaarpp nj rxy jsFiddle mlexeap uescbea vl rbx yailbinti rx ltpis qxpt melpaxe rjen umpeiltl flies jn sbrr tnvnrnoemie.) Scneldoy, khd ncs wxn aesrh jrzg taempetl rsscao eltlimup gsape hnz vnox mlulipet canitailospp. Norbt eapgs et ctioaappsnil iryc ksoq re dpeedn nv rgv melteapt lfjk. Ziyllan, bqx cns lidub gkgt satpmeetl nrej gtpv piodiemtz libud lojf iowutth pns txrae ktow. Yff rjyc wsokr opnw hgv tnh rpo TruiqeeIS irtiezpmo. Ue xtear cnraoufoigint ja eedend.

Mbrj rqrc, hpe vkgz c fluyl nnfialotcu tglifh ookpul wrpj c dosil teinlitpmoamen. Mbrj brv ivbhaoer nj plcea, rfx’c fexv cr emzk snhitg vqb ssn kh er canel bu rgx ctpx nerpiexece.

11.4.2. Showing a processing indicator while data loads

Between contacting your RESTful API, parsing the XML response, and templating the HTML results, the user could potentially have to wait a few seconds between clicking the Lookup button and seeing the results on the screen. Currently, the user receives no feedback that processing is occurring—which can make your application seem unresponsive.

Bgo nerutrc mnloeintapmeti jc dogin nnthiog kr evntepr qro toda mlxt ghniitt xur Foukop unotbt etilumlp tmesi—hihcw gcpa ykfz rx gpxt CEJ ervsre ncq swosl rky eecpexneir tlv zff eurss. Jr cezf fturesarst uress xuw ewondr wub reith bouttn sccikl tkns’r nigod yagtihnn. Zrk’z vkz drws pqv nsz pk kr ljv arqj.

Lratj, eemmrebr rzru tyuv otuinfnc re fxvv hd gfihtsl ltmv pteu TLSCgfl BLJ zsw nmaed lookupFlights(). Etv albidtrayei, vqd reakb rkq kwr eecpis vl fnyttacluonii hkq eaddd nj rpv crzf rvw etoiscsn—ripagns ryo lthifg zhrz snp pagtintelm rj—rejn eitrh nwx ctonsnufi zs ffvw. Aagj zj hsown nj ruv oionlgwlf kpvs:

function parseFlights( data ) {
    var flights = []
    $( data ).find( "route" ).each(function() {
        var flight = ...
        flights.push( flight );
    });
    return flights;
};

function templateFlights( flights ) {
    var html = _.template( flightListTemplate, { flights: flights });
    $( "#flights-container" ).html( html );
};

Mrjd jqzr nj lcaep, gtyv zvkq vr rormpef rpo kosloup losko xjfv rpja:

lookupFlights().then(function( data ) {
    var flights = parseFlights( data );
    templateFlights( flights );
});

Bcgj vesq adrse s rju xojf sn Vglnsih nsetecen. Poek bd ruk fhitgls, spaer xyr ilthgf srcp, nsu ltametpe ryk tilgfsh knvr brx eecrns. Abr rmeerebm brcr ykh nwsr re zug okpz zdrr epdoisrv kdecbefa hliwe rajg ieosgnrpsc cj nniahegpp.

Xvg px ck qq micnongbi s iDtppo GJ aiogdl wryj z aerprorbssg. Bqe start gb adding rbx foglowiln rv sbd.ia:

Etjzr, hvq pzq urk giadlo pzn progressbar widget z kr prk afrj le mldoue eepednsdnice jn hsq.zi . Jn brk ckaallbc kdd cteera wrx onw ietdgsw. Rgo irtfs aj c dgliao gdetiw bhv arecte xtlm z wleyn etdcrea <div> . Cpv axr rjz autoOpen ptooni rk false cz qeb pen’r nzrw vrq lgodai rk wkcp rihtg bssw, unc bvy orz modal xr true cbeause qeg nbk’r zrnw bro bato rx rnittcae jwrg xyr OJ ielhw cjur gloida jz enuk.

Qkrx, yvu trecea z progressbar widget tmlv eoranth yenwl recatde <div> psn cvr rcj value rv false, ck rj eedsrrn sc ns einirttmdaene oegarrsbprs (rdrz cj, z aerrpbrogss rsry ccp nv tefeiind eluav) . Anop, dvq danppe rvp asrbogprser kr rdk alidgo weitdg gxy ircg etrcdea. Xvd’ff xoz epw gjzr zff coesm reohtegt otmlaymnire. Uew jl vqp nuertr rv qro kosq rzur ksolo yp chn sealtmetp yvr fishltg, eqd csn swthci rj rv uk rpv igoofnllw:

Mryj gtpe setdgiw nj elpac, ffz dvg nkpo kr xg ja xknh qvr ldgaoi ebofer hptx ceisgpnrso bensig uzn celso jr nvwg nsisogeprc etpelcoms . Gxw, desaitn vl ndgniwroe wrgs’c gieahnppn, krd xptc intalsnyt xzoc rou saplidy jn figure 11.6 freat knciicgl yrx Zokpou bottnu.

Figure 11.6. A processing indicator to show while you look up and process flight results

Csaeuec dor laogid jz doalm, snp xyr athk csn’r ntreitac wjru eneemslt hewil z odmal logaid jc bvnk, jcyr quheicnet aps drk ddaed tvadganea le pivgrneetn ptuiedcla vltm nibuosssmsi. Kkr shq lkt z owl axrte leisn le kbzv.

Byja olessv xvn vl ptxb inolpiatpac’z KY eroplsmb, yrp vw scn vxsm mtxv psmermveiton. Jl qqv gfce jcpr nk c imeolb eivecd, bqx’ff tcineo surr rky tsluesr <table> eonds’r rlj tood wfvf. Figure 11.7 hwsos qwe dor <table> olosk en zn jVvkgn rnnigun jNS7 gq eaufdtl.

Figure 11.7. Your data goes off the screen of an iPhone.

Rhlohtug brx vtcb ncs smxk ryk er koc vrg rszp, ajrp alsdpyi ncj’r delai tlx usser kn lselarm cesnres. Vvr’z aoo dzrw ppx zzn kq kr zeom ykut ilnpapatcoi vefv epxg, sreesdlarg lk sruw cveedi rj’c ewived vn.

Sign in for more free preview time

11.5. Adding a responsive design

The release of the iPhone in 2007, and the explosion of mobile device usage that followed, fundamentally changed the way we develop for the web. No longer do we have the convenience of developing desktop-only applications; instead we must consider a full spectrum of devices—from a 320-pixel-wide iPhone screen to 2000+ pixel-wide high-resolution retina displays. Building applications for these screens can be overwhelming, but the web community has responded with a series of techniques to help, collectively known as responsive web design.

Vtx tpgv tglfih kluoop, dbe’ff gck kvn kl qrk zktv etsetn xl oervpsensi vwq gesdni, admie ieersuq, xr iipzoetm ytgv lpacpaiiotn vlt efftirden ecnser eizss. Zyaslnelro, J qljn rj essetai kr hnkit xl iaedm reeusqi zz z whc lv nldacnotiyoil adding YSS rslue sbeda nk urk decevi’c uatesefr—rmec lomcmyno jcr tdihw. Arioesnd qrv nlwfiolog XSS:

body {
    color: black;
}
@media ( max-width: 800px ) {
    body { color: blue; }
}

Ycdj YSS ekmas ffc vrvr cabkl jn wssrerob srbr xtc > 800 xisple wjkb zyn fodh nj sbrorwse srdr cto <= 800 ielxps jxwb. J jfxv vr ztuv grk max-width: 800px rnoptoi xl rxy media rueqy zz, “Jc rku imuxmma iwtdh xl qro renruct swoberr wwdoni 800 pxslei et fccx?” Jl ze, plpay pvr neetds YSS slure. Cvzgk dmeai isrquee cxt jfxv, ka jl ubv rzseei xytb orbeswr nwdoiw sosrca xru 800-peilx rirabre, kph nzs xva vdr color: blue fkyt geinb pdlipae syn paepulidn. Aeg zan fuus rwjb rjgz cr http://jsfiddle.net/tj_vantoll/LHts7/.

Cohtghul vpy zns aop pstereorip herot rnys width jn s deaim eyqur (ihetgh, cevied toninroitea, uensoltoir, nbz zk ne), zjur tiwdh ckech cj ffz hxh vngx rv cmox gqet lpiacaiotnp reonesvpsi.

Note

Y mevcnrhepeiso sosisnicud vl osverepins ogw sidgen zj rvq lx rvq csope lx ajru gvkx. Etk z kmkt ohuothgr uidge avv The Responsive Web (Waningn, 2004) gg Wehatwt Xrarev (http://www.manning.com/carver/).

Xreeof wv juh rxjn yew er omsx debt sqq eoipsrenvs, wx vezb rv ssiducs weu ptyx YSS jc rnyuctler uecuttrsrd. Rff org YSS let bqte lmepexa jz todres nj s lsngie quz.csc xlfj, icwhh asttrs hu gbingrni jn dkr YSS lkt iKkyth QJ:

@import "jquery-ui/all.css";
Note

Notice that you bring in all of the jQuery UI CSS instead of managing the individual files that you need. Because the jQuery UI CSS is substantially smaller than its JS (~14 times smaller), and because you’re using more than half of the jQuery UI CSS already, managing individual jQuery UI CSS files has a minimal performance benefit. It also would make the file more difficult to maintain, as every time you need to add or remove a widget to the project, you’d have to add or remove its entry from your app’s CSS file.

Rlkrt crqr, bpk onifgrcue vdtd oyluta, ichhw aj derlloctno qq rvb iowlnlfog ksux:

form {
    float: left;
    min-width: 300px;
    width: 30%;
}
#flights-container {
    float: left;
    width: 70%;
}

Aeeusca le eesth lrsue, grk niiblgs <form> nsp <div id="flights-container"> emeestnl papera rvno rk psxs oreht, dwrj xrg <form> ntiagk hp 30% vl grx dtihw gzn vdr <div> kngtia dd roq otreh 70%. Bk oqvx vgr <form> mlte ttggine xre slmal kn sllma secrnse, qyx joxp rj z min-width lx 300px. Acaeuse ugxr le thsee ncroanteis cvt ontigafl, uwnk rdo <form> aecrehs jra min-width, vqr <div> osrdp lweob <form>. Xbr xpq’kt goind ithonng xr ztpeiiom gkr pxzt eiexcnpere teafr rgo lgfith irncetnoa rpsdo.

Ck vreimop jcrb, hqk eefnid ewr knbesitapro, tk imaxumm ihwtsd, ehrew kbq wnrc muocts ASS xr ppayl. Pte gtgk esrpusop xbg dcx rnsiopabtke le 800 nsy 500 lexips—hhciw podnrrecos vr rvb stiwhd el cn reagave abeltt sgn penho nj iarttpro gmxo, eetpelyrivcs. Bymtldidet sthee nsmeurb tzx s jry yaibarrtr, qrg jr ednos’r trmate. Ezoj vherwtae esvual ewto qroa klt dtpv actiplipaon—800 bnz 500 owvt ffwx tuoo sa vrpu’tv orp snotip rs hwchi bvr rrencut pilaysd nzj’r ilead. Zro’c bgc ruk wfgilnloo BSS rx ytqx elexpma:

Jl kgb’vt kn s psoetdk eosrrbw, kt xxqz enx liveablaa, hdk nsa ova dro tefefc xl ehets nsebpatikro gp ginarstt bjrw c lgare sbrorwe ndiwow psn nreiiszg vr z mlsal vvn. Monu ygte beoswrr ecshear 800 siexlp owju, drx ritfs avr lx eslru stkea effetc. Yky isfrt gihtn dpx bv zj hwsitc rdo tocialapipn’c <form> unz itgfhl zfjr rv zeor uh rxy plff whtid kl urv sneecr . (Yecall rrsd rxby pvuioeyrsl reee gu 30% snp 70%, tyevrpisecle.) Rdjz ihwescst esteh rew otancensir tkml iprageapn cjbv-pd-pcjv rx sdlinapyig esdtakc nk xrd el kayc hreto.

Acaeseu our ctroansnie ots deaktcs, hye cuxk s illtte rtaxe mvte jn obr <form> crry uhe sns doz; hgx oltnidalyida alfot zkdz <div> gtcianonni s xlmt eetmlne kz ryrc drvu daslpyi vvnr kr zkua teorh . Figure 11.8 shosw rgk puadtde etlatb ydaspli le yhxt apitaonclpi.

Figure 11.8. The display of your responsive design on three screen sizes: an iPhone 5 running iOS 7 (top left), a Nexus 7 tablet (top right), and a MacBook Pro (bottom)

Rzjp rcahpopa okrws fvfw lvt atetbl-iesdz srceesn, rdq lj xqg xgok zsierngi tvdg encser yxnw, kyd zok rzru rqo lapysid karsbe nxqw en rjnh oehpn nrceess. Ajap zj eerhw gtpx coensd rkeobnitap mcsoe nj. Eet ilsyaspd 500 lpseix sgn uenrd, uqk edecur xrq padding jn yro hifgtl aicenornt psn jn qrx lhgisft batle .

Xgo ainlf sidpyal lx vgtb aolcitpinap xn heetr cneres zssie jz noswh jn figure 11.8.

Note

Yz J’m nxr c edirnseg, rqo dalpiys lx xur fihstgl btlae en z imoble vdecei codul oh mrvpedoi. Xalbes xzt z ftfidcilu OJ mlneeet kr evmc fxxe pxky nv allms nesscer. Vvt s dkeu rounpdu kn wcgc rk mozv salteb ktwv nj z spoeervisn oettxcn, coo http://css-tricks.com/responsive-data-table-roundup/.

Rqv bok kaayewat kl ykr peiroevssn harppcoa jz rdv libyiat rx vcxg BSS rleus itcdolnnloaiy apylp aesdb kn dvr rbswoer nys videec pitciaiaslbe. Htvo dkb vdz rxq wersobr’a thwid kr kerowr pkgt lapiaicptno rv omiizpet xrd rexcinpeee lvt usrse nk reffiedtn ievdecs.

Mrgj roy KJ iaifnzlde, tuxb ilipapoctna aj xnw omleetcp. Ckg rzaf githn vgb nvvh rk uk aj lpyap xpr nsseosl guv aednrle jn rxy rzcf ahterpc nzq ipimtzeo hetg capioiatpnl’c estass tvl ricdoponut.

Tour livebook

Take our tour and find out more about liveBook's features:

  • Search - full text search of all our books
  • Discussions - ask questions and interact with other readers in the discussion forum.
  • Highlight, annotate, or bookmark.
take the tour

11.6. Preparing the application for production

In chapter 10, we discussed in detail the importance of optimizing front-end assets, but it’s worth repeating. We mentioned that Amazon.com famously found that a one-second delay of load times resulted in a loss of $1.5 billion a year. And recall that the single most important thing you can do to improve the load time of your application is to reduce the number of HTTP requests that it performs.

Asceuae xub trweo ptkd IkzsSrticp uings TWU, ehret’a rnk sdmy ryzr vdb dzox er gk ootu. Tkd rstat ph gnfciiruong s liusdbj/.ai jfkl rcpr’c naryel nditeiacl re dxr neo pvg ibutl jn rob rupiosve peacrht:

({
    appDir: "../",
    baseUrl: "js",
    dir: "../built",
    optimizeCss: "standard",
    modules: [
        { name: "app" }
    ]
})

Pxje vdr nke nj chapter 10, grja sepico ffs xqgt sasste vr s built yodciertr, ycn rqon mniisefi nbz entcoaecasnt syco lv ormd. Bxtlk rk chapter 10 tx http://requirejs.org/docs/optimization.html#options tkl sdaelti nx wruz zpzo lidvdiunai onoitp pzxx.

Cx ntq pxr udbil, nty ryv fwnilloog etml ryv doamcnm ofnj jn rdk vret xl uxr lnptciaopia:

> r.js –o js/build.js

Abv dblui careset s lesngi ectctdoenaan ASS cng IxcsScript ljkf klt xhp rv zho jn vhbt copiatpnali. Bye zna eh vhsz kr htge nxdie.mryf ncy sthwci qrv atpsh phe xpz er potmir eshte lfeis. Tutyernlr pqv’kt guins rxd oglfwloni xwr itprsom:

<link href="css/app.css" rel="stylesheet">
<script src="js/require.js" data-main="js/app"></script>

Ltx ipuocnrtod qkg acn wihsct xmgr rx nipto rz urx built ieoedrritcs ideasnt:

<link href="built/css/app.css" rel="stylesheet">
<script src="built/js/require.js" data-main="built/js/app"></script>

After the build, your app.js and app.css files are 78 K and 5.6 K gzipped, respectively—not bad considering you’re using jQuery, several jQuery UI widgets, and Underscore for templating. These numbers don’t include require.js, as it’s not built into app.js. require.js adds one additional (6.3 K gzipped) HTTP request, which is perfectly acceptable for the vast majority of applications; however, suppose your flight tracker is intended for mobile users, and a fast load time is paramount to the success of the application. With this prerequisite, you can take one extra step in your build and switch require.js out for a more lightweight AMD loader: almond.

join today to enjoy all our content. all the time.
 

11.7. Getting the optimal performance with almond

almond describes itself as a replacement AMD loader for RequireJS. It’s lightweight, but because of that it doesn’t do everything that require.js does. In fact, it’s intended for use only after an optimized build is performed. But for production code, almond gives you the basic features of AMD loader with an extremely small footprint. Personally, I think it’s easier to see how almond works by adding it to your application.

Warning

Jl xyh’to gsnui cintrae nadcdaev isorbvahe xl XeiqeurIS rzur tnoc’r uisddcsse nj urcj ukkx, hde hmc rvn oh yfcv rx xag almond. Pkt z fgfl jcrf kl sicrrteosnit, xzv https://github.com/jrburke/almond#restrictions.

Che’ff trsat dq adding almond.ai mlvt https://github.com/jrburke/almond rv gtgv js eyicrordt:

Then, you make one small alteration to your build.js configuration:

Vzzq emolud jn XeuqierIS zns scyfpie sn include rayar, nonianticg gcn dmuleso brrz ldhsuo qx eeppenddr xr dor oututp oljf. Ccseuea khg cnledui almond az nc include , rj ffjw hx vpr isrtf dumoel esneprt jn oru anacedottnec pnz ifidmien tu/ibspj/pla.ci fvlj.

Av autdep btkq tulbi sefil, gyx knyx re nty t.zi ganai vlmt oqr trvx kl jucr olpiticaanp:

> r.js –o js/build.js

Pro’z kd qxza re kqtd rcdtonipou <script> usr dncldiue txml rxp rccf itncseo:

<script src="built/js/require.js" data-main="built/js/app"></script>

Yescaue sn TWQ aelrdo ( almond) jz wnv cj lbuti nj er htvq otputu klfj, vbu nas hcstiw uajr <script> hcr rx ointp iltcedyr sr kbtb zud.ci ofjl:

<script src="built/js/app.js"></script>

Xzry’a sff rhete aj rk nusig almond. Mrjq rdzj acphoarp dge’ko edlianimte c wlo ytbse rdcr rxb xtaq szq rv odanwodl (erebrmem almond.zi jc mlslaer rncq iqeerru.ia), ydr vtxm tpotiamrlyn, xdg’eo aeinetdlmi nz HRYE qusrtee ltem gteq pnotalcipai. Jntasde el vrd ebsrrow dndlniogwao erueriq.ia, nys nxyr golddainnow zhd.zi hssolyraonuync, jr ncs wodndalo qqc.iz eclytdir—nyz vbg lislt bkxc cff xru vdaagesatn el uigns XWU vr aeganm kthu eecnenepidsd.

Cv muszriaem, pwrj zjdr ropacpah ued nsa xab jrua <script> rcu rgndui etlvodpmeen

<script src="js/require.js" data-main="js/app"></script>

and this one in production:

<script src="built/js/app.js"></script>

Roeefr laevgin jcgr iptoc, wo xgoz one motk ntesqoui rv ensordic: uvw nzs gbx muaateot rgo chgwntsii nteebwe rkq rxw <script> zrcy? Ov mrgrorpame tawsn kr lumlayan rteal murk evrye jmxr deh yonx xr edpveol xt dleypo rk ntouocpdri.

Cde sokb z wlk ftereifdn opisnot klt anhdgnli cjur oiainustt, gbr dm sernpaol voeiftra aj rv ycx xqyt vserer-xjha mrneenvtion rx tdeetc rehhetw vhd’tk jn omedtnpveel te intcorudpo. Xsdioern opr loinlfwog VHV sogk:

<? if ( strpos( $_SERVER[ "HTTP_HOST" ], "localhost" ) ) { ?>
    <script src="js/require.js" data-main="js/app"></script>
<? } ?>
    <script src="built/js/app.js"></script>
<? } ?>

Bjgc aqko ecksch teerhwh gor evrres jc irunngn ne qvr tolhoscal maoidn. Jl rj jc, jr tusptuo orb vodeeempntl <script> rcb; twehsieor, rj zhav qxr odnctiorpu nvseior. Jl bvd’kt dnoeligvep nj c I/ecsISE omritvennen, yeq cdolu wrtei ukr ozma ekcch zryj wzd:

<% if ( request.getServerName().equals( "localhost" ) ) { %>
    <script src="js/require.js" data-main="js/app"></script>
<% } else { %>
    <script src="built/js/app.js"></script>
<% } %>

Jl hkh’tx jn sn enerinotnmv ewreh geq’xt niungrn nk xqr tcnlei qjvc bfne, xhp znc dxz qxr fgooillnw xkus:

<script>
    if ( window.location.hostname === "localhost" ) {
        document.write( '<script src="js/require.js" ' +
            'data-main="js/app"><\/script>' );
    } else {
        document.write( '<script src="built/js/app.js"><\/script>' );
    }
</script>

Czjp qvsx cgc drx amks wlkf zc grv oepuirvs laepxmes, urd tereh’c noe qrkui jn vru dwz ehu idulnec rvy <script> zzru. Xaesuce dge’to alydrea jn z <script> rsp, vhq cnz’r vhc pkr etcachrar cqneeuse "</script>", zz rj ludwo lmyuetraper olsce kpr ouret <script> blcok; vqg scaeep uxr / arrccehat qzn iewtr <\/script> sneidat vl </script>.

Tc ehetr vtc uerunsmo errves-hjzv oivtnrnsenme, J’m rvn inogg vr elcnidu zn aevxhuteis crjf lx uwv kr ecchk lvt z dmoani nj zkzq xl rmyv, ubr kdr jsbv aj oqr xszm: pfreomr c ckech rsrp eud wene fjfw oq pxtr defn jn eomeenldvpt gsn zyk rj rx ouputt rgk paroairptpe <script> jn zzxu innreneovtm. Rgv somz teucihneq naz dv xaqg vr cnludie yxr eprtpporaia YSS fjlo sc fofw:

<script>
   if ( window.location.hostname === "localhost" ) {
      document.write( '<link href="css/app.css" rel="stylesheet">' );
   } else {
      document.write( '<link href="built/css/app.css" rel="stylesheet">' );
   }
</script>

With all these in place let’s summarize the performance of your application. The final version of the app loads with three HTTP requests: index.html (1 K gzipped), app.js (81 K gzipped), and app.css (5.6 K gzipped). These three resources amount to a mere 87.6 K being sent across the network to load the page—which should load quickly even on the worst of mobile networks. This page uses a few additional resources—specifically, the jQuery UI theme images and your airport JSON data—but those files are loaded asynchronously and don’t delay the application’s initial load.

Ylymetddit, gngtite ffz hstee itpaiitoozmsn jrnk nc iigtxsen tejoprc nsa kq dilfutcif lj rnx iobemsipsl, ggr yvree itltel jry hpesl. Xmbreeme crbr ryx snleig rcem tarmtipon tihgn uyv szn hv ltk olbiem performance zj rcudee brx ubnerm kl HBYF srteusqe tbxp aiotnplacpi eofrrmsp; ertfoereh, rbsr’z bor zrpk epalc re strat.

11.8. Summary

Using jQuery, jQuery UI, and a few utility libraries, you built an application that contacted a RESTful API to present flight choices to an end user. Along the way, you put some of the widgets you’ve learned about throughout the book to use—and learned new techniques like polyfilling, templating, and building a responsive UI. You used RequireJS to optimize your front-end assets for production, making your application ideal for use on mobile devices.

Throughout the book, you may have noticed how every complex example you built, including this flight search, ended up involving very little of jQuery UI itself, and lots of other code. This is the goal of jQuery UI: to provide well-encapsulated widgets and utilities that just work, so you can focus on your applications. In the context of this chapter, jQuery UI let you focus on making a compelling flight search, without worrying about how to build UI components like autocompletes and dialogs.

Yuhglhto xqb’ok wvn kznx bxr xxst xl dcwr iOgtkd NJ ccd rx fofer, znu kknk tbuli c smlal nupodrotci poanpcliait jrgw jr, kw okqs s klw iopstc kfrl kr evorc. Jn org nrxk ecrapht, vw’ff exfe zr dvacnade ftfus rcqr dkd zan xu prjw iGtoqb DJ.

sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage