Front-End Fundamentals: A practical guide to front-end web development
Transcript of Front-End Fundamentals: A practical guide to front-end web development
Front-EndFundamentals
Apracticalguidetofront-endwebdevelopment.
JoeFenderandCarwinYoung
Thisbookisforsaleathttp://leanpub.com/front-end-fundamentals
Thisversionwaspublishedon2015-02-11
*****
ThisisaLeanpubbook.LeanpubempowersauthorsandpublisherswiththeLeanPublishingprocess.LeanPublishingistheactofpublishinganin-progressebookusinglightweighttoolsandmanyiterationstogetreaderfeedback,pivotuntilyouhavetherightbookandbuildtractiononceyoudo.
*****
©2014-2015JoeFenderandCarwinYoung
TableofContents
AbouttheAuthorsJoeFenderCarwinYoung
IntroductionWhoThisBookIsForAnOverviewofThisBookExampleCodeContactingtheAuthors
GettingStartedExpectationsofaFront-EndDeveloperSettingUpYourLocalEnvironment
FrameworksModel-View-WhateverArchitectureSingle-PageApplicationsAngularJSBackbone.jsEmberSummary
StylingResponsiveWebDesignAuthoringMethodologiesPreprocessorsPerformanceandCSSStylingSummary
DependencyManagementWhatisaPackageManager?Node.jsandtheNodePackageManager(npm)BowerBundlerDocumentationSummary
AutomationTaskRunnersGrunt
GulpCodeKitYeomanOtherTipsSummary
Summary
AppendixAccessibilityAutomationDeploymentLearningResourcesFrameworksLanguagesOnlineServicesPackageManagersPerformanceSoftwareStylingTemplatingTestingVersionControlWebServers
AbouttheAuthors
JoeandCarwinstartedworkingtogetherin2013atLullabot,aninteractivestrategy,designanddevelopmentcompany.
AtLullabot,theyworkwithdecisionmakers,developers,anddesignersatallstagesofawebproject.TheyhaveguidedthedevelopmentofsitesforclientssuchasMarthaStewart,TheGRAMMYs,MTVUK,SonyMusic,LifetimeTelevision,TheGeorgeLucasEducationalFoundation,LeoLaporte’sTWiTnetwork,andmanyothers.
JoeFender
JoeFenderlivesinLondon,UKandsplitshisfocusbetweenfront-endandback-enddevelopment.SpecializinginPHP,AngularJSandDrupal,JoewastheleadarchitectatStudioUmi,thelargestDrupalshopinJapanbeforejoiningLullabottoworkonawesomewebsitessuchasDrupalize.MeandMSNBC.
Inhissparetime,Joelikestocollectshoesandworkonsideprojects.Butneveratthesametime.
CarwinYoung
CarwinYoungisanexpertfront-enddeveloperlivinginSpringfield,Missouri.Hehasover8yearsexperienceworkingonthefront-end.Hewasthesecondfront-enddevelopertoworkatLullabotwherehehasplayedakeyroleonbuildingwebsitesforclientssuchasMSNBC,BravoTVandtheGRAMMYs.
Carwinlovestosharehisexpertise.HehasspokenontopicssuchasRapidDrupalDevelopmentatDrupalCampChicago,isaco-organizerfortheSpringfieldDrupalmeet-
upandisaregularcontributortotheFront-endRapport,anonlinemagazineforfront-enddevelopers.
Introduction
Front-endwebdevelopmentisevolving.LonggonearethedaysofwritingstaticHTMLfileswithself-containedCSS.Aswebdevelopersinthismodernworld,weneedtounderstandandutilizeanendlessnumberofframeworks,plugins,techniquesandmore.Knowingtherighttoolstouseandwhentousethemiskeytobuildingsuccessfulsolutions.
Despiteourbestefforts,itcanbedifficulttokeepupwiththespeedofadvancingwebdevelopment.Wesureashellcan’tslowitdown.Andwhywouldwewantto?It’ssoexcitingtonotknowwheretechnologyisheadedandwhatcouldbearoundthenextcorner.Weshouldbothprepareforchangeandembraceit.Andthatisexactlywherefront-enddevelopersexcel.Webuildwhattheconsumerseesandbecauseofthat,wewanttousethemostcutting-edgetechnologiestocreatethebiggestimpact.
Thisbookcametobefromthelessonswe,theauthors,havelearntthroughtrialanderrorwhilstworkingonfront-enddevelopmentprojectsrangingfromenormoushigh-trafficsitessuchasMSNBC,TheGRAMMYsandBravoTVtostartupsandexperimentssuchasDrupalize.MeandBracketCloud.Wewanttosharewithyouthetoolsthatweuseandthestandardsthatwefollow.Ourultimategoalisforyoutowalkawayunderstandingthecoreconceptsoffront-enddevelopmentsoyoucanconfidentlygoandworkonyourownprojects.
Thisbookissomewhatopinionatedbasedonourexperience,soyou’llfindsomeofthecontentheavilygearedtowardsaparticularwayofdoingthings.Ofcourse,therearemanywaysofaccomplishingthesametaskintheworldoffront-enddevelopment;that’swhatmakesitsoaccessibleandyetsocomplicated.Learningonewayofdoingsomethingcanmakelearningthealternativesamuchmorepalatabletask,soweencourageyoutolookintoanyandallofthetechnologiespresentedhereinandevengoontoseeifsomethingelsemightbetterfityourneeds.
WhoThisBookIsForThecontentinthisbookisaimedatthosewhoarenewtofront-endwebdevelopment.Regardlessofwhetheryouareabeginnerorexpertdeveloper,theconceptsoutlinedinthisbookareessentialtoembracingtherapidlyevolvingweb.Wewon’tbecoveringthegeneralbasicsofwebdevelopmentsoyouwillmostlikelywanttogetuptospeedonthefundamentalsofwebprogrammingsuchasHTML,CSSandJavaScript.Wewillalsobeutilizingthecommandline,sosomebasicknowledgeofhowtousethatonyouroperatingsystemisrecommended.
Ifyouhearyourselfaskinganyofthefollowingquestions,thisbookwilllikelybeveryusefulforyou!
“Whatactuallyisfront-enddevelopment?”“HowcanIbeamoreefficientwebdeveloper?”“WhatisGrunt/AngularJS/SASS/<insertpopulartoolnamehere>?”“HowdoIfitallofthesefront-endtoolstogetherintoaproject?”“HowcanIbuildafront-endapplicationthatcanscale?”
AnOverviewofThisBookEverywebprojectthatyouworkonisdifferentandthereisno‘onesizefitsall’setoftools.However,throughoutthechaptersinthisbook,we’llintroduceyoutosomeofthemostpopular,usefulandpowerfultoolsusedinfront-endwebdevelopmentsothatwhenyou’redonereadingyouwillbeabletoapplytheconceptsandtechniquestowhateveryouareworkingon.
1.GettingStartedTheaimofthefirstchapterofthisbookistohelpgetyouacclimatedwithfront-enddevelopmentingeneral.You’lllearnwhat’sexpectedfromafront-enddeveloperandgetashortoverviewofthetypesofprojectsafront-enddevelopermightbetaskedwith.We’lleventakeyouthroughsettingupyourcomputerforlocaldevelopment.
2.FrameworksThischapterjumpsrightintosomeofthemorepopularJavaScriptframeworksandtemplatelanguagesinusetoday.We’llcoverthedetailsofwhatexactlya‘framework’isandtalkabouttheMVCpatternbeforegettingintothedetailsofinstallationandpracticalusageof3popularframeworks:AngularJS,Backbone.jsandEmber.js.
3.StylingThere’salotmoretostylingthanjustthrowingCSSatyourmarkup;thatjustleadstoabigmessofunreadablespaghetticodesprinkledwith!importantparmesan.Herewe’llcoverCSSpreprocessorslikeSass,organizationalmethodsforyourstyles,andthekindofimpactyourCSSmayhaveonaproject’sperformance.
4.DependencyManagementGettingyourwholeteamonthesamepagecode-wisecanbechallenging.Heck,evenmakingsureyou’reusingthesameversionofthatawesomeRubygemafteranupdatecanbeapain.Thischapterondependencymanagementcoversanumberofreallyhandyfront-endpackagemanagerslikenpm,Bower,andBundler.You’lllearnwhatapackagemanagerisandhowtouseiteffectivelytokeepyourprojectontrack.
5.AutomationDoyoulikedoingthesamerepetitivetasksoverandover?No?Neitherdowe.
Theautomationchapterisaboutjustthat,automation.We’llgooverhowyoucanremovethetediumfromfront-enddevelopmentandjustgettowork.Inthischapteryou’lllearnhowtoperformtaskslikecheckingyourJavaScriptorCSSforerrorswheneveryousavea
file,minifyyourcodetoshaveoffsomefilesize,andevenhowtoautomaticallyrefreshyourbrowserwheneversomethingchanges.Onceyougetstarted,you’llneverlookback.
ExampleCodeThisbookcontainsmanycodesnippetsfordemonstrationpurposes.Codemayappeardifferentlydependingonthedeviceyouareusingtoreadthisbook.Hereisanexampleofacodesnippetsoyouknowwhattoexpect:1vargulp=require('gulp'),
2uglify=require('gulp-uglify');
3
4//ThistaskuglifiesourJSfiles.
5gulp.task('compress',function(){
6gulp.src('src/js/*.js')
7.pipe(uglify())
8.pipe(gulp.dest('dist'))
9});
Youwillalsonoticethatwereferencecodeinlinelikethisthroughoutthebook.
ContactingtheAuthorsIfyouhaveanyfeedbackorquestions,pleasefeelfreetocontactusviatheFront-EndFundamentalsGoogleGroup.
Youcaneithercreateanewtopicviathewebinterfaceoryoucansendanemailtothemailinglistaddress:[email protected]
Wewilldoourbesttoconsidereachandeverynewmessagereceived.Pleaseunderstandthatdependingonthevolumeofrequests,itmaybedifficultforustorespondimmediately.
GettingStarted
Traditionally,whenwebdevelopersstartedbuildingwebsitesinthe90s,theywouldwearallthehats.Theywouldsetuptheserver,writealltheserver-sidecode(inPHP)thatwouldeventuallyoutputHTMLandfinallysprinkleonsomeCSStomakethingslookpretty.Ifyouhaveabackgroundinwebdevelopment,thismaysoundveryfamiliartoyou.
Duetotheadvancementofmodernwebtechnologiesandcomputerhardware,we’reabletocreatemoresophisticatedexperiencesthanever.Becauseofthiscomplexity,theroleofawebdeveloperhasnaturallyevolvedandsplitintoseparatedisciplines.Specifically,alinewasdrawnbetweentheback-endandfront-end.Back-endbeingserver-sideprogrammingandfront-endbeingclient-side.Front-endshouldnotbeconfusedwithwebdesign.Althoughfront-enddeveloperscanbenefitfrompossessingdesignerchops,theirroleisn’ttodesignawebsitebutrathertoimplementadesigngiventothem.Dependingonthesizeofteamyouworkwith,youmayfindthelinesbetweentheserolesblurred.Youshoulddefinitelyreadthisgreatarticleontheconfusionwhichrevolvesaroundthedefinitionofafront-enddeveloper.
Thedividebetweenback-endandfront-endisstillnew.We’renotyetcompletelycomfortablewithit.Someback-enddevelopersstilldoalotoffront-endwork,orvice-versa.Sometimesitcanbetrickytofigureouthowtocommunicatefromeithersideofthedivide.However,whenyoulookatjobpostingsforwebdevelopersthesedaysyou’lloftenseethemlistedaseitherforaback-endorfront-endcandidate.Theindustryasawholehasembracedtheconceptofroleseparationforwebdeveloperswhichcanonlyleadtogoodthingsaswecannowspecializeinourdisciplineofchoice.
Beingafront-enddeveloperissoexciting.You’reonthe‘forefront’sotospeakandhaveadirectimpactonhowtheuserfeelsaboutandinteractswiththeproductorservicethatthewebsitesyoubuildportray.Youcaninstantlyseetheresultsofyourworkandbecauseofthisyouwillreceivealotoffeedback,sometimesharsh.Buttakeitwithagrainofsaltanduseitconstructivelytoimproveonyourselfandtheworkthatyoudo.
ExpectationsofaFront-EndDeveloperWhatyou’regoingtorealizebythetimeyou’vefinishedthisbookisthatthefront-endworldisbig.Therearesomanypeopleworkingtowardsthesamegoalbutwithdifferentpointsofviewonhowtogetthere.Thatisthebeautyofopen-source.You’regoingtofeelalittleoverwhelmedbutdon’tletthatfazeyou.Learntolovetryingnewthingsandembracechangeasitcomesandyou’regoingtodoreallywell.Hereisasummaryofthevariousthingsyoushouldconsiderwhentryingtobethebestfront-enddeveloperyoucan:
Ratherthansettingouttomasterasingletool,trytolearnalittleabouteachofthemandhowtheyworktogetherfirst.Whenyou’vegraspedthebiggerpicture,itismucheasiertospecialize.Ifyou’rereadingthisbook,you’redefinitelyontherighttrack.Lovetolearn.Tokeepupwiththespeedofevolvingwebtechnologies,you’llhavetopickupnewthingsregularlyoryoumayfallbehind.Beagoodcommunicator.Evenifyou’reworkingalone,you’llfindthattherearemanyopportunitiesonlinetointeractwithotherdevelopersandgetsupport.Googleisyourfriend.Whenanyoneasksme“Howdoyougoaboutsolvingaproblem?”myansweralwaysstartswithGoogleandthatisperfectlyOK!Googleisthekeeperofmuchknowledgeandyoushouldlearntouseitinabundance.Don’treinventthewheel.You’regoingtofindsomeoneelsehasprobablythoughtofasimilarsolutiontotheproblemyou’refacing.Itmaynotbetheperfectmatchbutbybendingyourprojecttomakeroomforpre-existingsolutions,you’realsobenefitingfromthecommunityandsupportthatcomeswiththem.Cross-everything.You’reresponsibleformakingsurewhatyoubuildactuallyworksandisn’tdependentontheuserhavingaspecificbrowserordevice.Buildcross-browser,cross-platformandcross-device.Askoutsidetheboxquestionslike“WhatifIamaccessingthiswebsitefromamobileonaslow3Gconnection?”or“WhatifIamviewingonaniPhone4inlandscape?”.Testandthentestagain.Testforvisualregressions(e.g.usingWraithorPhantomCSS),rununitandfunctionaltestsonyourJavaScript(KarmaorJasminearegoodplacestostart)andtestforgeneralperformance(readtheYahoobestpracticesandtryYSlow).Awelltestedsitemakesallthedifferencesomakesuretocreatetimefortesting.Thinkaboutaccessibility.Forexample,askyourselfhowwillablindpersonorsomeonewhoiscolorblindinteractwithyourwebsite?Hereisausefulchecklistofthingstothinkabout.Documenteverything.Notonlydoesithelpeducatenewdevelopersaboutthetheorybehindyourcodebuttheactofwritingcommentswillhelpyoutospotflawsinyourlogic.Savetimewithautomation.Therearealotofrepetitivetasksinfront-enddevelopmentthatcanbeautomatedtosavetime.Wefinditsoimportantthatwe’veevenwrittenanentirechapteronautomationinthisbook!Learnaboutcollaborationtools.Asawebdeveloper,youaregoingtouseGithubatsomepoint.SoyouwouldbebetterofflookingintoGitanditsvariouspopularworkflowssuchasgit-floworGithubflow.ReadaboutContinuousIntegration.Althoughyoumaynotbeabletoincorporateitintoeveryproject,learningaboutitsprincipleswillhelpyouapproachcodedeploymentinamoreintelligentway.Haveagoodeyefordesign.Asomewhatoverlookedassetoffront-enddevelopersisbeingabletoprovidefeedbacktodesigners.Seeingaswe’retheoneswhoactuallybuildtheinteractions,wewillprobablyusethewebsitealotmorethanthedesignerseverwill.Theywillgreatlyappreciateanyfeedbackyoucangivesodon’tbehesitanttopointoutflawsintheUI.Learnaboutserver-sidewebdevelopmentconcepts.Althoughyoumaynotbetouchingtheback-end,itcanhelpifyouknowwhatisgoingonandhowthedatayou
areusingactuallygottoyou.
SettingUpYourLocalEnvironmentAlotoffront-enddevelopmenthappensoneitheralocalmachineoranon-productionwebserversetupformultiplepeopletouse.Longgone(hopefully)arethedaysofjusteditingCSSonalivesitebyoverwritingCSSfilesoverFTP.Asdevelopers,weprefertodoeverythinglocallysowedon’tneedtoworryifwe’regoingtoaccidentallyruinourproductionsite.Alsoit’sjustplainfastersincethere’snoneedtoconnecttotheinternet.
Settingupyourlocalenvironmentcouldprobablybeabookinitself(thereareatonofviableoptions),butwe’regoingtocoverwhatwethinkisprobablytheeasiestmethod:MAMP.
MAMPMAMPisaneasytouse,freeapplicationforMacandWindowsbasedmachines.MAMPwillinstallPHP,MySQL,andtheApachewebserverinasimpletomanageenvironmentandyoucanstartupyourwebserverwithaclickofabutton.
Installation
Let’swalkthroughafirstrun:
1. ThefirststepintheinstallationprocessistogodownloadtheMAMPinstallerforyouroperatingsystem.
2. Oncethedownloadcompletes,runtheinstallerprogramthatyoudownloaded.3. Walkthroughtheinstallationprocess.Onceitfinishes,you’llbereadytostartusing
MAMP*.
*:TheMAMPinstalleralsoinstallsMAMPPROwhichhassomeextrafeaturesifyouchoosetopayforthem.
Setup
Onceyou’vemadeitthroughtheinstaller,runtheMAMPapplication.
WhenMAMPisopen,you’llseea“StartServers”buttonontherightsideofthescreen.Youcanclickitnowandyou’llbeinbusinesswithyourdocumentrootset,whichistheplaceallyoursitefileswillgo,tothedefaultpath.InthecaseofOSX,thedefaultdocumentrootisinthehtdocsdirectorywhereveryouinstalledMAMP(typicallyin/Applications/MAMP).
Ifyouwanttochangeyourdocumentroottoanotherlocation,changetheversionofPHPthattheserveruses,oranumberofothersettings,makesuretostoptheserversandheadtothePreferencespaneofMAMP.
ThedefaultconfigurationofMAMPisfineforgettingstarted,soifyoudon’tneedtocustomizeanythingjuststarttheserversandyoushouldbeabletoaccessatestpageat
http://localhost:8888/MAMP/.Thispageisn’tontheinternetanywhere,it’sonyourlocalmachine!Ifthatlinkworksforyou,you’rereadytostartdeveloping.Justcreateasimplehtmlpageinsideyourdocumentrootfolder,andyoushouldbeabletoaccessitathttp://localhost:8888/your_file.html.
Foramoredetailedinstallationandset-upguide,takealookattheMAMPuserguide.
SummaryMAMPisagreattoolandonethat’seasytosetupandgetstartedwith.Thereisabigwideworldofwebserversolutionsfordevelopinglocally;heck,ifyou’reonaMacyouhaveApache/PHP/MySQLalready!Therejustisn’tagreatinterfaceforputtingthosethingstouse,somanydeveloperspreferMAMP.Intheend,howeveryougoaboutservingyourHTML/CSS/JStoabrowserisfine.What’simportantisthatitworksforyou.
Frameworks
JavaScriptallowsyoutowriteclient-sidescriptsformanipulatingtheDOMandforinteractingwiththeuserandtheirbrowser.JavaScriptiscommonlywrittenprocedurallyusinglibrariessuchasjQuery.AsHTML,browsersandJavaScripthavebecomemorepowerful,sohasthecomplexityofJavaScriptfiles.Butthisgrowthcanquicklyleadtospaghetticodeandawholebunchofmaintenancenightmaresasitisquitecommoninwebdevelopmentforasite’sentireJavaScripttobemaintainedinasinglefile.Overtheyears,developershaveattemptedtotackletheissueofgrowingdemandforJavaScriptbycreating‘frameworks’toapproachimplementationinamoremodularway.Frameworkshelptoorganizecode.
TherearealargenumberofJavaScriptframeworksavailable.Inthischapteryou’llbeintroducedtosomeofthemostpopularones.
Themajorityofframeworksincorporateavariationofthe‘Model-View-Controller’(MVC)architecturalpattern.It’sworthnotingthatyoumaycomeacrossafewinathewildthatintroducetheirowntakeonMVC,ortheymayhaveanentirelydifferentstructure.IfyouhaveabackgroundinsoftwareengineeringyoumayalreadybefamiliarwithMVC,butlet’sgooveritbrieflysowe’reallonthesamepage.
Model-View-WhateverArchitectureMVCisapopulararchitecturalpatternthatisusedinmanyJavaScriptframeworks.Aspreviouslymentioned,therearesomeframeworksouttherethatputtheirownspinontheMVCpattern.TheseframeworksimplementwhatissometimesreferredtoasanMV*orMVW(Model-View-Whatever)architecture.
However,itisimportanttoknowwhatMVCis,andwhyithasbeensuchanimportantpartofthewayapplicationsarebuilt.MVCseparatesthedifferentaspectsofanapplicationintothreecomponents:
Model
Modelsrepresentanapplication’sdata.Youcanthinkofeachmodelasadifferentcategoryofdatathatanapplicationwillstore.Forexample,atypicalwebapplicationmayhaveUserandArticlemodels.Modelsdonotoutputanything,asthisisthejobof‘views’,buttheyarecommonlyresponsiblefortellingviewswhentheirdatahasbeenupdated.
View
Viewshandleoutput.Inmostimplementationstheywilloutputthemarkupthatrepresentstheuser-interface.Viewsrequestinformationfrommodelsinordertogeneratethisoutput,andmodelskeepthemuptodate.
Controller
Controllersareresponsibleforhandlinguserinputsuchasformdatasubmissionorclickevents.Theycansendrequeststomodelsinordertomanipulatetheirdata.
ThefollowingdiagrammayhelpyouvisualizethevariouscomponentsofanMVCfromtheuser’sperspective:
IfyoufancyreadingmoreaboutJavaScriptarchitecturalpatterns,AddyOsmani’sbookLearningJavaScriptDesignPatternsishighlyrecommended.
Single-PageApplicationsWhenlearningaboutJavaScriptframeworks,onetermyoumightseefloatingaroundis‘single-pageapplication’,or‘SPA’.
WhenbuildinganSPA,theUIandapplicationlogicisessentiallyshiftedfromtheservertotheclient.AlltheresourcesforanapplicationsuchasHTML,CSSandJavaScriptaredownloadedinasinglepageload.Astheusernavigatesasite,thebrowserwillnotreloadthepagebutinsteadtheSPAisresponsibleforrenderingthedifferentpartsofapageattherighttime.
CreatinganSPAprovidesamorefluiduserexperiencethanthemoretraditionalapproachofrequestinganewpageeverytimetheusernavigatessomewhere.Bymovingalltheapplicationlogictothefront-end,therequirementsoftheserveraredrasticallyreduced.Insteadoftheserverrenderingeachpageeverytimethereisapagereload,anSPAdynamicallyrequestsonlytherequiredrawdatabehindthescenes,andusesthattorenderthevariousviews.
JavaScriptframeworkscanmakeitfairlyeasytobuildanSPA.Infact,someofthembuildtheirfunctionalityentirelyaroundtheconcept.However,thereareafewdisadvantages
thatyoumaywanttoconsiderbeforetakingtheplunge.
Forexample,SPAsneedJavaScript.IftheuserdisablesJavaScriptintheirbrowser,thesitewon’trender.It’sdifficulttosayhowmanyusersactuallydothisbutaccordingtocertainstudiesit’slessthan1%.Soyouhavetoaskyourselfifitisactuallyworthsupportingthoseusersandifnot,whatexperiencecanyouprovidefortheminstead?
You’llalsoneedtoconsiderrobots.Maybenotthekindofrobotsyou’rethinkingofbuttheonesthatGoogle,FacebookandTwittercansendtofindoutaboutapage.Theserobotsscrapeapageformetatagsandotherinformationaboutcontentsotheycandisplaythemappropriatelyinsearchenginesorsocialnetworks.Morerecently,GooglerobotshavestartedtorenderJavaScript.ThismeansthatwhenaGooglerobothitsanSPA,itshouldcorrectlyrenderthepagefirstusingJavaScript.However,otherrobots(suchasFacebook’s)areyettoimplementthisfunctionality,andthiscancausesharedpagesfromasitetoproduceundesirableresults.You’llneedtocomeupwithawaytorenderdataserver-sidefortheserobots.Servicesexisttosolvethisexactproblem,suchasPrerender.io.
BeforeyoustarttouseJavaScriptframeworkstobuildSPAs,thinkcarefullyabouttherequirementsofyourapplication.Itcanbequiteeasytoover-complicatethings.Forexample,ifyou’rebuildinganapplicationwherethereisaheavyemphasisonuserinteraction,suchasaticketmanagementsystemorato-dolist,thenyou’lldefinitelybenefitfromusingaJavaScriptframeworkandSPA.However,ifyou’recreatingapagewhichonlyrequiresverybasicinteraction,suchasaportfoliopagegivingdetailsaboutyouandyourwork,thenitcouldbeoverkill.Youmayfindthatthetraditionalapproachtowebdevelopment(i.e.renderingontheserver-sideusingalanguagesuchasPHPandthenaddingontheclient-sideinteractionsusingjQuery)wouldmakelifealoteasier.
AngularJS
AngularJSisanopen-sourcewebapplicationframeworkthatismaintainedbyGoogle.ItisagreatfitfordynamicwebapplicationsandSPAs.
OverviewThewaythatAngularJS(morecommonlyreferredtoasAngular)approacheswebapplicationdevelopmentisunique.ItpromotesHTMLsyntaxextensiontoincludeadditionaltagattributestoelements.Theseattributes,knownas‘directives’,allowyoutoattachnewbehaviortoDOMelements.Angularcomespackagedwithseveraldirectivesforcommonactivitiessuchasdatabinding,repeatingorhidingDOMelements,eventhandlingandformmanagement.Youcanalsowriteyourowndirectives,butthatwillbecoveredlater.
InstallationAswithmostJavaScriptframeworks,toinstallAngularallyouneedtodoisincludethelibraryonyourpage.Let’sworkthroughanexampletogethertoillustratethis.Inourexample,we’lllaythefoundationsforanapplicationthatwillallowuserstoviewthetimezonesfordifferentcitiesaroundtheworld.Let’scallitTimezoneFinder.YoucanfindthesourcecodeforTimezoneFinderinthisrepository:https://github.com/fender/TimezoneFinder.
Createanewfolderfortheprojectandcreateanindex.htmlfileatitsroot:1<!doctypehtml>
2<html>
3<head>
4<metacharset="utf-8">
5<title>TimezoneFinder</title>
6</head>
7<body>
8<p>Helloworld!WelcometoTimezoneFinder.</p>
9</body>
10</html>
HeadovertotheofficialAngularJShomepageandclickthedownloadbutton.GooglehostsAngularonitsCDNsoyou’reabletoleveragethatquiteeasilywithasimplecopy-and-pasteofthegivenCDNURL.Alternatively,youcandownloadthefiledirectlyor,ifyouprefertohostitlocally,youcanuseBower(seetheDependencyManagementchapter).
Let’sreturntoourexampleandaddtheCDNreferencetoourindex.html:1<head>
2<metacharset="utf-8">
3<title>TimezoneFinder</title>
4<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.mi\
5n.js"></script>
6</head>
Next,wecancreateaapp.jsfilethatwillberesponsibleforkick-startingtheAngularapplication.Butfirst,let’sjustaddasinglelinetooutputtheincludedversionofAngulartothebrowserconsolesothatwecanconfirmithasbeenloadedsuccessfully:1console.log(angular.version.full);
Weneedtomakesureweincludeapp.jsinourpageafterAngularhasbeenincluded:1<head>
2<metacharset="utf-8">
3<title>TimezoneFinder</title>
4<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.mi\
5n.js"></script>
6<scriptsrc="./app.js"></script>
7</head>
VisitthesiteonalocalwebserverandyoushouldseetheAngularversionnumberoutputtoyourbrowserconsole.
Modules
AnycustomfunctionalitythatyouwishtoimplementwithAngularmustbedefinedinamodule.WhenAngularstartsitlooksforadefaultmoduletoauto-bootstraptheapplication.YoucantellAngularwhichmoduletousebyusingthengAppdirective.
Inourexample,let’saddthedirectivetoour<body>tag:1<bodyng-app="timezoneFinderApp">
AngularreferstodirectivesinlowerCamelCase(e.g.ngApp)butwhenyouwritetheminyourmarkupyouneedtowritetheminlowercaseandseparatewordswithhyphens.Inourexample,we’venamedourmoduletimezoneFinderAppsolet’sgoaheadandcreatethismoduleinourapp.jsscriptfile:1varapp=angular.module('timezoneFinderApp',[]);
That’sallittakestoinitializeanewmodule.However,nothingwillactuallyhappenjustyet.Let’smoveontocontrollerstoreallygetthispartystarted!
ControllersAngularcontrollersallowyoutowritefunctionalitythatinteractswithascopeobject.Itcantakealittletimetogetyourheadaroundscopesandhowtheyworkbutit’simportanttorememberthattheyarethegluebetweencontrollersandviews.Bothcontrollersanddirectiveshaveaccesstothescopeobjectbutnotwitheachotherdirectly.
ThebestwaytounderstandthisistoreturntoourTimezoneFinderexample.Let’swriteourfirstcontrollerdirectlyinapp.js:1varapp=angular.module('timezoneFinderApp',[]);
2
3app.controller('MainController',['$scope',function($scope){
4//Definesourcities.
5$scope.cities=[
6{name:'London',timezone:'GMT'},
7{name:'Tokyo',timezone:'JST'},
8{name:'Melbourne',timezone:'EDT'},
9{name:'LosAngeles',timezone:'PST'},
10{name:'NewYork',timezone:'EST'}
11];
12
13//Thedefaultcity.
14$scope.selectedCity=$scope.cities[0];
15}]);
We’venamedourcontrollerMainControllersince,forthisexample,we’reonlygoingtoneedasinglecontroller.However,forlargerapplicationsyou’lloftenendupwantingtonametheminrelationtothefunctionalitythattheyimplement.
You’llseethatwe’veattachedtwovariablestothe$scopeobject.$scope.citiesisanarrayofcityobjects,eachwithanameandatimezone.$scope.selectedCityisareferencetothefirstiteminthecitiesarray.
Themagicstartstohappenwhenweswitchovertoourmarkup.Let’sopenuptheindex.htmlandaddsomethinglikethisinsidethe<body>:1<p>Helloworld!WelcometoTimezoneFinder.</p>
2<divng-controller="MainController">
3<label>Selectacity</label>
4<selectng-model="selectedCity"ng-options="city.nameforcityincities"></se\
5lect>
6<p>Thetimezonein{{selectedCity.name}}is{{selectedCity.timezone}}.</p>
7</div>
Wehaveusedseveralnewdirectiveshere.Firstly,weattachedMainControllertotheDOMusingngController.We’vethengota<select>elementthatimplementsngModelwhichsetsupdata-bindingbetweentheelement’sselectedoptionand$scope.selectedCity.Whenchoosingadifferentoption,thescopeobjectwillautomaticallyupdateanyotherplacewhereitisbeingused,suchasinthemessagebelowthe<select>element.Lastly,ngOptionsisadirectivespecificto<select>elements.Ifwerunourapplicationweshouldseesomethinglikethis:
Aninterestingpointtomentionhereiswe’veactuallyjustcoveredallpartsofanMVCarchitecturalpatternwithinAngular.Themodelsarethepropertieswesetonthe$scope.TheHTMLwithdata-bindingsthatwewroteareourviews,andthengControllerspecifiesourcontrollerfunctionthatcontainsourbusinesslogic.
RoutingAngularisexceedinglywellsuitedforbuildingSPAs.Angular‘routing’allowsyoutocreatemultipleviewsandhavethemloadedseparatelydependingontheURLpathbeingrequestedbytheclient.
RoutingfunctionalityisnotbakedintotheAngularJScorelibrary.ItisprovidedasaseparatemodulecalledngRoute.Justlikewhenweincludedthecorelibrary,wecanusetheCDNaddresstograbthefile.We’llupdateour<head>tolooksomethinglikethis:1<head>
2<metacharset="utf-8">
3<title>TimezoneFinder</title>
4<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.mi\
5n.js"></script>
6<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-ro\
7ute.min.js"></script>
8<scriptsrc="./app.js"></script>
9</head>
NowwecanaddanewpagetoourapplicationwhichwilldemonstratehowroutingcanbeimplementedinAngular.Let’shavethenewpagedisplayalistofalltheavailablecitiesandtheirrespectivetimezones.Wecancallitthe‘FullList’page.We’llneedtocreateanewtemplate(i.e.anHTMLmarkupfile)forthepage.Createafilecalledlist.htmlintherootdirectoryandplacethismarkupinside:1<h1>FullList</h1>
2<ul>
3<ling-repeat="cityincities">
4 Thetimezonein{{city.name}}is{{city.timezone}}.
5</li>
6</ul>
That’sallweneedtoadd.Don’tworryabout<html>tagsoranythinglikethat.Thisfileisjustastubforothertemplatestouse.Now,dependingontheURLusedtoaccessthepage,wewillaskAngulartoincludethemarkupintoindex.html.Beforesettingupourrouting,let’salsocreateafilecalledwelcome.htmlintherootdirectoryandaddthefollowingfamiliarmarkup:1<h1>Welcome</h1>
2<p>Helloworld!WelcometoTimezoneFinder.</p>
3<label>Selectacity</label>
4<selectng-model="selectedCity"ng-options="city.nameforcityincities"></sele\
5ct>
6<p>Thetimezonein{{selectedCity.name}}is{{selectedCity.timezone}}.</p>
Let’smoveontotheindex.html.We’renowabletoremovethewelcomemarkupfrominsideour<body>:1<!doctypehtml>
2<html>
3<head>
4<metacharset="utf-8">
5<title>TimezoneFinder</title>
6<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.\
7min.js"></script>
8<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-\
9route.min.js"></script>
10<scriptsrc="./app.js"></script>
11</head>
12<bodyng-app="timezoneFinderApp">
13<divng-view></div>
14</body>
15</html>
We’veaddedanewelementwiththengViewdirectiveattached.ThisdirectivetellsAngularwheretoincludethetemplatethatisrequiredforthecurrentroute(i.e.URLpath)beingaccessed.Whentheroutechanges,thedirectivewillautomaticallyupdatetheelementwiththecontentsoftherelevanttemplate.
NowwecanjumpovertoourJavaScriptfileapp.jstoaddthelogicforrouting.Firstofall,weneedtocreateadependencyonthengRoutemodule.Withoutdoingthat,we’reunabletousethefunctionalitythatthemoduleprovides.FormoreinformationonhowthisworksinAngular,checkouttheofficialdocumentationondependencyinjection.Addthedependencytothefirstlineofapp.js:1varapp=angular.module('timezoneFinderApp',['ngRoute']);
Nowlet’simplementourroutinglogic.We’lldothisinsideourmodulesconfigblock.Thismethodallowsustosetupconfigurationsforourmodulebeforeanycontrollersareexecuted:1varapp=angular.module('timezoneFinderApp',['ngRoute']);
2
3app.config(['$routeProvider',function($routeProvider){
4$routeProvider.
5when('/welcome',{
6templateUrl:'welcome.html',
7controller:'MainController'
8}).
9when('/list',{
10templateUrl:'list.html',
11controller:'MainController'
12}).
13otherwise({
14redirectTo:'/welcome'
15});
16}]);
17
18app.controller('MainController',['$scope',function($scope){
19...
Asyoucansee,we’veinjectedthe$routeProviderserviceintoourconfigblock.Then,usingthe$routeProvider.when()function,wewereabletodefineourcustomroutes.We’vesetuptworoutesforeachtemplatethatwehave.WhentheURLpathmatcheseitherofthoseroutedefinitions,itwillgrabthemarkupfromthetemplateUrlandplaceitinsidethengViewdirectivethatweplacedinsideindex.html.
WecanalsotellAngulartoloadaspecificcontrollerforeitherofourroutes.Thesedon’thavetobethesamebutforourapplicationexampleweonlyneedtoexecutetheMainControllercontroller.We’vealsoused$routeProvider.otherwise()toredirecttothewelcomepagewhennootherroutedefinitionismatched.Readthe$routeProviderdocumentationtolearnaboutalltheavailableoptions.
Ifweruntheapplicationagain,wemightnoticethattheURLupdatedtoincludethe/welcomeroutebecauseweaccessedthepagewithoutarouteandourredirectTokickedin.ThefullURLwillactuallylooksomethinglikehttp://localhost/TimezoneFinder/#/welcome.ThehashisaddedbyAngularbydefaultaspartofits$locationservicehashbangmodefunctionality.However,wecanturnontheHTML5modewhichisbasedontheHTML5HistoryAPIandthiswillremovethehash.PrettymuchallmodernbrowserssupportthisfunctionalityandAngularwillfallbacktousingthehashbangmodeforolderbrowsers.SeeCanIUseforafulllistofbrowsersupport.
Let’supdateourconfigblock:1app.config(['$routeProvider','$locationProvider',function($routeProvider,$loc\
2ationProvider){
3$locationProvider.html5Mode(true);
4$routeProvider.
5when('/welcome',{
6...
Now,whenweaccesstheapplicationviatherootURLweshouldberedirectedtothewelcomepagewithoutahashappearingintheURL.Note:Ifyouareaccessingtheapplicationinasub-directory(e.g.http://localhost/TimezoneFinder/)thenyouwillneedtoaddthe<base>tagtoyourindex.htmlinsidethe<head>asfollows:1<head>
2<metacharset="utf-8">
3<title>TimezoneFinder</title>
4<basehref="/TimezoneFinder/"/>
5...
Ok.Sotobeabletonavigatebetweentheroutes,let’saddanavigationmenutoourindex.html.1<bodyng-app="timezoneFinderApp">
2<ul>
3<li><ahref="welcome">Welcome</a></li>
4<li><ahref="list">FullList</a></li>
5</ul>
6<divng-view></div>
7</body>
Runtheapplicationagainandyou’llseethatwecannavigatebetweenthedifferentviews.AlthoughremovingthehashfromtheURLmakesthemlookpretty,youmightnoticethatifyoutrytoaccessadefinedroutedirectlythatyouwillreceivea404NotFounderrormessage.ThisisbecauseAngularisservingallofitsfilesthroughindex.htmlfile.You’llneedtoconfigureyourwebservertohandlerewrites.CheckoutthisveryusefulGithubwikipagethatexplainshowtodothisforvariouspopularwebserversoftware.
CustomDirectivesAngularprovidesawholebunchofusefuldirectivesoutofthebox.Youcangetprettydamnfarwithouteverneedingtowriteyourown.However,itcanbeveryusefultoknowhowtowriteyourowndirectives.Evenifyoudon’tplantouseacustomdirective,writingonemayhelpyouunderstandtheinnergoings-onofAngular.
Let’screateadirectiveforourTimezoneFinderapplication.Ifyoulookinsideourtemplatefiles,wearerenderingthemessageThetimezonein{{selectedCity.name}}is{{selectedCity.timezone}}.bothonthewelcomeandfulllistpages.Ifwewantedtochangethemarkupforthesemessages,wewouldhavetochangecodeintwoseparateplaces.Topreventthis,let’screateadirectivethatwillrenderthemessagewithinanyelementitisattachedto.
Wecanopentheapp.jsfileandaddthedirectiveblockasfollows:1app.directive('cityTimezone',function(){
2return{
3scope:{
4city:'=city'
5},
6template:'Thetimezonein{{city.name}}is{{city.timezone}}.',
7};
8});
9
10app.controller('MainController',['$scope',function($scope){
RememberthattheAngularstandardistonamedirectivesinlowerCamelCase.We’vecalledourdirectivecityTimezone.Itrequiresacitytobepassedasaparameter.ThetemplatemarkupisreturneddirectlybutyoucoulduseaseparatetemplateHTMLfileusingtemplateUrlifyouwantedto.
Returningtoourexample,let’saddthenewdirectivetoourtemplatefilesstartingwithwelcome.html:
1<h1>Welcome</h1>
2<p>Helloworld!WelcometoTimezoneFinder.</p>
3<label>Selectacity</label>
4<selectng-model="selectedCity"ng-options="city.nameforcityincities"></sele\
5ct>
6<pcity-timezonecity="selectedCity"></p>
Andthenoursecondtemplatefile,list.html:1<h1>FullList</h1>
2<divng-controller="MainController">
3<ul>
4<ling-repeat="cityincities"city-timezonecity="city"></li>
5</ul>
6</div>
Visittheapplicationagainandnothingwillhavechangedfromanenduser’sperspective.However,ifwewantedtoimprovethemessageoritsfunctionalityinthefuture,wenowonlyneedtochangecodeatasinglelocation.
Thereareplentyofotherconfigurationoptionsfordirectivessobesuretoreadtheofficialdocumentationtofindoutmore.
AdvancedItwouldbegreattocoverAngularinmoredepthbutthisisn’tanAngularbook.However,beforeusingAngularinyourownprojects,it’srecommendedthatyougainsomeunderstandingofthefollowing:
Services-Angularservicesaregreatforseparatingoutfunctionalitythatistobesharedthroughoutanapplication.Thereareendlessimplementations,suchasstoringlogged-inuserdetails,accessingexternalAPIsandothers.ReadtheServicesdocumentation.Filters-Angularfiltersareusedforformattingoutputbeforeitisdisplayedtousers.Theycanbeusedinvariouspartsofanapplicationandyoucanevenbuildyourown.ReadtheFiltersdocumentation.Testing-Getintothehabitofwritingunittestsasyouaddfunctionality;itcansaveyoualotoftimewhilstdebuggingandtestingyourapplicationasitgrows.You’llneedtolearnaboutsomeadditionaltoolsfortestingsuchasKarma,JasmineorProtractor.Formoreinformation,seetheunittestingandendtoendtestingdocumentationpages.
Ifyou’reseriousaboutlearningAngularJSthenthisawesomelistofeducationresourcesishighlyrecommended.
AnotherrecommendationtocheckoutisAngularJSBatarang,aChromeextensionfordebuggingandprofilingAngularapplications.
Backbone.js
Backbone.jsisanotherpopularJavaScriptframeworkforbuildingwebapplications.ItwascreatedbytheamazingmindofJeremyAshkenaswhoisalsothecreatorofCoffeeScript.Afairnumberoflarge-scaleapplicationshavebeenbuiltusingBackbonesuchasAirbnb,PinterestandSoundcloud.
OverviewBackboneisoftenreferredtoasalibraryratherthanaframeworksinceitplayswellwithotherJavaScriptlibrariesinsteadoftryingtoreinventtheJavaScriptobjectmodel.Backbonecomeswithfourcorecomponents:models,collections,viewsandrouters.ThecombinationofthesecomponentsmakeupthecorearchitectureofBackbonebutyoucanextendanyoftheirfunctionalitytomeetanapplication’srequirements.
Backboneisverylight.Theproductionminifiedversionofthelibrarycomesinatjustover6kb.Incomparison,AngularJSisover40kb.Notthata30kbdifferenceisanywhereneartheendoftheworldoranything,butitdoessaysomethingforthesimplicityofBackbone.However,BackbonerequiresUnderscore.js(5kb)andyoushouldincludejQuery(30kb+)tomakefulluseoftheHistoryAPIwithBackbone.RouterandDOMmanipulationinBackbone.View.
JustaswedidintheAngularJSsectionearlierinthischapter,we’regoingtolearnabitaboutBackbonebyusingittorebuildourapplicationexample,TimezoneFinder.Afterwe’veinstalledBackbonewe’regoingtointroduceeachofthecorecomponentsbyaddinganotherpiecetotheapplication.
InstallationJustaswithanyotherJavaScriptlibrary,itneedstobeincludedonthepage.Hereisasampleindex.htmlthatincludesaCDNversionofBackbone,itsharddependencyUnderscore.js,andsoftdependencyjQuery.Let’smakethisfilenowinourprojectroot:1<!doctypehtml>
2<html>
3<head>
4<metacharset="utf-8">
5<title>TimezoneFinder</title>
6<scriptsrc="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></\
7script>
8<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/unde\
9rscore-min.js"></script>
10<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbo\
11ne-min.js"></script>
12<scriptsrc="./app.js"></script>
13</head>
14<body>
15<p>Helloworld!WelcometoTimezoneFinder.</p>
16</body>
17</html>
We’vealsoincludedapp.jswhichwillstoreourcustomscripts.That’sit!NowwecancontinuetobuildTimezoneFinderagain,butthistimeinBackbone.
ModelsandCollectionsModelsinBackboneletyoudefineyourdatastructureandrelatedlogic.InourTimezoneFinderapplication,we’regoingtodisplaycitiesandtheirrelatedtimezones.SoitmakessensetohaveaCitymodelthatstoresitsnameandtimezone.
Setsofmodelsarecalled‘collections’.Soinourexampleapplication,we’regoingtocreateacollectionthatholdsasetofcities.Let’scallitCitiesCollection.
Let’sopenupapp.jsandaddthefollowingcode.1//Defineourmodelanddefaultvalues.
2varCity=Backbone.Model.extend({
3defaults:{
4id:'',
5timezone:''
6}
7});
8
9//Defineourcollection.
10varCitiesCollection=Backbone.Collection.extend({
11model:City
12});
13
14//Createourcollectionofcities.
15varcitiesCollection=newCitiesCollection([
16{id:'London',timezone:'GMT'},
17{id:'Tokyo',timezone:'JST'},
18{id:'Melbourne',timezone:'EDT'},
19{id:'LosAngeles',timezone:'PST'},
20{id:'NewYork',timezone:'EST'}
21]);
Noticetheextendfunction;you’llseethisbeingusedalotinBackbone’scorefunctionality.Intheabovecodesnippet,we’reusingittoextendthefunctionalityoftheBackbone.ModelandBackbone.Collectioncomponents.YoucanlookatextendastheBackbonewaytodefinepropertiesforthedifferentcomponentsofyourapplication.It’sworthnotingthatitsuseisnotrequired.IfwehadremovedtheextendfunctionfromBackbone.Modelthecodewouldstillworkbuttherewouldn’tbeanydefaultvalueswhenanewCityiscreated.
ViewsBackboneviewsgiveyouaplacetoorganizecoderelatedtoyourinterface.Theyletyouwritethelogicbehindthepresentationofmodeldatabutdon’tcontainHTMLmarkuporCSS.Backbonedoesn’treallyhavecontrollersfromthecommonMVCpatternbutinsteadviewsusuallycontainmuchofthatlogic.Therearenorestrictionsonhowyourenderyourviews.ItisrecommendedtouseaJavaScripttemplatingengine.Underscore.jshasitsowntemplatingenginewhichissimple,lightweightandperfectforourexampleapplication.YoucanalsolookintootheroptionssuchasMustacheorHandlebars.ToquoteEleanorRoosevelt,andnowtheSpidermanmoviescript,“Withgreatfreedomcomesgreat
responsibility.”YoumayfindyourselfwritingafairamountofboilerplatecodeforyourapplicationsinBackbonewhichmightbeseenasagoodthingbecauseitgivesyoufullcontrolandunderstandingofthelogicbehindyourapplication.
Let’sgoaheadandjumpbackovertoTimezoneFindertoimplementourfirstview.Openupindex.htmlandaddtheinitialmarkuptothe<body>tag:1<body>
2<divid="view"></div>
3</body>
The#viewelementwillberesponsibleforrenderingourview.Switchovertoapp.jsandappendthefollowingscripttothefile,andthentogetherwe’llgothroughwhatwe’veactuallydone:1//Thisviewrendersaselectelementcontainingthedifferentcities.Whena
2//cityisselectedamessageoutputstherelativetimezone.
3varSelectCityView=Backbone.View.extend({
4//ThisistheDOMelementtheviewwillberenderedwithin.
5tagName:'div',
6
7//Declaretemplatesrequiredforthisview.
8template:_.template(
9'<p>Helloworld!WelcometoTimezoneFinder.</p>'+
10'<label>Selectacity</label>'+
11'<selectid="cities"></select>'+
12'<pid="message"></p>'
13),
14
15option_template:_.template('<optionvalue="<%=id%>"><%=id%></option>'),
16
17message_template:_.template('<p>Thetimezonein<%=id%>is<%=timezone%>.\
18</p>'),
19
20//Thisisautomaticallycalledwhentheviewiscreated.
21initialize:function(){
22this.render();
23this.renderMessage();
24},
25
26//Rendersourviewandtheselectelement.
27render:function(){
28this.$el.html(this.template());
29_.each(this.collection.models,function(item){
30varoption=this.option_template(item.toJSON());
31this.$el.find('#cities').append(option);
32},this);
33returnthis;
34},
35
36//Renderstheselectedcitymessage.
37renderMessage:function(){
38varmessage=this.message_template(this.model.toJSON());
39this.$el.find('#message').html(message);
40returnthis;
41},
42
43//Listenstothe'change'eventonourselectelement.
44events:{
45"change#cities":"citySelected"
46},
47
48//Whenacityisselectedweupdatethemessage.
49citySelected:function(){
50varid=this.$el.find('#cities').val();
51this.model=this.collection.get(id);
52this.renderMessage();
53}
54});
55
56//WaitfortheDOMtobereadybeforecreatingourview.
57$(document).ready(function(){
58varselectCityView=newSelectCityView({
59collection:citiesCollection,
60model:citiesCollection.get('London')
61});
62$('#view').html(selectCityView.el);
63});
WestartedbydefiningSelectCityViewwhichcontainsourview.Aswedidwithmodelandcollection,weextendedaviewtoincludeallofourcustomlogic.Backboneviewsarequitesimpleandcomewithonlyahandfulofproperties.
AviewmustbeassociatedwithaDOMelement.Theelpropertyofaviewcontainsareferencetothiselement.Therearetwodifferentwaystoinitializeel.YoucancreateanewelementbyusingacombinationoftagName,classNameandidproperties.Alternatively,youcanreferenceanelementthatalreadyexistsinthepage.Inoursnippetabove,wespecifiedatagNamesothatanewdivelementwascreatedfortheview.
UsingtheUnderscore.jstemplatefunction,wehavesetupafewtemplatestobeusedwithinourviews.Templatescancontainplaceholdersandwecanpassinahashcontainingthevaluestopopulatethem.
Next,we’vegotinitialize.Thisisanoptionalpropertythatactsasaconstructorforourview.Itisrunautomaticallywhentheviewiscreatedsoyou’lloftenseeitbeingusedtocallrenderfunctions.
Thenexttwoproperties,renderandrenderMessagearefunctionsthatoutputourHTMLmarkup.We’veusedthemtodynamicallycreatethevariousselect<option>elementsandtheselectedcitymessage.It’scommonpracticetoplacereturnthisattheendofrenderfunctionssothatyoucanchainfunctionalitytogether,e.g.view.render().renderMessage().
Theeventspropertyallowsustobindtovariousevents.Inourexample,we’relisteningforthechangeeventonourselectelement.Forthefullcatalogofavailableevents,seetheofficialdocumentation.WhenachangeisdetectedwecallourcustomcitySelectedfunctionwhereweusejQuerytoreturntheselecteditemsvalue,updatethelocalmodelvariabletocontaintheselectedcityandthenre-renderourmessagebycallingrenderMessage().
Lastly,wewrappedtheactualcreationofourviewina$(document).ready.Wedidthisbecauseour<script>tagsareinthepage<head>andwouldcauseourJavaScripttocompilebeforetheDOMisready.Alternatively,youcanplaceyour<script>tagsjust
before</body>tohavethembecalledlaterinthepagelifecycle.Asmentionedearlier,theelpropertycontainsareferencetotheDOMelementforourview.Sowe’reabletousejQuerytopopulate#viewwithourviewsrenderedHTML.
RoutersInBackbone,youareabletoutilizeBackbone.RoutertoconnectURLstoactionsandeventsinapplications.MostbrowsersnowsupporttheHistoryAPIbutforthosethatdon’t,theroutergracefullyfallsbacktousinghashfragmentsinstead.
Formostapplications,you’llusuallyonlyneedtohaveonerouter.Inourexample,let’sgoaheadanddefineourrouterinapp.js:1//DefinesourRouterwhichisresponsibleforhandlingtheswitchingbetween
2//ourvariouscontentviews.
3varAppRouter=Backbone.Router.extend({
4routes:{
5'':'showWelcome',
6list:'showList'
7},
8
9showWelcome:function(){
10varselectCityView=newSelectCityView({
11collection:citiesCollection,
12model:citiesCollection.get('London')
13});
14this.showView(selectCityView);
15},
16
17showList:function(){
18varcityListView=newCityListView({
19collection:citiesCollection
20});
21this.showView(cityListView);
22},
23
24showView:function(view){
25if(this.currentView){
26this.currentView.remove();
27this.currentView.unbind();
28}
29
30this.currentView=view;
31$('#view').html(this.currentView.el);
32}
33});
Theroutespropertycreateskey-valuepairingsforthevariousapplicationroutesintheformatpath:'callback'.Ourdefaulthomeroute(i.e.definedas'')willcallshowWelcomewhichinturnwillrenderSelectCityView.We’vealsoaddedalistroutethatwillrenderanewviewcalledCityListViewwhichwe’llcreateinamoment.Tomakethingsalittlemoresimple,we’vecreatedahelperfunctioncalledshowViewthatmanageswhichviewiscurrentlybeingdisplayed.Wealsohavetodestroyviewswhenchangingbetweenthemtopreventmemoryleaksandbindingeventsmultipletimes.
Let’screatethatmissingCityListViewinourapp.js:
1//Thisviewrendersalistofallcitiesandtheirtimezones.
2varCityListView=Backbone.View.extend({
3tagName:'div',
4
5template:_.template('<h1>FullList</h1>'+
6'<divid="list"></div>'
7),
8
9message_template:_.template('<p>Thetimezonein<%=id%>is<%=timezone%>.\
10</p>'),
11
12initialize:function(){
13this.render();
14},
15
16render:function(){
17this.$el.html(this.template());
18_.each(this.collection.models,function(item){
19varmessage=this.message_template(item.toJSON());
20this.$el.find('#list').append(message);
21},this);
22returnthis;
23},
24});
IncomparisonwithSelectCityView,thisviewshouldbefairlyself-explanatory.Itrendersalistofmessagesforeachofthecitiesinourcollection.
Nowthatwehavearouterandbothviewsincode,we’regoingtoneedtohaveawaytoswitchbetweentheviews.Let’saddasmallmenutoourindex.html:1<body>
2<ulid="menu">
3<li><ahref="/">Welcome</a></li>
4<li><ahref="/list">FullList</a></li>
5</ul>
6<divid="view"></div>
7</body>
Thelastpieceofthepuzzleistoupdateour$(document).readyfunction:1//WaitfortheDOMtobereadybeforecreatingourview.
2$(document).ready(function(){
3Backbone.history.start({
4pushState:true
5});
6
7//CreateourRouterandautomaticallyshowthewelcomeview.
8varappRouter=newAppRouter;
9appRouter.showWelcome();
10
11//Capturemenuitemclickstotriggerroutechanges.
12$('#menua').click(function(e){
13e.preventDefault();
14appRouter.navigate($(this).attr('href'),{trigger:true});
15});
16});
WecallBackbone.history.starttoroutetheinitialURLandstartlisteningforroutechanges.ThisisrequiredforustosetuproutingwithpushStatefunctionality.Next,we
createtherouterandcallshowWelcomesothatviewisdisplayedbydefault.Lastly,we’relisteningtoclicksinourmenuandcallingnavigatetochangetheroute.
That’sit!We’vesuccessfullycreatedourapplicationinBackbone.TakeitforaspinonyourlocalwebserverandyoushouldnoticeverysimilarresultstotheapplicationthatwecreatedintheAngularJSsectionearlierinthischapter.
We’veonlytouchedthesurfaceofwhatispossiblewithBackbonebuthopefullythetakeawayfromyourbriefencounterispositive.Althoughthereisalearningcurve,it’sanextremelypowerfulJavaScriptframeworkthatgivesyouplentyoffreedomtoscale.Ifyou’dliketolearnmoreaboutBackbone,DevelopingBackbone.jsApplicationsbyAddyOsmaniisstronglyrecommended.Also,youmightbeinterestedintheBackboneDebuggerChromeextensiononceyougetstartedbuildingBackboneapps.
Thefinalpopularframeworktointroducetoyouinthischapteris‘Ember’.
Ember
Ember.jsisanotheropen-sourceJavaScriptframeworkwhichwasoriginallycreatedbyYehudaKatzandTomDalein2011.Althoughit’sthelastframeworkcoveredinthischapter,it’sbynomeanstheleast.Withastrongcommunityandabrilliantarchitecturalpattern,Emberisthechoiceframeworkformanydevelopers.
OverviewThecoreoftheEmberarchitecturelieswithintheURL.QuotingfromtheEmbercoreconcepts:
“It’simportanttorememberwhatmakesthewebspecial.ManypeoplethinkthatsomethingisawebapplicationbecauseitusestechnologieslikeHTML,CSSandJavaScript.Inreality,thesearejustimplementationdetails.Instead,thewebderivesitspowerfromtheabilitytobookmarkandshareURLs.URLsarethekeyfeaturethatgivewebapplicationssuperiorshareabilityandcollaboration.Today,mostJavaScriptframeworkstreattheURLasanafterthought,insteadoftheprimary
reasonfortheweb’ssuccess.Ember.js,therefore,marriesthetoolsandconceptsofnativeGUIframeworkswithsupportforthefeaturethatmakesthewebsopowerful:theURL.”
Becauseofitsstronglyopinionatedarchitecture,manypeoplefindEmbereasiertolearn.Itmayalsobemorestraightforwardbecauseoftheexcellentofficialdocumentation.
ItisworthnotingthatEmberhasadependencyonHandlebars.Soifyouwerehopingtouseanothertemplatinglanguage,youmayneedtolookelsewhere.You’llalsoneedjQuery,butwhat’snewright?
JustaswedidwithAngularandBackbone,let’sgoaheadandrebuildourTimezoneFinderapplication,butthistimeinEmber!
InstallationWe’llcreateindex.htmlinourapplication’srootdirectoryandincludetheEmberlibraryanditsdependencies:jQueryandHandlebars:1<!doctypehtml>
2<html>
3<head>
4<metacharset="utf-8">
5<title>TimezoneFinder</title>
6<scriptsrc="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></\
7script>
8<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/hand\
9lebars.min.js"></script>
10<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/ember.js/1.9.0/ember.min\
11.js"></script>
12<scriptsrc="./app.js"></script>
13</head>
14<body>
15<p>Helloworld!WelcometoTimezoneFinder.</p>
16</body>
17</html>
ForfutureprojectsyoumaybeinterestedincheckingouttheStarterKitofferedontheEmberhomepagewhichprovidesyouwithabarebonesstartingpointforyourapplication.However,forTimezoneFinderwe’lljustcreateeverythingourselves.
We’llcreateanapp.jsfileandaddthefollowingsinglelinetogetstarted:1App=Ember.Application.create();
EveryEmberapplicationhasonlyoneEmber.Applicationobject.SowhenusingEmber,beforeyoudoanythingelseyouhavetocallcreate.
RouterAsexplainedearlier,theURLisacoreconceptofEmber’sarchitecture.Bydefault,Embercreatesanindexroutewhichmapsto/(i.e.theroot).Soforourapplicationwe’llonlyneedtocreateoneextrarouteforourFullListpagewhichlistsallcitiesandtheirtimezones.Let’sappendthefollowingtoourapp.js:
1App.Router.map(function(){
2this.route("list");
3});
UsingApp.Routerweareabletomapasmanyroutesaswewant.We’venamedourroutelistwhichmeansthat,bydefault,itspathwillbe/list.Emberalsosetsupseveralothercomponentsautomaticallyforeachroutecreatedandthesewillbeintroducedaswedivedeeperintotheapplication.
TemplatesTemplatesareresponsibleforpresentingthemarkupfortheuserinterface.EmbertemplatesareexclusivelywritteninHandlebarssoyouwillbenefitfromspendingsometimegettingfamiliarwiththeirsemanticsbeforebuildingyourownreal-worldapplication.
Returningtoourexample,we’llstartbyaddingthenavigationmenutothetopoftheapplication.We’regoingtoaddourtemplatesdirectlytoourindex.html:1<body>
2<scripttype="text/x-handlebars">
3<ulid="menu">
4<li>{{#link-to'index'}}Welcome{{/link-to}}</li>
5<li>{{#link-to'list'}}FullList{{/link-to}}</li>
6</ul>
7</script>
8</body>
UsingtheEmberHandlebarslink-tohelperit’seasytocreatelinkstothevariousrouteswithinanapplication.Inourexamplewe’veaddedalinktothefrontpagerouteindexandourcustomlistroute.Nowlet’ssetupthetemplatesforeachofthoseroutes.
WhenrequestingtheURLforaroutemappedinApp.Router,EmberwillautomaticallylookforaHandlebarstemplatetodisplayforthatroute.Let’saddatemplateforbothindexandlistroutes,again,inourindex.html:1<scripttype="text/x-handlebars"id="index">
2<h1>Welcome</h1>
3<p>Helloworld!WelcometoTimezoneFinder.</p>
4</script>
5
6<scripttype="text/x-handlebars"id="list">
7<h1>FullList</h1>
8</script>
We’vesetthe<script>element’sidattributetomatchtheroutename.Butbeforethiswillwork,wehavetocreatesomethingcalledanoutlet.Theoutlethelpertellstheapplicationwheretorenderthetemplatethatwaspickedforaspecificroute.Let’saddtheoutlethelpertoourindex.htmljustbelowthenavigationmenu:1<scripttype="text/x-handlebars">
2<ulid="menu">
3<li>{{#link-to'index'}}Welcome{{/link-to}}</li>
4<li>{{#link-to'list'}}FullList{{/link-to}}</li>
5</ul>
6
7{{outlet}}
8</script>
9...
Now,whenaccessingtheapplicationyou’llbeabletoswitchbetweenthetwotemplatesusingthelinksinthenavigationmenu.Thatwasprettyquickandeasyright?Let’sstartaddingsomelogictoourapplication.
ModelsEachtemplateisbackedbyamodel.Modelsareobjectsthatcontaindatatobedisplayedtotheuserviathetemplate.ThemostcommonuseofamodelistolinkituptoanAPIsothatyou’regettingdatadirectlyfromaservice,butforthepurposeofTimezoneFinderwe’rejustgoingtodefineourmodeldatalocally.Let’sappendacitiesvariableinourapp.jsfilesothatitcanbeusedthroughoutourapplication:1varcities=[
2{name:'London',timezone:'GMT'},
3{name:'Tokyo',timezone:'JST'},
4{name:'Melbourne',timezone:'EDT'},
5{name:'LosAngeles',timezone:'PST'},
6{name:'NewYork',timezone:'EST'}
7];
Next,we’regoingtotellEmberthatwewanttousecitiesasthemodelforbothourindexandlistroutetemplates.ThewaytodothisistocreateaRouteobject.Thatcanbealittleconfusing,butjustrememberthataRouteobjectisnotthesameasaroutedefinedinyourRouterobject.Again,we’llappendthefollowingtoourapp.js:1App.IndexRoute=Ember.Route.extend({
2model:function(){
3returncities;
4}
5});
6
7App.ListRoute=Ember.Route.extend({
8model:function(){
9returncities;
10}
11});
NowthatEmberknowswhichmodeltoprovideourroutetemplateswith,wecanswitchbackovertoindex.htmlandupdateourHandlebarstemplates:1<scripttype="text/x-handlebars"id="index">
2<h1>Welcome</h1>
3<p>Helloworld!WelcometoTimezoneFinder.</p>
4<label>Selectacity</label>
5{{view"select"content=modeloptionValuePath="content.name"
optionLabelPath="\
6content.name"selection=selectedCity}}
7<p>Thetimezonein{{selectedCity.name}}is{{selectedCity.timezone}}.</p>
8</script>
9
10<scripttype="text/x-handlebars"id="list">
11<h1>FullList</h1>
12<ul>
13{{#each}}
14<li>Thetimezonein{{name}}is{{timezone}}.</li>
15{{/each}}
16</ul>
17</script>
Forourindexroute,we’reusingtheEmber.Selectviewclasswhichrendersa<select>elementforus.We’veexplicitlysettheoptionstobepopulatedfrommodelwhichisdefinedinApp.IndexRoute.Aseachiteminourcitiesmodelisanobject,we’vehadtoexplicitlysettheoptionvalueandoptionlabel.TheselectedvaluewillbestoredinselectedCitysothatweuseittopopulateamessagebelowour<select>element.
Forthelistroute,itisveryeasytoiterateoverthemodelobjectandoutputeachcity’spropertiesusingtheHandlebarseachhelper.
ControllerSomethingthatcantakealittlewhiletounderstandisthatalthoughtemplatesgettheirvaluesfrommodels,acontrollerliesbetweenthem.Thecontrollerisresponsiblefor‘decorating’amodelwithdisplaylogicbeforeitsvaluesarepassedtothetemplate.Bydefault,thecontrollerbetweenamodelandatemplatesimplypassesthroughthedata.Inourexample,thisisthecasewithboththeindexandlistroutes.Wedidn’tcreateanycontrollersbecausewedidn’tneedtodecorateoralterthedata.Theimportantpointtorememberhereisthattemplatesreceivealloftheirdatafromcontrollersandnotmodels.
Althoughwedon’tneedtoimplementcontrollersinTimezoneFinder,thissimplediagramtakenfromtheofficialdocumentationsumsuptheconceptquitenicely:
Advanced
ThereisplentymoresophisticationtoEmberthanwastouchedonhere.Aspreviouslymentioned,goandcheckouttheirexcellentdocumentationifyou’reinterestedinlearningmore.JustaswithAngularandBackbone,thereisapopularChromeextensionthatyoucanusetodebugyourEmberapplication.CheckoutEmberInspectorformoredetails.
SummaryHopefullythischapterwasasenjoyabletoreadasitwastowrite!Allthreeofthepopularframeworksintroducedinthischapteraregreatfuntoworkwith.Eachofthemhelpyoutothinkaboutdevelopingyourapplicationinasmartwaythroughmaintainableandscaleablecode.We’veonlylookedatthreeframeworksbutthereareplentymoreoutthereinthewildthatyoumaycomeacrossinyourtravels.Again,youcanfindthesourcecodeforthevariousversionsofTimezoneFinderavailablehere:https://github.com/fender/TimezoneFinder
Sothenextquestionyou’reprobablygoingtoaskis,‘whichisthebestframeworkformyproject?’That’ssomethingonlyyouareabletoanswerreally.Eachprojectisdifferentandwhenpickingaframeworkyouneedtoconsideryourspecificrequirements.Hereareafewrecommendedthingstothinkabout:
Whichframeworksmeetsyourrequirements?Forexample,ifyouspecificallydonotwanttouseHandlebars,thenyoucanruleoutEmber.Or,ifyoudon’twanttousejQuery,youmaywanttoconsiderAngular.Aswithanyapplicationyoubuild,you’regoingtoneedhelp.Sodosomeresearchtoseehowactiveandmaturetheframeworkis.Arethereregularupdates?Howbigisthecommunity?Aretheregoodresourcesforfindinganswerstocommonproblems?Tryit!Often,thebestwaytofindoutifsomethingisrightforyourprojectistotryitfirst.Youmightfindoneframeworkcomplimentsthewayyouthinkanddevelopmorethananother.CheckoutTodoMVC.Thisisaveryusefultoolthatoffersthesameto-doapplicationinalargenumberofdifferentframeworksforyoutocompare.ItwascreatedbyAddyOsmaniwhoalsowroteabrilliantarticleonselectingaframework:JourneyThroughTheJavaScriptMVCJungle.
Styling
Stylingyourmarkuponthefront-endisprettystraightforwardonthesurface.YouhaveaselectorinyourDOMandyouwantittolookacertainwayandusingthedescriptivelanguageofCSS,youtellitso.Thechallengemanynewfront-enddevelopersfaceisn’tgenerallyduetothecomplexityofCSSitself,it’sthenatureofCSSthatcanmakeitsochallenging.
TheCinCSSstandsfor‘cascading’,meaningthatstylesappliedtooneelementareinheritedbyotherelementsfurtherdowntheDOMtree.Ifyouapplyafont-sizetoyourdocument’s<body>andthenlookatthefont-sizeofsome<li>furtherdowntheDOM,you’llnoticethey’rethesame.Thefont-sizewasinherited.ThiscascadingnatureisboththebestandmostfrustratingfeatureofCSS.
Manytimes,you’llwanttostylesomethinglikeanitemlist,<ul>,thesameway,nomatterwhereitappearsintheDOM.Easy!Butwhenthedesignyou’retryingtoimplementhasdifferentvariationsofanelementbasedonwhereitappears,youmaystartrunningintospecificityproblems.
SpecificityinCSSisactuallyanintendedfeatureofthelanguage.Forexample,ifwewantedourallofouritemlistelementsinafootertohaveafewpixelsofmarginontheleftside,wesimplyneedtobemorespecificaboutourselector.Whatstartedoutas:1ul{margin-left:10px;}
Wouldbecome:1.footerul{margin-left:10px;}
Now,ifwewantallofouritemlistsinadifferentparentelementtohavenomarginatallthenwecandothesamething:1.blockul{margin-left:0;}
KeepingthosetwoCSSselectorsinmind,whathappenswhenwehavethefollowingmarkup?1<body>
2<divclass="block">
3<h1>HelloWorld!</h1>
4</div>
5<footerclass="footer">
6<divclass="block">
7<ul>
8<li>Listitem</li>
9<li>Listitem</li>
10</ul>
11</div>
12</footer>
13</body>
Intheexampleabove,the<ul>isstyledentirelybythecascade.Ifwedeclared.footerullast,the<ul>hasmargin,ifwedeclared.blockullast,the<ul>hasnomargin.Thereasonforthisisthatbothselectorshavetheexactsamespecificity,andCSSfallsbacktothecascadeapplyingthelastthingdeclaredthatmatchesourselectors.Tofurthercomplicatetheissue,differenttypesofselectorshavedifferentlevelsofspecificity.Forinstance,thethreemostcommonselectortypesfollowthisorderofspecificity:
1. element(leastspecific)2. class3. id(mostspecific)
Inourexampleabove,divulwouldalwaysbeoverriddenby.footerulbecausetheclassismorespecificthantheelement,eventhoughdivulhasamatch.
Soifwe’reawareofhowallthisworks,whyisCSSsuchatroublespotinfront-enddevelopment?Usuallytheanswertothatcomesfromacoupledifferentsources.
Firstly,thelargerapageisthemoreopportunityforselectorconflictsthereare.It’stoughtoknowjustwhereeverythingonapagemightappear,sowhenwe’refacedwiththeneedtohaveaninstanceofaselectorstyledjustso,thefirstthingmanyfront-enddevelopersdoisgetmorespecificandaddanewstyledeclaration.Ifthatgetsoverriddenbysomethingevenmorespecificorlowerinthecascade,theeasiestoptionistothrow!importantonthestylethatseemstoodifficulttooverride.Thatcausesfrustrationdowntheroadwhenachangecomesinthatneedstoaffectsomething,includingthethingthatwasoverwritten.Largely,thisproblemcanbesolvedwithproperplanning(ifyouhavethatluxury)orbychoosinganauthoringmethodologytominimizefuturefrustration(moreonthatlater).
Secondly,themorepeoplewhoareinyourcodebasewritingstyles,themoreopportunitiesforconflictandduplicationthereare.Morepeople,moreproblems.Johnnymightbeabletomakethestylingadjustmentsheneedsonewaywithouteverknowingthathe’stotallymangledthestylesforsomethingCarlwroteearlier.Thisproblemboilsdowntocommunication.Likethefirstissue,thiscanbesomewhatsolvedbyplanningorchoosingamethodologylikeBEM,butdocumentation,andevenwritingtestsforyourCSSwhichrunautomatically,canbeequallyasuseful.
ResponsiveWebDesignInthelastcoupleofyears,therehasbeenanexplosioninthepopularityofthisterm.Sowhatdoesitmean?Ifyou’renewtotheworldoffront-enddevelopment,youmaynothaveafullygraspedit.Inshort,ResponsiveWebDesign(RWD)istheconceptthatpagedesignsshouldbeflexibleandshouldaccommodateanyscreensize.Whetherit’syourmobilephoneoryourTV,thepageshouldadaptandthecontentshouldbereadilyviewableandaccessible.
Thetaskofactuallyfiguringouthowalayoutshouldadjustandchangetoaccommodatevariousscreensisoftenadesigner’sjob,butthetaskofmakingthoseflexiblelayoutsarealityfallstoafront-enddeveloper.
Responsivewebdesignisahumongoustopicandtherearelotsoftipsandtricksandmethodsformakingitallhappen.Solet’sgooversomeofthebasicthingsyou’llprobablywanttounderstandtogetstarted.
MediaQueriesAmediaqueryisastatementthatlimitsthescopeofyourstylestoaparticularmediatype(likescreenorprint)and/oramediafeature(likemin-widthorheight).Thisisreallyimportanttounderstandwhenyou’regettingintotheworldofRWDbecausetheygiveyouthepowertochangeyourstylesbasedoncertainconditions.
Amediaquerycanbeappliedasanattributeonthe<link>elementthatimportsyourstylesheet:1<linkrel="stylesheet"href="/path/to/your/style.css"media="(min-width:400px)">
Usingthismethod,yourstyle.cssstylesheetwillonlybeusedwhenthewidthofthepageis400pxorgreater.
Youcanalsousemediaqueriesdirectlyinyourstylesheetsaswrappersaroundablockofselectors:1.foo{
2color:blue;
3}
4
5@media(min-width:400px){
6.foo{
7color:green;
8}
9}
Intheabovestatement,.foowillbecoloredblueuntilthepagewidthis400pxorgreater.Oncethepagewidthhits400px,thecolorof.foowillchangetogreen.
Mediaqueriescanbechainedtogetherusingandtoincreasespecificity.Thisnextmediaquerymakessurethatstyle.cssisusedonlywhenthepageisbeingviewedonascreenandthewidthisbetween400pxand601px:1<linkrel="stylesheet"href="/path/to/your/style.css"media="screenand(min-wid\
2th:400px)and(max-width:600px)">
Thatsamemediaquerywritteninthestylesheetwouldlooklikethis:1@mediascreenand(min-width:400px)and(max-width:600px){
2/*yourstyles*/
3}
Youcanalsoseparatemediaqueriesbyusingacommatorepresent‘or’.Thismediaquerywillapplystyle.csswhentheorientationislandscapeorthepageisatleast400pxwide:1<linkrel="stylesheet"href="/path/to/your/style.css"media="(orientation:lands\
2cape),(min-width:400px)">
YoucanfindadetailedaccountofmediaqueryfeaturesandtypesattheMozillaDeveloperNetwork.
RelativeUnits
Agooddealofthevisualflexibilityofelementsonapagecomesfromdefiningtheirdimensionsnotwithpixels,butwithrelativeunits.Haven’theardofrelativeunits?Fearnot,here’saquickoverviewoftheunitsyou’llseeusedmostoften.
emandremUnits
Theemunitrepresentsthevalueofthefontsizeofthecurrentelement’sparent.Takealookatthefollowingexample:1.form{
2font-size:16px;
3}
4
5.forminput{
6width:2em;
7}
Whatwouldthewidthof.forminputbe?That’sright,32px,or2xthefontsizeof.form.emunitsarescalable,sowhenauserzoomsinwiththeirbrowseranythingstyledwithanemunitwillscaleproportionally.
Theremunitisabitnewer,andrepresentstherootfont-sizeofthedocument,whichisthefontsizeseton<html>.Whywouldyouwantsuchaunit?Well,aproblemthatoftencropsupwhenusingemunitsisasortofnestingmess.Hereisanotherexampletodemonstrate:1html{
2font-size:16px;
3}
4
5.forminput{
6width:2em;
7}
8
9.forminputspan{
10width:2em;
11}
Youmaythinkthatthewidthof.forminputspanwouldalsobe32pxbutactuallyitis64px.However,ifweswitchedoutemforremunitsthenbothwidthswouldindeedbe32px.Usinganremunitcankeeptextscalableandprovideasanebasefornestedems.
%Units
Usinga%valueworksinthewayyou’dexpect.Ifyouhaveanelement’swidthsetto100%anditsparentwidthis200px,yourelement’swidthisalsogoingtobe200px.ThisisaprettycommonwaytodefinedimensionsintheRWDworld.Ifyoudefinethemajorityofyourdimensionswithpercentagesratherthanabsolutepixelvalues,you’llbewellonyourwaytocreatingflexiblepages.
vwandvhUnits
Thesetwounitsarealsorelativelynewinthefront-endspaceandareveryhandyindeed.Theyfunctionsomewhatsimilarlytothe%unit,butinsteadofbeingbasedontheirparentstheyarebasedonthe‘viewport’(thedimensionsofthebrowserwindow).1vwisequalto1/100thofthecurrentviewportwidthand1vhisequalto1/100thofthecurrentviewport
height.Ifyouwantedtohaveanelementbeexactlytheheightoftheviewport,youcouldsetitsheightto100vhwhichequatesto100%oftheviewportheight.
Thereareahandfulofotherreallyinterestingrelativeunitsoutthere,butyou’lltypicallydealwiththeonespresentedaboveinourdaytodaywork.Ifyou’reinterestedinmoredetailsaboutalltherelativeunitsandtheirsupportinvariousbrowsers,checkouttheMozillaDeveloperNetwork.
SummaryBuildingfront-endapplicationswithRWDprincipalscangetprettycomplicated.Wrappingyourheadaroundemand%unitscanbetricky,andgettingeverythingonthepagetolookrightallthetimecanbeapainstakingprocess.Tobehonest,itwasabitofastruggletofigureoutwhat,ifanything,shouldbesaidaboutresponsivewebdesigninthisbook.Thetopicismassive,heavilydebated,andconstantlyevolving.Butsomethingneededtobesaidtobringnewerfront-enddevelopersuptospeedonwhatitisifnotexactlyhowtoimplementit.ThattopicisworthyofanentirebookitselfandtherejustsohappenstobeafantasticonewrittenbyEthanMarcottesimplytitledResponsiveWebDesign.YoucanfindthisseminalworkatABookApartinbothpaperbackandebookforms.
AuthoringMethodologiesA‘methodology’isagroupofproceduresforaccomplishingsomethingand,particularlyifyou’reworkingwithateam,somethingthatisincrediblyhelpfulwhenauthoringCSSforyourproject.Choosingorevencraftingyourownmethodologyisaperfectwaytokeeppeoplefromsteppingoneachother’stoesinthecodebase(mostly)andittakessomeofthe“HowthehelldoIdothis?”thinkingoutoftheequationaswell.
Sowhatexactlydowemeanwhenwetalkabout‘authoringmethodologies’forCSS?Generally,wemeanadocumentedstrategyforhowourcodewillbeorganizedandhowwe’llapproachstylingsomething.WillweuseIDselectorsforeverymajorsection?Shouldwetotallyignorethecascadetoavoidspecificityproblems?Howshouldwenameourclasses?Thesearethekindsofthingschoosingamethodologyisallabout.
Unfortunately,nobodycanjusttellyouwhichmethodologyisthebest.There’snorightanswer.Everysingleprojectwilllikelyhavedifferentneedsandthoseneedshavetobeanalyzedbeforeyoucanbegintothinkofhowtoapproachmeetingthem.Moreover,thereisnoonesinglemethodologythatcaneverbeperfectlyadheredto.Therewillalwaysbeexceptionstowhateveryoudecidetodobut,ifyoudocumentthoseexceptionsasyoucomeacrossthem,theirimpactonthewholeoftheprojectisusuallyprettyminimal.
Belowaretwocommonmethodologies,groupedbyhowtheauthorsperceivethespiritofthosemethodologies.
ComponentsandPatternsAcomponentbasedmethodologycentersaroundtheideathatcertainindividualchunksofstylescanbegrouptogethertoforma‘component’.ThesecomponentsshouldtypicallybepresentedtheexactsamewaynomatterwheretheyappearintheDOM.Agoodexample
ofthismightbeasmallupcomingeventscalendarwhichshouldalwayslookthesame,whetherinthefooterorasidebar.Let’swriteitup:
Markup1<divclass="calendar-component">
2<ul>
3<li>Jan<span>01</span></li>
4<li>Feb<span>29</span></li>
5</ul>
6<ul>
7<li>NewYear's!</li>
8<li>LeapDay!</li>
9</ul>
10</div>
CSS1.calendar-component{
2width:400px;
3}
4.calendar-componentul{
5width:60%;
6display:inline-block;
7padding:0;
8}
9.calendar-componentul:first-child{
10width:30%;
11}
12.calendar-componentli{
13list-style:none;
14}
15.calendar-componentspan{
16font-weight:bold;
17}
Thatisn’texactlythemostbeautifulcomponentontheweb,butit’scompletelyselfcontained.Nothingwehavewrittenabovewillaffectanythingthatisn’tdirectlyrelatedtoourcalendarcomponent.Ourcomponentmaystillbeaffectedbymoregenericstyles(likeour<ul>declarationsearlierinthischapter),butifweseparatethevariouspartsofourdesignsintotheseuniquecomponentsrightfromthebeginningofaproject,thenwecanpreventthatfromhappening.
Takingthisastepfurther,let’snowimaginewehaveanidenticallystyledside-by-sidelistcomponent.Itcontainssimilarlyformattedcontent,butinsteadofacalendarit’saT-shirtsizingchart.Thesimplestoptionwouldbetoduplicateourmarkupforthatcomponent,classesandall,withthedifferentcontent.Blamo!We’vejustcreatedare-usableCSSpattern.
Now,calendar-componentisn’texactlythemostgenericnametobeapplyingtoaT-shirtsizingchart,sofordevelopersanityit’srecommendedthatyoutrytogeneralizecomponentnamessotheycanbere-usedandunderstoodmoreeasily.ReducingduplicatedCSSisprettyimportantduetothenatureofthecascadeandthetendencyforstylestoshowupinoddplaces.IfyourprojecthappenstosupportInternetExplorer6-9thenduplicationcanbeevenmoreimportanttoavoidbecauseIEhasa4095totalselectorlimit-andthenitjuststopsreadingthem.
BEM
BEMisapopularcomponentwritingmethodologydevelopedbyYandexthatis,inmanyways,similartotheobjectorientedprogrammingmethodologyusedinotherlanguages.BEMstandsfor‘block’,‘element’and‘modifier’;thesearethecategoriesyourCSSselectorswillfallinto.Blocksarethelargestchunkofstylesthatmakeupaparticularvisualelementonapage.Elements,inregardstoBEM,aresub-sectionsofaparticularblock,andmodifiers,asyoumightguess,describeanyvariationsofaBEMblockorelement.
TheBEMmethodologyitselfisn’tlimitedtoCSSsinceit’salsoapplicableforXML,JSON,andprobablyotherthings,butwe’remostlyconcernedwiththeCSSusage.
Ourcalendarcomponent,describedabove,wouldequatetowhatBEMcallsablockwhiletheinnerHTMLwouldbeourelement.
ThetypicalpatternfornamingclassesusingBEMistoincludeourblocknameineveryselectorandnameeachelementoftheblockusing__toseparatethem.Modifiersaredenotedby--.
UsingBEMourCSSmightlooksomethinglikethis:1.calendar-component{
2width:400px;
3}
4.calendar-component__list{
5width:60%;
6display:inline-block;
7padding:0;
8}
9.calendar-component__list:first-child{
10width:30%;
11}
12.calendar-component__list-item{
13list-style:none;
14}
15.calendar-component__day{
16font-weight:bold;
17}
WhencomparingthevisualdisplayoftheblocktoourCSSselectors,itisallperfectlyclearataglancewhatstylesareaffectingwhatpieces.
Ifwelookatthatthirdselector.calendar-component__list:first-childyoumightnoticethatit’stheperfectopportunitytousewhatBEMcallsamodifiersinceallit’sreallydoingismodifyinganotherwiseperfectlynormal.calendar-component__list.
BEMconventionwouldbetocreateadifferentclassandapplyittothetheelementthatvariesfromitscompanions.OurHTMLcouldchangetosomethinglikethis;let’scallourvariationofthe.calendar-component__list.calendar-component__list--smallersincethevariationisdefiningasmallerwidth:1<divclass="calendar-component">
2<ulclass="calendar-component__listcalendar-component__list--smaller">
3<liclass="calendar-component__list-item">Jan<spanclass="calendar-componen\
4t__day">01</span></li>
5<liclass="calendar-component__list-item">Feb<spanclass="calendar-componen\
6t__day">29</span></li>
7</ul>
8<ulclass="calendar-component__list">
9<liclass="calendar-component__list-item">NewYear's!</li>
10<liclass="calendar-component__list-item">LeapDay!</li>
11</ul>
12</div>
Now,ifweswap.calendar-component__list:first-childwithournewmodifierclass.calendar-component__list--smallerwe’llbeinbusiness!
Sowhywouldyouwanttodoallthis?Well,thereareprobablyalotofopinionsoutthereregardingwhatmakesthissouseful,butafewofthemoreimmediatelydigestiblereasonsare:
1. BEMmakesscanningCSSdocumentseasier2. BEMavoidsdeepselectorandinheritancehell3. BEMmakesitsimpletolookatavisualelementandfigureoutexactlywhichCSS
selectorisstylingit
AninterestingthingaboutwritingyourCSSentirelywithBEMisthatyou’reessentiallyremovingtheCfromCSSandignoringthecascadingfeature.Thismeansyouendupwritingalotmoreclasses,butyoualsoavoidsomeofthetroublethatthecascadecancause.
IfyouwanttoreadmoreaboutBEMthanthisshortintroductioncanprovide,checkouttheofficialwebsite.
UtilityAutilitybasedmethodologycentersaroundtheideathatasingleclassshoulddoveryfewthingsandthatanynumberofCSSclassescanbeappliedtoanelementtoobtainthedesiredoutput.Usingautilityclassparadigmisactuallyfairlystraightforward,althoughorganizationcanbecomequiteahassleduetothesheernumberofutilityclassesthatgetcraftedthroughoutthecourseofasitebuild.
Beforewetalkaboutwhythismightbeuseful,let’stakealookatsomeutilityclassexamples:1.small-text{
2font-size:11px;
3}
4
5.underlined{
6text-decoration:underline;
7}
8
9.black-bg{
10background-color:#000000;
11}
12
13.float-left{
14float:left;
15}
Theseareallfairlystandardexamplesofutilityclasses.Eachclasspresentedabovedoesonlyonething,althoughitisperfectlyacceptableforthemtodomore,soifyouwantedsomeparagraphoftexttobe11pxandunderlined,you’dneedtoapplyboth.small-textand.underlinedtothemarkup:1<pclass="small-textunderlined">Loremipsum</p>
Ifthissoundsinsane,nobodycanblameyou.Theusefulnessofthismethodologyistoughtoseeatfirst,solet’stalkaboutwhywe’dgothisroute.
Firstly,it’sveryquicktostylesomethingnewifyou’vegotanarmyofutilityclassesalreadysetupinyourstylesheet.Ifyouneedyourtexttobered,byGod,justthrowa.red-textclassonit.
Secondly,usingutilityclassesmeansthereisverylittlerepetitioninyourstylesheetswhichmeansasmalleroverallfilesizewhich,inturn,meansafasterloadtimeforyourstylesheet.Withacomponentbasedmethodologyyoumayfindyourselfrepeatingfloat:left;onlotsofelements,butusingutilityclassesyoumayonlyhaveonefloat:left;inyourentiredocument.
Usingutilityclassesexclusivelybringsitsownsetofchallengestoaprojecthowever,asitbecomestheresponsibilityofHTMLauthorstostyletheircontentaswellascreateit.Thebenefitsofeaseandreducedrepetitioninyourstylesheetsmightbenegligiblewhencomparedwiththetroubleofcreatingnewcontentusingutilityclasses.IfyourworkflowmakesiteasiertomodifyHTMLclassesthanupdatestylesheets,utilityclassesmightbeforyou.
MixingandMatchingProbablythemostusefulwaytoauthoryourstylesistotakeabitfrombothcomponentandutilitymethodologies.
Buildingyourvisualpatternsascomponentsisoneofthebestwaystomakesureyourstylesscalewell.Ittakesalotofthoughtoutoftheauthoringprocessbyprovidingwhatare,essentially,templatesofstyle.Bycreatingone-offutilityclassesthatdon’treallyneedtobeembeddedintoaparticularcomponent,youcaneasesomeofthepainofmodifyingacomponentasneeded.
Let’sjumpbacktoourcalendarcomponentexampleandintroduceanewscenariothatmixesthetwomethodologies.
Let’ssaythatonoursitewehavemultipleinstancesofourcalendarcomponentsprinkledinvariousareasofthelayout.Oneinasidebar,oneinthemastheadandanotherinthefooter(weREALLYwantpeopletoknowaboutourupcomingeventsokay?).However,whenauservisitsoursiteonabrowserlessthan400pxwidewewanttohidethecomponentinthefooterwithoutactuallyaffectingtheotherinstancesonthepage.Thisistheperfectspotforautilityclassbecausewehaveascenarioinmindthatcouldbeappliedtoanycomponent.Thatscenariois“hidethisthingwhenthesitewidthislessthan400px.”
Ourutilityclassmightlooklikethis:
1.hide-under-400{
2display:none;
3}
4@mediaonlyscreenand(min-width:400px){
5.hide-under-400{
6display:block;
7}
8}
Nowwecanapply.hide-under-400toourcomponent,oranyothercomponent,andhaveitdisappearfromviewwheneverourbrowserwindowislessthan400pxwide.
PreprocessorsApreprocessorisaprogramthattakessomekindofinputandspitsoutanotherkind.Intheworldoffront-enddevelopment,wehaveanumberoftheseprogramsatourdisposalwhichmakeswritingCSS(orJavaScriptandHTML)amorepleasantexperience.ThebasicideaisthatpreprocessorsletyouwriteyourstylesusingadifferentsyntaxwhichthenspitsoutintoplainoldCSSwhichyourbrowsercanread.
ThemainbenefitofusingpreprocessorsisthatmostcomewithfeaturesnotnormallyfoundinCSS(atthetimeofwriting,anyway).Thefeaturesthemselvesarealittledifferentdependingonwhichpreprocessorwe’relookingat,butmostcomewithawaytowritevariablesforstoringvaluesandnestingsupportso,amongotherthings,youdon’tneedtowritelengthyselectors.
Sass(SyntacticallyAwesomeStyleSheets)Atthemoment,theSasslanguageiswidelyusedinthefront-endcommunity.Sasssportsanumberoffantasticfeaturesandhastwosyntaxestochoosefrom;theoriginalindentedsyntaxSassandthemorerecentSCSSsyntaxwhichusethe.sassand.scssfileextensionsrespectively.TheoriginalversionofSassisbuiltonRubyalthoughotheroptionsareavailableincludingPHP,C,andJava.SassevenhasascriptinglanguagecalledSassScriptunderthehoodsoifyou’recomfortablewithsuchthings,youcouldevenwriteyourownfeatures.
Installing
TogetstartedusingSass,you’llneedtoinstalltheprogramonyourcomputeroruseathird-partyapplicationthatcontainsitsuchasCodeKitorScout.Inthissectionwe’llcoverthetypicalwaytoinstallitwithRuby,butforamorethoroughoverviewofalltheoptionsoutthere,visitSass’installationpage.
ThissectionalsoassumesthatyouhaveRubyalreadyinstalledonyourmachine.InstallingRubyisoutsidethescopeofthisbook,butifyou’reusingaMacthegoodnewsisthatyou’vealreadygotit.WindowsandLinuxmachineshavetheirowninstallationprocesses.
Onceyou’vegotRubyonyoursystem,simplyheadtoyourcommandprompt(TerminalinOSX,cmdinWindows)andinstalltheSassgem:1geminstallsass
That’sallthereistoit!Youcanmakesureeverythingisinstalledproperlybyrunningsass-v.Ifyougetoutputlikeso,thenyou’reallset:1sass-v
2Sass3.4.9(SelectiveSteve)
UsingSass
Nowthatyou’vegotSassinstalled,you’llfindit’sfairlysimpletouse.ThepatternforusingSassfromthecommandlineiseasy:1sass[options][input][output]
Soifyou’vegotastyle.sassfileinyourcurrentdirectoryyoucouldrun:1sassstyle.sassstyle.css
tohaveSasscompileyourstyle.sassstylesheetintostyle.css.
UsingtheSCSSsyntaxisalittletrickier,andyou’llneedtopassanoptiontothecommandtotellitthatyou’reusingSCSSinsteadofSass.Assumingyouhavestyle.scssinsteadofstyle.sass,thatcommandwouldlooklikethis:1sass--scssstyle.scssstyle.css
Youcanwatchafileoranentiredirectoryforchangestoyour.sassfilesandhavetheprogramautomaticallycompileCSSanytimeitdetectschangesusingthe--watchoption.Thisisreallyquitehandyifyou’reworkingonaprojectanddon’twanttokeeprunningthecommandmanuallytoseethechangesyou’remaking.
Towatchasinglefile:1sass--scss--watchstyle.scss:style.css
Towatchanentiredirectory:1sass--scss--watchyour_scss_directory:your_compiled_css_directory
ThesasscommandhasanumberofotherusefuloptionsavailabletocustomizetheoutputofyourcompiledCSS.Youcanseethefulllistbyrunning:1sass-h
TheSassSyntax
TheoriginalSasssyntaxisnotdirectlycompatiblewithCSS.Thatmeansyoucan’tjustchangeyourfileextensionsto.sassandexpectthingstorun.Sassusesindentationtoseparatepropertydeclarationsfromselectors,andnewlinesinsteadofsemicolonstodenotetheterminationofaproperty.Asaquickexample,let’scomparethesamestyledeclarationinCSSandSass:
CSS1.class-name{
2color:purple;
3background:blue;
4}
Sass1.class-name
2color:purple
3background:blue
Thiscanbealittleconfusingtoactuallyuseatfirst,butifyou’restartinganewprojectyoumayfinditpreferabletousetheSasssyntaxbecauseyouactuallyendupwritingfeweroverallcharacters.
TheSCSSsyntax
UnliketheSasssyntax,theSCSSsyntaxiscompatiblewithCSS.YoucancopyandpasteanygenericCSSintoa.scssfileandhaveabsolutelynoissues,althoughofcourseitdoesn’tworktheotherwayaround.TheSCSSsyntax,forthemostpart,usestheexactsamesyntaxthatCSSdoes.Yougroupsetsofpropertiesonaselectorwithbracesandyouterminatepropertydeclarationswithasemicolon.Indentiondoesn’tmatteratall,althoughyoushouldbeconsistentforyourownmentalwellbeing.
ManypeopleprefertheSCSSsyntaxbecauseit’ssocompatiblewithexistingCSS.Ifyou’vealreadygotawellestablishedprojectandyou’dliketostartswitchingtoSass,usingtheSCSSsyntaxisaneasywaytogetstarted.
VariablesinSass
VariablesinSassaredenotedbya$whichshouldfeelprettyfamiliarifyou’veeverusedalanguagelikePHP.BoththeSCSSandSasssyntaxesusethesameformatfordeclaringavariable,althoughintheSasssyntaxyouwon’tneedtouseasemicolonattheend:
Sasssyntax1$my-blue-variable:#0000ff
2headerh2
3color:$my-blue-variable
SCSSsyntax1$my-blue-variable:#0000ff;
2headerh2{
3color:$my-blue-variable;
4}
CSSoutput1headerh2{
2color:#0000ff;
3}
NestingSelectorsinSass
Sasshastheconceptofnestingselectorswithinoneanothertoavoidhavingtowritereallylongstringsofselectorstogetthespecificityyouwant.Theeasiestwaytounderstandthisconceptistovisualizeit.Let’ssaywewanttotargetthisspecificCSSselector:#mainarticle.author.Todothatwecanwriteourstylesfor.authorinsidethestylesforarticleandwritethoseinside#main.TodothatwithSass,we’dwrite:
Sasssyntax1#main
2background:#ffffff
3
4article
5margin:0auto
6color:#000000
7
8.author
9background:#cecece
10padding:10px
SCSSsyntax1#main{
2background#ffffff;
3
4article{
5margin:0auto;
6color:#000000;
7
8.author{
9background:#cecece;
10padding:10px;
11}
12
13}
14
15}
CSSOutput1#main{
2background:#ffffff;
3}
4#mainarticle{
5margin:0auto;
6color:#000000;
7}
8#mainarticle.author{
9background:#cecece;
10padding:10px;
11}
Selectingparentswith&
AnotherhandyfeatureofnestingthatSassprovidesisawaytoreferencetheparentselectorusing&.Thisisparticularlyhandywhenyouwantanestedselectortohaveadifferentstylewhenitsparentisslightlydifferent,forinstance,whentheparenthasanextraclassorstate.Aneasyexampleofthisparentselectorusageisstylingthevariousstatesofalink:
Sasssyntax1a
2color:blue
3
4&:hover
5color:green
6
7.some-class&
8color:purple
SCSSsyntax1a{
2color:blue;
3
4&:hover{
5color:green;
6}
7
8.some-class&{
9color:purple;
10}
11
12}
CSSOutput1a{
2color:blue;
3}
4a:hover{
5color:green;
6}
7.some-classa{
8color:purple;
9}
Anotherinterestingfeatureofthe&selectorisitsabilitytocreatecombinatorialexplosionsofselectorsusing&+&withinanestedlistofselectors.Thiswillgenerateeverypossiblepermutationoftheparentselectors.
Note:The+operatorinCSSdenotessiblingelements.
Sasssyntax1ul,quote,p
2
3&+&
4margin:0
SCSSsyntax1ul,quote,p{
2
3&+&{
4margin:0;
5}
6
7}
CSSOutput1ul+ul,
2quote+ul,
3p+ul,
4ul+quote,
5quote+quote,
6p+quote,
7ul+p,
8quote+p,
9p+p{
10margin:0;
11}
MixinsinSass
Mixinsareareallyhandywaytogroupsetsofpropertiestogethersoyoucanreusethem.Acommonpracticeistousemixinsforwritingvendorprefixes(e.g.-webkit-border-radius,-moz-border-radiusetc),butyoucanusethemtogroupanykindofpropertiesyouwant.
Todefineamixin,declare@mixinyour_mixin_nameinSCSSand=your_mixin_nameintheSasssyntax.Let’screateamixinthataddsadashedborderandsomepaddingaroundanelement:
Sasssyntax1=dashed_border
2border:1pxdashed#000000
3padding:10px
SCSSsyntax1@mixindashed_border{
2border:1pxdashed#000000;
3padding:10px;
4}
Nowthatthemixinisdefined,wecanuseitonanyselectorwewantbyusingthemixin’sname,prefixedwith+inSassand@includeinSCSS:
Sasssyntax1.foo
2+dashed_border
SCSSsyntax1.foo{
2@includedashed_border;
3}
CSSOutput1.foo{
2border:1pxdashed#000000;
3padding:10px;
4}
Mixinscanalsotakearguments!Let’ssaywewanttogivetheoptiontochangethebordercolorandtheamountofpaddingwhereverweusethemixin.Todothat,we’llneedtochangeourmixindefinitionsalittlebit:
Sasssyntax1=dashed_border($border_color,$padding)
2border:1pxdashed$border_color
3padding:$padding
SCSSsyntax1@mixindashed_border($border_color,$padding){
2border:1pxdashed$border_color;
3padding:$padding;
4}
Nowwewouldsimplypassinacolorasthefirstparameterandapaddingvalueasthesecond:
Sasssyntax1.foo
2+dashed_border(#dddddd,4px)
SCSSsyntax1.foo{
2@includedashed_border(#efefef,.5em);
3}
Additionally,mixinsmaybedefinedwithdefaultargumentvalues.Adefaultvalueforanargumentwillbeusedwhennoargumentispassedwhenthemixinisreferencedinaselector.Let’sreuseourdashedbordermixinandthistimedefineadefaultvalueforthe$border_colorargument.Todothis,we’llneedtochangetheorderofourargumentsinourmixin’sdefinitionbecauseargumentswithdefaultvaluesmustcomeafterargumentswithout:
Sasssyntax1=dashed_border($padding,$border_color:#000000)
2border:1pxdashed$border_color
3padding:$padding
SCSSsyntax1@mixindashed_border($padding,$border_color:#000000){
2border:1pxdashed$border_color;
3padding:$padding;
4}
5
6.foo{
7//Withoutasecondargument,thedefaultbordercolorwillbeused.
8@includedashed_border(10px);
9}
10
11.bar{
12@includedashed_border(10px,green);
13}
CSSOutput1.foo{
2border:1pxdashed#000000;
3padding:10px;
4}
5
6.bar{
7border:1pxdashedgreen;
8padding:10px;
9}
Sassmixinscanalsobecalledbyusingargumentkeywordslikeso:@includedashed_border($padding:10px,$border_color:green);.Toillustratetheusefulnessofthisfeature,let’saddathirdargumenttodashed_bordercalled$border_widthandgiveitadefaultvalue:
Sasssyntax1=dashed_border($padding,$border_color:#000000,$border_width:1px)
2border:$border_widthdashed$border_color
3padding:$padding
SCSSsyntax1@mixindashed_border($padding,$border_color:#000000,$border_width:1px){
2border:$border_widthdashed$border_color;
3padding:$padding;
4}
Nowsupposethatinourselector.foo,wewanttoaddadashedborderwithawidthof5pxbutleavethedefault$border_colorvaluealone.Since$border_widthisthethirdargument,wecannotsimplypassavaluetoitwithoutalsopassingavalueto$border_colorunlessweusethekeywordfunctionality:
Sasssyntax1.foo
2+dashed_border(10px,$border_width:5px)
SCSSsyntax1.foo{
2@includedashed_border(10px,$border_width:5px);
3}
CSSOutput1.foo{
2border:5pxdashed#000000;
3}
Usinganargument’skeywordinthereferencetothemixin,youcansetthatargument’svaluenomatterwhatpositionit’sdefinedin.@includedashed_border($border_color:green,$padding:4px,$border_width:8px)wouldcompilejustfinesincewe’reusingkeywords,eventhoughwe’redeclaringargumentvaluesinadifferentorderthantheyweredefinedinthemixin.
Sassmixinsareverypowerfulandthere’salotmoreyoucanaccomplishwiththemthanwhat’sbeenshownhere.ForacompleteoverviewofmixinsinSass,takealookatthemixindocumentation.Hereyou’llfindalotmoreinformationaboutmixinarguments,likehowtousevariablesasarguments,howtopasscontentblockstoamixin,andvariablescopewithinmixinsamongotherhandynuggetsofinformation.
ControlExpressions
Toaddtothepotentialcomplexityofthelanguage,Sasssupportstheusageofcontrolexpressionssuchasif,for,each,andwhile.Formoreinformationonthissubject,takealookatSass’documentation.Controlexpressionsarenotlimitedtousageinmixins,butthisisthemostcommonplaceyou’llfindtheminthewild.
Extending
Theconceptof‘extending’aselectorinSassissomewhatsimilartomixins.Youcantakeallofthepropertiesappliedtooneselectorandextendthemontoanotherone.
TheactualusageoftheextendfunctionalityisthesameinbothSasssyntaxandSCSS.Simplywrite@extendbeforethenameoftheselectoryouwanttoextendinthepropertylistofyourselector:
Sasssyntax1.div_1
2color:blue
3
4.div_2
SCSSsyntax1.div_1{
2color:blue;
3}
4
5.div_2{
7}
Themaindifferencebetweenextendingaselectorandusingamixinisthatifyouuseamixinmultipletimesonamultitudeofdifferentselectors,yourCSSoutputwillhavethesamecontentrepeatedmultipletimes.Usingourmixinexamplefromabove,thatmightlooklike:
Sasssyntax1.div_1
2+dashed_border
3
4.div_2
5+dashed_border
SCSSsyntax1.div_1{
2@includedashed_border();
3}
4
5.div_2{
6@includedashed_border();
7}
CSSOutput1.div_1{
2border:1pxdashed#000000;
3padding:10px;
4}
5
6.div_2{
7border:1pxdashed#000000;
8padding:10px;
9}
Byextendingaselectorontoanotherone,thetwoselectorswillbecombinedandonlythedifferenceswillbesplitoutintotheirownselectors:
Sasssyntax
1.div_1
2border:1pxdashed#000000
3padding:10px
4
5.div_2
6color:blue
SCSSsyntax1.div_1{
2border:1pxdashed#000000;
3padding:10px;
4}
5
6.div_2{
7color:blue;
9}
CSSOutput1.div_1,.div_2{
2border:1pxdashed#000000;
3padding:10px;
4}
5
6.div_2{
7color:blue;
8}
Operators
TheSasslanguagealsosupportssomemathematicaloperatorsthatyoucanusewhenwritingyourstyles.Thesupportednumberoperatorsare:
+-Addition--Subtraction*-Multiplication/-Division%-Modulo
Inadditiontothesemathematicaloperators,Sassincludesrelationalandequalityoperators,typicallyforuseincontrolexpressions:
<-Lessthan>-Greaterthan<=-Lessthanorequalto>=-Greaterthanorequalto==-Equalto!=-Notequalto
PartialsandImporting
AcommonwaytoorganizeyourCSSistosplitoutcategoriesorspecificchunksofstylesintotheirownfiles.Usingacomponentbasedmethodology,eachindividualcomponent
mighthaveitsownfilesothatdevelopersknowexactlywheretofindaparticularcomponent’sstyles.
OutoftheboxCSSsupportsthe@importdirectivewhichisusedforincludingotherCSSfilesintoaCSSfile.Thisisgreatfororganization,butnotsogreatfortheoverallperformanceofyourpage.Eachusageof@importcreatesanHTTPrequestwhichwillslowdowntherenderingofyourpage.
WithSass,youcancreate‘partials’whichareessentiallythesameassiloedCSSfilesandtheycanbeincludedinanotherSassfileinthesamewaythatCSScan@importotherCSSfiles.Thedifferenceis,whenSasscomesacrossan@importdirectiveduringcompiletime,itconcatenatesthefilestogethercreatingasingleCSSstylesheet.Inthisway,youcanhavealltheniceseparationofstyleswithoutcreatingexcessHTTPrequests.
Here’sanexampleofhowtouseapartialfilewithinSass:
Sasssyntax1/*main.sass*/
2@importpartial
3
4.main_class
5background:#efefef
6
7/*_partial.sass*/
8.partial_class
9color:purple
SCSSsyntax1/*main.scss*/
2@import'partial';
3
4.main_class{
5background:#efefef;
6}
7
8/*_partial.scss*/
9.partial_class{
10color:purple;
11}
CSSOutput1/*main.css*/
2.partial_class{
3color:purple;
4}
5.main_class{
6background:#efefef;
7}
Thenamewegavethepartialfileinthisexample,_partial.sassisprefixedwithanunderscoresothatSassdoesn’tcreateapartial.cssfilewhenwecompilethedirectory,whichitwilldoifweleaveofftheunderscore.Thereisnorequirementthatpartialfilesbenamedthiswayandimportingapartialusingthenameofthefilewithouttheextensionwillworkwhetherornotyou’veprefixedthefilenamewithanunderscore.
Itisalsoimportanttonotethatthecontentsofourpartialwillappearinthefinaloutputintheplacetheywereimportedwithinthemainfile.Inourexampleabove,[email protected]_class,sothecontentsofourpartial,.partial_classwerecompiledoutabove.main_class.
OtherSassTools
ThepopularityofSasshasgivenrisetoanumberofauthoringframeworksandlibrariesbuiltspecificallyforthelanguage.ThelargestplayersinthisspaceatthetimeofwritingareprobablyCompassandBourbon.Eachoftheseprovidesagoodnumberofbuilt-inmixinsandfunctionalitythatyoumightotherwisecreatebyhandforyourprojects,includinglayouthelpers,typographyutilitiesandCSS3shortcuts.
Thebenefitsofusinglibrarieslikethesearemany,butdependingonthescopeandsizeofyourprojecttheycouldverywellbeoverkill.TyingyourSassprojecttoasingleframeworkorlibrarysuchasthesecanbealimitingfactorwhenSassitselfgetsupdatedandyouwanttoupdatetothelatestversionsinceyourlibrarywilllikelyrequireaspecificversionofSasstorun.
Overall,toolslikeCompassareincrediblyuseful,particularlyforbeginners.
Summary
Inthisbook,we’reonlyscratchingthesurfaceofwhatSassiscapableof.ThereareotherpowerfulfeaturesinthislanguagethatarehonestlybestdescribedwithinSass’owndocumentationandgivingsuchacursoryoverviewashasbeengiventherestofthefeaturespresentedinthisbookwouldlikelybeadisservicetoreaders.Moredetailedinformationonfeatureswe’vecoveredaswellasthosewehaven’tcoveredsuchaslists,maps,placeholderclasses,anddefiningcustomSassfunctionsamongotherscanbefoundhere.
LessLess.jsisaJavaScriptbasedpreprocessorthatatonepointwaskingoftheveritableCSSpreprocessorhill.Morerecently,itseemstohavetakensomewhatofabackseattoSassinoverallpopularity,butitisstillwidelyused.LessandSasshaveaverysimilarfeatureset.
LikeSass,LessisaverypowerfullanguageandjustliketheSCSSsyntaxosSass,Less’syntaxisbackwardscompatiblewithvanillaCSSmakingitrelativelyeasytopickupanduse.
Lesscanbeusedviathecommandline,programmaticallyviaJavaScriptorontheflyviathebrowserbyincludingtheless.jsscriptonyourpage.ThelattermethodisnotrecommendedduetothesheersizeofthescriptandthefactthatsomebrowsersmaycompileLessintoreadableCSSmoreslowlythanothers.Inthisbook,we’llconcernourselvesonlywiththecommandlineusage.
Installation
ToinstallLesslocallyforuseonthecommandline(thepreferredmethod),you’llneedtohavethenpmpackagemanagerinstalled.TheinstallationprocessfornpmcanbefoundintheDependencyManagementchapterofthisbook.
Onceyouhavenpm,installationisabreeze.Simplyheadtoyourcommandlineandrun:1npminstall-gless
Many*nixsystemsalreadycomewithaprogramcalledless,sowhatourpackagemanagerisactuallyinstallinghereisaprogramcalledlesscwhichstandsfor“lesscompiler.”Tocheckwhetherornottheinstallationwassuccessful,runthefollowingandlookforsimilaroutput:1lessc--version
2lessc2.1.2(LessCompiler)[JavaScript]
UsingLess
TohaveLesscompileourcodewecansimplyrunthelesscprogramonourLessfile(theextensionforLessfilesis.less):1lesscstyle.less
Thiswillprintoutthecompiledresulttoyourterminalscreenasapreview.Ifyouactuallywanttosaveyourcompiledstylesyou’llneedtosendthatoutputintoitsownfile:1lesscstyle.less>style.css
ThelesscprogramhasatonofoptionsyoucanusetotweaktheoutputofyourcompiledLess.Toseethem,run:1lessc--help
VariablesinLess
VariablesinLessaredefinedbyprefixingavariablenamewith@:
Less1@a-less-variable:10px;
2
3.foo{
4width:@a-less-variable;
5}
CSSOutput1.foo{
2width:10px;
3}
Lessvariablesdonotneedtobedefinedbeforetheyareusedinadocument.Intheaboveexample,@a-less-variablecouldhavebeendefinedbelow.foowithoutanyissue.
Aninteresting,althoughmaybenottotallyintendedfeatureofLess’variables,isthattheyarescoped,meaningthatthecompilerwillsearchforavariabledefinitionwithinthelocalscopeofaselectorbeforemovinguptoitsparent.Inthecaseofnestedselectors,thismeansmovinguptotheparentselector.Inthecaseofselectorswithnoparent,Lesswilllookintheglobalscope.
Here’sanexample:1@dark-color:#000000;
2
3.foo{
4@dark-color:#777777;
5color:@dark-color;
6}
Above,[email protected]:@dark-coloritfirstlookedwithinthatselectorforadefinitionof@dark-colorandonfindingone,appliedittothepropertythatreferencedit.Therefore,thecolorusedis#777777.
If@dark-colorhadnotbeendefinedinthislocalscope,thecompilerwouldhavemovedupalevel,totheglobalscopetosearchforadefinitionof@dark-color.
ThisconceptofvariablescopeisveryimportanttogetahandleonwhenusingLesssincethecompilerwillusethelastdefinitionofavariableinthecurrentscope.
Inthefollowingexample,we’lldefine@dark-coloranumberoftimesandseewhathappenstotheoutput:1@dark-color:#000000;
2
3.foo{
4color:@dark-color;
5}
6
7@dark-color:#777777;
8
9.bar{
10color:@dark-color;
11}
12
13@dark-color:#333333;
Intheaboveexample,both.fooand.barwillbeoutputwithacolorof#333333sincethatwasthelastdeclarationof@dark-colorinthecurrentscope.NotethatthisisnotsomethingonewouldactuallywriteandissimplybeingusedtoillustratethefactthattheLessreadsvariablesbottom-to-topinthecurrentscope.
VariablesinLessareconstantsandgenerallyshouldnotbedefinedmorethanonce.
NestingSelectorsinLess
NestinginLessfunctionsthesamewayasnestingusingSass’SCSSsyntax,includingtheusageof&asaparentselector:
Less1.foo{
2color:green;
3
4.bar&{
5color:black;
6}
7
8.baz{
9color:purple;
10}
11
12}
CSSOutput
1.foo{
2color:green;
3}
4
5.bar.foo{
6color:black;
7}
8
9.foo.baz{
10color:purple;
11}
Mixins
MixinsinLessarealittledifferentthanmixinsinSass.Infact,they’reabitclosertothewaySass’extendfunctionalityworks.InLess,mixinsareessentiallyclassorIDselectorsthemselveswhichcanbeextendedontootherclasses.TomixinanIDorclass’propertiesinadifferentselector,simplywritethemixinselector’snameoptionallysuffixedwith()iftherearenoarguments:
Less1.mixin{
2border:1pxsolid#000000;
3}
4
5.foo{
6.mixin();
7}
CSSOutput1.mixin{
2border:1pxsolid#000000;
3}
4
5.foo{
6border:1pxsolid#000000;
7}
Ifyouwanttodefineamixinwithoutitgettingcompiledasitsownselector,suffix()onthedefinitionofthemixinlikeso:
Less1.mixin(){
2color:black;
3}
4
5.foo{
6.mixin;
7}
CSSOutput1.foo{
2color:black;
3}
Thisalsoworkswithmoredeeplyspecifiedmixinselectors.Duetothisbehavior,itispossibletonamespaceyourmixinswhichishandyforcategorizingmixinsthatallrelatetosomesimilarproperty.AsimplisticexampleofthiswouldbetonamespaceasetofmixinsthatcontrolthedirectionofCSS’floatproperty.The.floatclassiscreatedasthemixinwhichwedon’twanttobeoutputtedinourcompiledCSSandnestedwithinare.leftand.right.In.fooand.barwecallthenamespacedmixins:
Less1.float(){
2
3.left{
4float:left;
5}
6
7.right{
8float:right;
9}
10
11}
12
13.foo{
14.float.left();
15}
16
17.bar{
18.float.right();
19}
CSSOutput1.foo{
2float:left;
3}
4
5.bar{
6float:right;
7}
Thereareactuallyamultitudeofwaystousenamespacedmixinsinthislanguage.Forinstance,allofthefollowingwouldoutputthesameCSSwhenaddedtothepropertyblockofaselector:
.float.left
.float.left()
.float.left
.float.left()
.float>left
MixinArguments
MixinsinLessalsosupportoneormoreargumentsintheirdefinition.Argumentsdefinedontheseso-calledparametricmixinsareeithercommaseparatedorsemicolonseparatedandagain,justlikeSass,mixinargumentscanhavedefaultvaluesandcanbeaccessedusingtheargumentkeywordswheretheyarereferenced.BelowisasimpleexampleofaLessmixinwithmultiplearguments:
1.mixin-foo(@border-width,@border-style,@border-color:#000000){
2border:@border-width@border-style@border-color;
3}
Wecancallthismixinonanewselectorinanumberofways.Wecansimplypassvaluesinacommaseparatedlist,specifytheargumentvalueswithkeywords,andcompletelyignorethethirdparametersinceitwasdefinedwithadefaultvalueof#000000:
.mixin-foo(1px,solid,#0000ff)
.mixin-foo(@border-width:1px,solid,#0000ff)
.mixin-foo(1px,solid)
Commasinamixin’sargumentdefinitioncanserveanotherpurposebeyondactingasaseparator;theymayalsoactasaCSSlistseparator.Ifyoudecidetousecommastoseparatearguments,thenyouremovetheabilitytoaddCSSlistasanargumentsinceLesswillassumeeachiteminthelistisitsownargument.Forthisreason,itisrecommendedtoseparateargumentswithsemicolonsratherthancommas.
Let’srewrite.mixin-footoincludeaCSSlistargument:
Less1.mixin-foo(@border-width;@border-style;@border-color;@font-stack){
2border:@border-width@border-style@border-color;
3font-family:@font-stack;
4}
5
6.foo{
7.mixin-foo(1px;solid;#0000ff;Helvetica,Arial,sans-serif);
8}
CSSOutput1.foo{
2border:1pxsolid#0000ff;
3font-family:Helvetica,Arial,sans-serif;
4}
SomethingtonoteaboutLessmixinsisthatitiscompletelyvalidtowritemultiplemixinsofthesamenamewithavaryingnumberofargumentsoneach.Whenamixinlikethisisreferencedinaselector,Lesswillattempttouseanyandallmixinswhosenumberofpassedargumentsmatchthenumberofdefinedarguments.Toseethisfunctionalityinaction,let’sdefineacoupleversionsof.mixin-foo:
Less1//MixinA
2.mixin-foo(@border-width;@border-style;@border-color;@font-stack){
3border:@border-width@border-style@border-color;
4font-family:@font-stack;
5}
6
7//MixinB
8.mixin-foo(@line-height,@height:100px){
9line-height:@line-height;
10height:@height;
11}
12
13//MixinC
14.mixin-foo(@width){
15width:@width;
16}
17
18//MixinD
19.mixin-foo(@font-size){
20font-size:@font-size;
21}
22
23.foo{
24.mixin-foo(100px);
25}
CSSOutput1.foo{
2line-height:100px;
3height:100px;
4width:100px;
5font-size:100px;
6}
Sowhathappenedthere?Ourcallto.mixin-fooincludedonlyoneparameterwhichmatchedthenumberofargumentsdefinedforbothMixinCandMixinDso.foogotitswidthandfont-sizepropertiesfromthosetwomixinsrespectively.Additionally,MixinB’ssecondargumenthadadefaultvaluedefinedsoourcallof.mixin-foo(100px)in.foomatcheditaswell,givingitbothline-heightandheightvalues.
Intheabovescenario,ifwehadcalled.mixin-foowithfourarguments,onlyMixinAwouldhavematched.Ifwehadcalleditwithonlythreearguments,noneofthemixinswouldhavematchedandLesswouldhavegivenusanerrorduringcompiletime.
There’salotmoreyoucandowithLessmixinsthanwehaveroomtocoverinthescopeofthisbook.Tolearnmoreaboutusingmixinsasfunctions,argumentpatternmatching,andotherniftymixinfeaturesofLesscheckoutthedocumentation.
Extending
ExtendingaselectorontoanotherselectorispossiblebyusingLess’special:extendpseudo-class.Thispseudo-classtakesoneormoreargumentsofselectors.TheseargumentstellLesswhichselector’sstylesyouwanttoextendontothecurrentselector.Inalistofselectors,anyandallofthemmayusethe:extendpseudo-class:
Less1.foo{
2color:green;
3}
4
5.bar{
6background:black;
7}
8
9.baz:extend(.foo),
10.qux:extend(.bar){
11font-size:1.5em;
12}
CSSOutput1.foo,
2.baz{
3color:green;
4}
5
6.bar,
7.qux{
8background:black;
9}
10
11.baz,
12.qux{
13font-size:1.5em;
14}
Inadditiontotheselectorargument(s),the:extendpseudo-classmayoptionallybepassedthekeywordallwhichwillmatcheveryinstanceoftheprecedingselectorargument(s).Topiggy-backonourpreviousextendexample,let’screateanestedinstanceof.barandpass.qux:extend(.bar)theallkeyword:
Less1.foo{
2color:green;
3}
4
5.bar{
6background:black;
7
8.norf&{
9border-radius:8px;
10}
11
12}
13
14.baz:extend(.foo),
15.qux:extend(.barall){
16font-size:1.5em;
17}
CSSOutput1.foo,
2.baz{
3color:green;
4}
5
6.bar,
7.qux{
8background:black;
9}
10
11.norf.baz,
12.norf.qux{
13border-radius:8px;
14}
15
16.baz,
17.qux{
18font-size:1.5em;
19}
Above,wecanseethat.norf.bazand.norf.quxnowhavetheborder-radiuspropertyintheirruleset.Withouttheallkeyword,the.quxwouldhaveonlyextendedtheselectorwegaveit,.bar.Inthatscenario,.norf.barwouldnothavematchedourselectorargument.Selectorargumentswithouttheallkeywordmustmatchexactly.TheScopeof:extend
Intheresponsivedrivenworldoffront-enddevelopmentwelivein,you’llprobablyfindyourselfwritingalotofmediaqueriestochangethestyleofyourcontenttobestsuitavarietyofscenarios.Somethingtokeepinmindwhenusing:extendinLessisthatyoucannotextendaclassthatisoutsideyourcurrent@mediablock:
Less1@mediascreenand(min-width:400px){
2.foo{
3color:blue;
4}
5}
6
7@mediascreenand(min-width:600px){
8.bar:extend(.foo){
9background:brown;
10}
11}
12
13.baz{
14line-height:1.5;
15}
CSSOutput1@mediascreenand(min-width:400px){
2.foo{
3color:blue;
4}
5}
6
7@mediascreenand(min-width:600px){
8.bar{
9background:brown;
10}
11}
12
13.baz{
14line-height:1.5;
15}
Intheaboveexample,.bar’sextensionof.fooiscompletelyignoredsince.foodoesnotexistinthescopeofthis@mediablock.
Whileextendingacross@mediablocksisano-go,weareabletoextendaselectorwithina@mediablockontoagloballyscopedselector.Inthepreviousexample,that’s.baz.Thecaveathereisthatextendinga@mediascopedselectorontoagloballyscopedselectorwillkeepthe@mediascopeonoutput.Essentially,anythingyouextendthatcomesfromaselectorwithina@mediablockwillonlybeextendedinthat@mediablock:
Less1.baz:extend(.foo){
2line-height:1.5;
3}
4
5@mediascreenand(min-width:400px){
6.foo{
7color:blue;
8}
9}
CSSOutput1.baz{
2line-height:1.5;
3}
4
5@mediascreenand(min-width:400px){
6.foo,
7.baz{
8color:blue;
9}
10}
Summary
We’vecoveredalotaboutLess’featuresinthissection,buttherearemanymanymore.InalotofwaysLessandSassarequitesimilar,butunderthehoodandinspecificimplementationsoftheirsharedfeaturestherearealotofsubtledifferences.Less,asalanguage,isverymatureandwelldocumentedandshouldsuitjustaboutanyprojectjustfine.It’seasytogetstartedwithandasyougetmorecomfortableusingitthemorecomplexfeaturesitprovidescanbeanenormousassettoyourfront-endworkflow.
Ifyou’reinterestedinmorethanaquickintroductiontoLess,headovertotheLesswebsiteanddigin.
StylusTheStyluspreprocessorrunsonnodeandboastsahugenumberofquality-of-lifetypefeatures.TheStylussyntaxmakesnearlyeverythingoptional,fromcolonseparatorsbetweenpropertiesandvaluestobracesaroundrulesets,evenmixinargumentparenthesis.
Stylusisrelativelynewonthesceneanditsharesafewsimilaritieswithotherpreprocessors.Forexample,StylussupportsanindentedsyntaxthatcloselymatchesSass’originalsyntax.Additionally,StylussupportsnestingselectorsintheexactsamewaythatLessandSassdo,nomatterwhichsyntaxyou’reusing.
Installation
InstallingStylusworksjustlikeinstallingLessoranyothernodepackage,byusingnpm:
1npminstall-gstylus
Runaquickchecktoseeifeverythingworkedandyou’rereadytogetstarted:1stylus--version
20.49.3
UsingStylus
Thestylusprogramisbyitselfjustaninterpreter.Runningitwithoutanyparameterswillbringyouintoawritingmodewhereyoucanwritestyluscode.Whenyouquittheprogram,it’llspitoutwhateverStylusyouwroteintoregularCSS.
IfyouwanttobeabletosaveyourStyluscode,you’llneedtostoreitinafileandthenrunthestylusprogramonthatfile,muchlikeyouwouldforSassorLess.ThetypicalextensionforStylusfilesis.styl,althoughlikemostotherthingsinStylus,thatextensionisoptional.
OnceyouhaveafiletostoreyourStyluscodein,youcansimplyrun:1stylusyour_file.styl
andthestylusprogramwillgenerateaCSSfilewithamatchingname.Inthiscase,that’syour_file.css.
Toseethefulllistofthestylusprogram’soptionsrun:1stylus--help
StylusSyntax
Asmentionedintheintroductoryparagraphofthissection,almosteverythinginStylusisoptional.What’smore,youcanmixandmatchdifferentsyntaxstylesinthesameStylusdocumentwithoutanyproblem.
Thisisbothexceedinglyneatandpotentiallydisastrous,particularlyforaprojectwithmultipledevelopersworkinginthecode.Itishighlyrecommended,ifyouchooseStylusforyourproject,thateveryoneagreeonaparticularsyntax.LikeLessandSass’SCSSsyntax,StylusdoessupporttheregularCSSsyntaxinadditiontoalltheothercraziness.
Let’stakesomeexampleCSSandwriteitinafewvalid,butdifferent,Stylussyntaxes:1.foo{
2color:blue
3background:black
4}
5
6.foo
7color:blue;
8background:black;
9
10.foo{
11colorblue
12backgroundblack
13}
EveryoneofthosedifferentsyntaxeswillprovidethesameCSSoutput:1.foo{
2color:blue;
3background:black;
4}
VariablesinStylus
Unliketheotherpreprocessorswe’vecoveredsofar,youdon’tneedtoprefixanyparticularsymboltodenoteavariableinStylus,althoughitdoesoptionallyletyouuse$ifthathelpsyousleepatnight:
Stylus1darkColor=#777777
2
3.foo
4color:darkColor
CSSOutput1.foo{
2color:#777777;
3}
MixinsinStylus
ThesyntaxforStylusmixinsisalittledifferentfromtheotherpreprocessors,butifyou’vebeenreadingthischapter,thefollowingexampleshouldbefairlyeasytograspataglance:
Stylus1dashed_border_mixin(padding,border_width=1px,border_color=#000000)
2border:border_widthdashedborder_color
3padding:padding
4
5.foo
6dashed_border_mixin(5px)
CSSOutput1.foo{
2border:1pxdashed#000;
3}
InStylus,argumentsareseparatedbycommas,defaultvaluesaresetwith=,andforthemostpart,mixinslookthesameasvariables.
Extending
Stylussharesit’sextendfunctionalitywithSassinbothsyntaxandbehavior:
Stylus1.foo
2color:green;
3
4.bar
6background:blue
CSSOutput1.foo,
2.bar{
3color:#008000;
4}
5
6.bar{
7background:blue;
8}
Summary
TheStyluslanguagecanmakeyourownpersonalpreferencesasyntacticreality.Youcanreallywriteyourstylesnearlyanywayyouplease.Thislanguagealsosportsaheftynumberoffeaturesthattheotherpreprocessorswe’vecovereddo.Althoughtheauthorshaven’tusedStylusexclusivelyinanyclientprojects,youmightfindsomeofitsflexibilityveryattractive.IfStylusissomethingyou’dliketotryforyourself,youcanfindmoreinformationontheStyluswebsite.
PerformanceandCSSWhenyou’retryingtoimproveyourpage’sperformanceyoulikelydon’tthinkofCSSasaprimaryconcern.Inthegrandschemeofthings,improvingyourCSS’performancewon’tprovideasmanyquickgainsasreducingthenumberofimagerequestsoroptimizingyourJavaScriptwill.Thatsaid,therearestillsomethingsyoucandotomakebrowsersrenderyourCSSmoreefficiently.
Likely,yourlargestCSSperformancegainswillcomefromoptimizinghowyourstylesgetloadedontothepage.InliningcriticalCSSwillavoidtheneedtofetchastylesheetentirely.CompressingandminifyingyourCSSwithsomethinglikecssminwilldecreasethefilesizeofyourstylesheetsandlowerthetimeittakesforthebrowsertodownloadandparsethatstylesheet.Optimizingthewayyourserveractuallyservesyourstylesheetswillprovideasimilargain,butcachingandserverconfigurationareoutsidethescopeofthissection.
CSSPaintingAnotherperformanceoptimizationcancomefromimprovingthetimeittakesforabrowsertoactuallypaintallofyourstylesontothescreen.Themorethingsyouhaveonthescreen,themoreworkyourbrowserhastodoto.Thisisaprettytrickytopicand,formostwebsites,gettingdownanddirtywithvisualpainttimesisprobablynotnecessary,butit’sgoodtoknowabouthowitallworks.
AsthebrowserbuildstheDOMtree,itconcurrentlystartsbuildinguparendertreewhichismadeupofallthevisualstuffthat’sgoingtobepaintedontothescreen.Thisishowthebrowserknowsinwhatordertoapplyyourstylesonthescreen.Eachvisualelementbecomesanodeontherendertreecontainingallofthestylesthatmakeitup.Thesestylescomefrominlinestyles,thestylesheetsappliedtothepageand,insomecases,localstylesheetsthatusershaveaddedtocustomizetheirbrowsingexperience.Browserenginesallhandlethisprocessalittledifferently,buttheconceptisprettymuchthesame.
Thebrowserwilltypicallystartatthetopofadocumentandevaluatethestyleofthefirstelementitsees,usuallybody,beforepaintingit.Atthispointthebrowserdoesn’tcareaboutanythingelseinthedocument.Onceitfinishespaintingbodytothescreen,it’llmoveontothenextelementandgothroughthesameprocessofevaluatingthestyleand
paintingittothescreen.Afterthischildelementhasbeenpainted,thebrowserthentriestofigureoutifitneedstorepainttheparentelement.Ifthechildelementchangedtheheightofitsparent,bodywillbereevaluatedandpaintedoncemore.
Thenodesoftherendertreeallgetcalculatedindividuallyforlayoutandstyleandthat’sthegistofhowCSSactuallygetsapplied.Theactualpaintingpartofthatprocessfollowsastrictorderdefinedbyanumberofconditions.Theseconditionsarethingslikewhetherornottheelementbeingstyledisarootelement,whethertheelementispositionedinlineorblock,orwhetherit’sachildofanotherelement.Thegeneralorderofpaintinglookslikethis:
backgroundcolorbackgroundimageborderanychildelements
backgroundcolorbackgroundimageborder
YoucanfindalotmoreinformationaboutthisintheCSSspecification.
Sowhatcanwedowiththisinformation?Nowthatwe’reawareofhowthebrowserputsallourstylesintopractice,wecanfocusontryingtolimittheamountofoverallpaintingourbrowsershavetodobyapplyingstylesfurtherupthecascade,removingunnecessaryelementsfromthepage,andtrackingdownstylesthattakealongtimetopaint.ChromeactuallyhasapaintprofilerintheTimelinesectionofitsDevToolsthatmakesitprettyeasytoseeeachpainteventandhowlongittooktohappen.Withthisinformationyoucanquicklyseethestylesthattakethelongesttimeforthebrowsertorender.Moreoftenthannot,thingslikeCSSshapesandgradientswillbeyourprimaryculprits.
CSSSelectorsTheselectorsyouwritetostyleyourelementsgetevaluatedfromrighttoleft.Themorespecificyourselectorsarethemoreworkthebrowserneedstodotofigureoutwhetherornotanelementonthepageneedstobestyled.IfwehadsomeCSSlikethis,thebrowserwouldgothroughourentiredocumentlookingfor<span>elements.Ateach<span>itwouldlooktoseeiftherewasa<p>parentelement,thenforeachofthoseitwouldlookfora<div>parentandsoon:1.foodivpspan{
2color:green;
3}
Thiscantakeaverylongtime(comparatively)becausetherendertreecangetprettymassiveandtherecouldbealotoffalsematches.Ifonlyone<span>inourdocumentexistsinsidea<p>andthereare50<span>sthenourbrowserhasdonealotofneedlessworkbecauseithadtoruleout49ofthem.BEM,introducedearlierinthischapter,avoidsthisproblementirelysinceeveryelementtypicallyonlyhasoneclassappliedtoit.
It’sworthwhilenotingthatoptimizingthispartofaCSSdocumentisprobablynotgoingtogiveyoumuchbangforyourbuck.Thesedays,browsersareprettyexcellentatevaluatingselectors,andworryingtoomuchabouttheefficiencyofyourselectorsinrelationtopageperformanceisprobablyagoodrecipeforinsanity.Knowingabouthowtheselectorswork,however,isimportantknowledgetohavetuckedaway.
SummaryInthissectionwelearnedalittlebitabouthowbrowsersworktoactuallyrenderyourstylesonthepage.It’ssomeprettyinterestingstuff.There’salotmoreyoucandotooptimizewebpages,butasmentionedatthebeginningofthissection,yourbiggestperformancegainswillalmostcertainlybetheoptimizationofyourCSS’deliverytothebrowser.Chasingpainttimesandselectorefficiencymightresultinsomeleanperformancegains,butasourmachinesandourbrowsersimprove,thesebecomelessandlessimportantinfront-enddevelopment.
Ifyouwanttolearnmoreaboutmakingyourpagesrenderefficiently,it’shighlyrecommendedthatyoureadthroughGoogle’sPageSpeedInsights.Thereyou’llfindallkindsofgreattipsforoptimizingimages,prioritizingrenderablecontent,andmuchmore.
StylingSummaryInthischapterwe’vealreadycoveredalotoftopicsandconceptsthatmighthelpyoubetterunderstandthecurrentlandscapeofstylingthefront-end.Thiswaspossiblyoneofthemostdifficultchapterstowritebecausethislandscapeisevolvingalmostdaily,butifyouhaveanyquestionsorwouldjustliketotalkaboutbestpracticestheGoogleGroupforthisbookisagoodplacetostart.
DependencyManagement
Thereisanentireuniverseoffront-enddevelopmentframeworks,pluginsandtoolsavailableforbuildingapplications.Theopensourcemovementbringswithitahugearsenalforbuildingamazingapplications.Byusingsomeoftheseavailable‘packages’,weareabletodevelopfasterandsafer.
Butyou’llquicklycometorealizethatthebiggestproblemnowisbeingabletoefficientlymanageandkeepuptodateallthepackagesthatyouuse.‘Dependencyhell’isatermyoumayhearfront-enddevelopersuse.Itbecomesevenmoredifficultwhenworkingaspartofateam,asit’scrucialthatalldevelopersonaprojectusethesamepackageversionstoavoidconflictsorbugs.
Donotfear!‘Packagemanagers’areheretosavetheday.Inthischapteryou’llbeintroducedtoseveralessentialpackagemanagersthatarehighlyrecommendedforincorporatingintoyourworkflows.
WhatisaPackageManager?Alldeveloperscreatesoftwaredifferently.Becauseofthis,installing,upgradinganddeletingsoftwarecanbecomeachore.Thatiswherepackagemanagerscomeintoplay.Apackagemanager,itselfapieceofsoftware,keepstrackofwhatisinstalledandallowsyoutoautomatetheprocessofmanagingsoftwareusingastandardAPI.Byadheringtothestandardssetoutbyapackagemanager,softwaredeveloperscanoffertheirpackagestoabroaderaudience.
Node.jsandtheNodePackageManager(npm)ThereareahugenumberofNode.jspackagesavailableforfront-enddevelopmenttasks.Manyofthepopularonesareintroducedinthisbook,suchasGrunt.Forthosewhodon’tknowwhatNode.jsis,here’saquotefromtheirhomepage:“Node.jsisaplatformbuiltonChrome’sJavaScriptruntimeforeasilybuildingfast,scalablenetworkapplications.”Whilewe’renotgoingtobebuildingourownNode.jspackages,wewillneedtoinstallNode.jsitselfsothatwecanuseNodepackages.
TheNodePackageManager(npm)wasnotincludedaspartofNode.jsoriginallybutsinceversion0.6.3itisautomaticallyinstalledforus.npmallowsustoinstallandmanagealloftheNode.jspackagesthatareusedinanapplication.Node.jsisahugeopensourcecommunity.Youcanbrowsethedirectoryofavailablepackagesattheirhomepage,https://www.npmjs.org/.Atthetimeofwritingtherewereover70,000packagesrackingup10milliondownloadsaday.It’sobviousthatnpmisaverypopularresource.
InstallingNode.js
Let’sgetNode.jsinstalled.Ifyou’reonOSXandusehomebrew(anotherPackageManagerforOSX)thenallyouneedtorunisbrewinstallnodeinthecommandline.
Foreveryoneelse,youcangototheNodehomepageanddownloadthepre-builtbinaryinstaller.Alternatively,youcanclonetheirGitrepositoryandbuildfromthere.Runningthefollowingcommandsshouldgetthejobdone:1gitclonehttps://github.com/joyent/node.git
2cdnode
3./configure
4make
5sudomakeinstall
Theremaybesomedependencyrequirementsandifyou’dliketofindoutmoreoninstallingonyourspecificOS,checkouttheirdocumentation.
Wecanconfirmwe’veinstalledNodecorrectlybyrunningthefollowinginthecommandline:1node--version
Aspreviouslymentioned,npmisbundledwithNode.Toconfirmnpmisinstalled,run:1npm--version
Thepackage.jsonWe’regoingtoneedtocreateafileinourprojectdirectorynamedpackage.jsonthatwillcontainmetadatarelevanttoourapplication.Thisfilewilldefinebasicprojectinformationand,mostimportantly,thepackagedependencies,(i.e.referencestotheNodepackageswerequireforourapplication).Thisfileisimportantbecauseitwillallowbothourselvesandotherdeveloperstoquicklyinstallthedependencieswithasinglecommand,npminstall.Althoughit’spossibletogetawaywithouthavingthisfile,oneofthemostimportantissuesitsolvesisconflicts.IfadeveloperhasadifferentversionofaNodepackagethatisusedinyourapplicationinstalledglobally,thenitmayconflictwithwhicheveroneyouactuallywantthemtouse.
Theeasiestwaytounderstandthepurposeofthisfileistojumpinandcreateit.Let’screateanexampleprojectdirectoryandpackage.jsonfile:1mkdirmyApp
2cdmyApp
3touchpackage.json
Usingyourfavoritetexteditor,openpackage.jsonandaddthefollowing:1{
2"name":"myApp",
3"description":"Anall-roundamazingawesomeapp.",
4"version":"0.0.1",
5"devDependencies":{}
6}
Asthefilenamesuggests,thisfilecontainsJSON.Sofarwe’vedefinedthenameofourproject,itsdescriptionanditsversionusingthestructuremajor.minor.patch.(Formoreonsemanticversioning,checkouthttp://semver.org/).Thisbasicprojectmetadataisrequiredinpackage.jsonfiles.We’vealsodefineddevDependenciesasanemptyobject.
Thispropertywillbewherewelistthepackagedependenciesforourproject.(Incidentally,youcanautomatethecreationofyourpackage.jsonfilebyusingthecommandnpminitbutit’sgoodtoknowhowtowritethefileyourself!)
InstallingpackagesThisisprobablywhyyouhavenpminthefirstplace.Youwanttoinstallsomething,right?It’sveryeasy.Afteryoufindthenameofthepackageyouwanttoinstall,navigatetoyourprojectfolderandrun:1npminstall<packagename>
Thiswillinstallthepackagetoafoldercallednode_moduleswithintheprojectsdirectory.But,ifyouopenpackage.json,youwillnoticenothinghasbeenaddedtothedevDependenciesobject.It’simportantthatweaddareferencetothepackagetheresothatthenexttimeweoranotherdeveloperwanttosetuptheapplicationfordevelopment,thereferencedpackagesareeasilyinstallable.
Althoughwecouldeditourpackage.jsonmanually,it’sbesttoappendthe--save-devparameterwheninstallingtoautomatetheprocess.Let’strythiswithonethemostpopularnpmpackagesasanexample,express:1npminstallexpress--save-dev
Thisinstallsthepackageonoursystem(inthenode_modulesdirectory)butalsosavesareferencetotheversionwe’veinstalledinpackage.json.Openitupandyou’llseesomethinglikethis:1{
2"name":"myApp",
3"description":"Anall-roundamazingawesomeapp.",
4"version":"0.0.1",
5"devDependencies":{
6"express":"^4.8.7"
7}
8}
Afteryou’veinstalledallofthepackagesthatyouneedthisway,allthatanewdeveloperhastodotomakesuretheyhavethecorrectversionofeverythinginstalledisnavigatetotheprojectdirectoryandrun:1npminstall
Prettycool,huh?
Anotherthingworthmentioningisthatyoucanalsoinstallpackagesglobally.Bydefault,npminstallwillplacepackagesinthenode_modulesfolderofyourcurrentworkingdirectory.Installinggloballywillplacethemodulesoutsideofthecurrentdirectoryandinsteadplacetheminyourglobalfilesystem,(e.g.,/usr/local/lib/node_modulesonOSX).Toinstallapackageglobally,youcanapplythe-gparameter:1npminstall<packagename>-g
Updatingpackages
Probablythebestpartofapackagemanagerishowcrazyeasyitistoupdatestuff.Becausewhoreallylovesspendingtimeonmaintenance?Thenpmupdatecommandsearchestheregistryfornewerversionsofinstalledpackagesandupdatesthemautomatically.Italsopointsdependentpackagesatthenewversionandremovestheolderversions.Wewanttomakesurethatpackageversionnumbersareupdatedcorrectlysowemustapplythe--save-devparameteragain:1npmupdate--save-dev
Alternatively,wecanupdatejustasinglepackageatatime.Ifwehavealargenumberofpackagesandupdatethemallatonce,itmightbedifficulttodebugtheapplicationifsomethingbreaks.Toupdateaspecificpackagerun:1npmupdate<packagename>--save-dev
ListingpackagesSimplylookinginsidethenode_modulesfolderisnotgoingtotellyoueverypackagethatisinstalled.Installedpackagesmayalsodependonotherpackagesandsotheyeachmayhavetheirownnode_modulesfoldertoo.
Thenpmlistcommandwillgiveyouanicelyformattedhierarchicaloverviewofwhatisinstalledwithversionnumbers.Thecommandalsoletsyoufiltertheresults(i.e.grep)byappendingthepackagename:1npmlist<packagename>
UninstallingpackagesYoucanuninstallpackageseasilyusingthenpmuninstallcommand.You’llneedtoappendthepackagenameand--save-devsothatthepackage.jsonisupdated:1npmuninstall<packagename>--save-dev
GettinghelpYoucanappendthe--helpparametertoanycommandforsomequicksyntaxinformation.Alternatively,prependhelptogetamoredetaileddescription.Forexample,try:1npminstall--help
2npmhelpinstall
BowerWe’veestablishedthatnpmmanagesNode.jspackages.Bower,builtbyTwitter,isapackagemanagerthatisactuallyaNodepackageitself.Boweroffersasolutiontofront-endpackagemanagement.You’llfindmanyofthepopularCSSandJavascriptframeworks,librariesandpluginsareavailablethroughBower.VisittheirSearchpageforalistofalltheavailablepackages.Inasimilarfashiontonpm,Bowerallowsustokeepreferencestowhichpackagesareusedinourapp.
InstallingBower
Let’sinstallBowerusingnpm:1npminstallbower-g
Noticethe-gflagwhichindicatesthatwewanttoinstallthispackageglobally.Whatthismeansisthatwewillbeabletousethepackageregardlessofthedirectorythatwearein.Weinstallthispackagegloballybecausewe’renotverybotheredwithmaintainingitsversionacrossenvironmentsandwe’dratherbeabletouseitfromthecommandlinefromnewprojectdirectorieswithouthavingtoinstallitagaineachtime.
Thenextstepistocreateafilenamedbower.jsonwhichservesasimilarpurposeaspackage.jsondoesfornpm.Thistime,let’suseahelperfunctiontogetstarted:1bowerinit
Thiswillpromptuswithasetofquestionstowhichtheanswersareusedtocreateourbower.jsonfile.Herearethesettingsusedforourexample:1[?]name:myApp
2[?]version:0.0.1
3[?]description:
4[?]mainfile:
5[?]whattypesofmodulesdoesthispackageexpose?
6[?]keywords:
7[?]authors:JoeFender<[email protected]>
8[?]license:MIT
9[?]homepage:
10[?]setcurrentlyinstalledcomponentsasdependencies?No
11[?]addcommonlyignoredfilestoignorelist?No
12[?]wouldyouliketomarkthispackageasprivatewhichprevents
13 itfrombeingaccidentallypublishedtotheregistry?Yes
Theresultingbower.jsonfileshouldthenlooksomethinglikethis:1{
2name:'myApp',
3version:'0.0.0',
4authors:[
5'JoeFender<[email protected]>'
6],
7license:'MIT',
8private:true
9}
Bydefault,anypackagesinstalledwithBowerwillbeplacedinafoldercalledbower_components.Ifwewanttochangethenameofthisfolder,wecandosobycreatingafilecalled.bowerrcintheapplication’srootdirectoryandaddingthenewdirectoryname.Forexample:1{
2"directory":"vendor"
3}
SearchingpackagesSomethingneatthatBowerdoesisallowustosearchforpackagesfromthecommandline.Simplyrun:1bowersearch<keyword>
Thiswillreturnpackagesthatcontainthegivenkeyword.YoucanalsoomitthekeywordtogettheentirelistofavailableBowerpackages.
InstallingpackagesJustlikewithnpm,wecaninstallpackagesusingthiscommand:1bowerinstall<packagename>--save
Noticeweuse--saveasopposedto--save-devusedinnpm.Theybothdothesamejobofensuringthatourbower.jsonfileisupdatedwithareferencetotheinstalledpackageanditsversion.Bowerdoesinfactletyouuse--save-dev.Thedifferencebetweenthetwocommandsisthatthepackageswillbereferencedinadifferentobjectwithinbower.json.Whenusingthe--saveparameter,packagesarereferencedinthedependenciesobjectwhereasthe--save-devparameteraddsthemtothedevDependenciesobject.MostoftheNodepackagesinstalledarefordevelopmentpurposes,suchastaskrunning,whichwewilldiveintointhenextchapter.Thatiswhyweuse--save-devinnpm.However,themajorityofthepackagesinstalledwithBowerwillbeserveddirectlytotheclientsofourapplications.Forexample,someCSSfilesoraJSplugin.Youcandefinitelyuseboth--saveand--save-devtoseparatereferencestoinstalledNodeorBowerpackages.It’stotallyuptoyouwhichcombinationsyouuse.
ListingpackagesListinglocalpackagesandavailableupdatesissimple:1bowerlist
UpdatingpackagesToupdatepackagestotheirlatestversion,run:1bowerupdate<packagename>--save
Aswithnpm,thepackagenameisoptionalandifomittedthenallinstalledpackageswillbeupdated.
UninstallpackagesAgain,thisoneisfairlystraightforward:1boweruninstall<packagename>--save
BundlerAnotherveryusefulpieceofthepackagemanagementpuzzleisBundler.BundlerhelpsyoutomanageRubyproject‘gems’.GemsareprogramsandlibrarieswrittenintheRubyprogramminglanguage.Thereareafewusefulgemsforfront-enddevelopmentthatweintroduceinthisbook,soit’srecommendedthatyouinstallBundlertomanagethem.
DependingonyourOSyoumayneedtoinstallRubyfirst.TodosoyoushouldfollowthedocumentationontheRubyhomepage.
NowthatRubyissetup,let’sgetBundlerinstalledfromourcommandline:
1geminstallbundler
ThisinstallstheBundlergemgloballyonthesystem.Ashasbeenthecasewithourotherpackagemanagers,Bundlerrequiresafileinourprojectsrootdirectorythatwillstoreprojectdependencies.Thefile’snameisGemfileandunlikenpmandBower,hasitsownsyntaxasopposedtousingJSON.Let’screatethefileGemfileandaddasinglelinetoitinourtexteditor:1source"https://rubygems.org"
ThistellsBundlerwheretolookfortheRubygemsthatwewanttoinstall.Alternatively,wecouldrunbundlerinittohaveBundlercreateGemfileforus.
InstallinggemsUnlikenpmandBower,thereisn’tacommandtoinstallgempackages.WeneedtomanuallyedittheGemfileandwritethereferences.Forexample,saythatwewanttoinstallSass.WewouldeditourGemfilesothatitlookedsomethinglike:1source"https://rubygems.org"
2gem'sass','~>3.3.6'
Thentoinstallthepackage,wegototheproject’srootdirectoryandrun:1bundleinstall
Noticethatweusebundleandnotbundler.Thatisnotatypo!Ifyouneedhelpfiguringouthowtowritetheversionnumber,checkouttheGemfilehelppage.Also,it’sworthnotingthatgemsarenotinstalledwithinalocalfolder.Instead,theyareinstalledglobally.
UpdatinggemsThereissomedisagreementinthecommunityaboutwhatpartBundlerplaysinupdatinggems.YoumightfinditeasiesttojustmanuallyeditGemfile,changetheversionnumberandthenrunbundleinstallagain.FormoreinformationonthereasoningbehindsomeofthefunctionalitydecisionsmadeinBundler,checkouttheirPurposeandRationalepage.
UninstallinggemsTouninstallagem,youwillfirstneedtorun:1gemuninstall<gemname>
Then,edityourGemfileandremovethereferencetothegem.
DocumentationSoftwareisonlyasgoodasitsdocumentation.Withoutcleardocumentationofyourprojectyouarelikelytorunintofrustrationswhendeveloping,maintainingortransferringknowledgetonewdevelopers.It’salwaysgoodtostartdocumentingprojectsimmediatelysothatyoudon’thavetoplaycatch-up.Ideally,youshouldcreatedocumentationassoonasyouhavesetupyourpackagemanagers.
StartbycreatingaREADMEfileintheproject’srootdirectorythatwillexplaintheprocessrequiredtoinstallthepackagemanagers.Aswithalargenumberofprojectsthesedays,wemaywelluseGitHubtomaintainourcoderepository.IfyoudouseGitHub,orthinkyoumightinfuture,youcancreateaREADME.mdfile.The.mdfileextensionstandsforMarkdownandallowsyoutowriteformattedtextfileswithshorthandsyntax.Let’screateREADME.mdandaddsomethingalongtheselines:1#GettingStarted
2
3Beforeworkingonthisapplicationwe'llwanttomakesurewehaveall
4therequireddependencies.Hereisaquickguidetogetstarted.
5
61.InstallNodewith`brewinstallnode`
72.InstallBowergloballywith`npminstallbower-g`
83.InstalllocalNodepackageswith`npminstall`
94.InstallBowerpackageswith`bowerinstall`
105.InstallBundlerwith`geminstallbundler`
116.InstallrequiredBundlergemswith`bundleinstall`
Youcouldcreateascriptthatwouldrunthesesixcommandsbehindthescenesfortheuserbut,aswitheverythinginthisbook,usingthetoolsdirectlygivesyouabetterunderstandingofhowtheywork.Ultimately,thiswillgiveyoumoreconfidencewhenintegratingthemintoyourownprojects.
SummaryInthischapter,welookedatthebasicsofthreeofthemostpopularfront-enddeveloperpackagemanagers:npm,BowerandBundler.You’velearnthowtoinstallthemallandhowtomanagetheirpackages.Wefinishedupbywritingsomedocumentationoftheinstallationprocess.
Packagemanagersareanextremelyimportantpartoffront-enddevelopmentthesedays.Usingtheminallofyourprojectsishighlyrecommended,regardlessofprojectsize,sothatyouareabletomaintainandscalethemefficiently.
Automation
Timeisakeyfactorinproductivity.Thischapterwillexploresomewaystoautomateyourdevelopmentworkflowsoyouspendlesstimeworkingandmoretimemakingawesome.You’llbeintroducedto‘taskrunners’whichhelpautomaterepetitivetasks.You’llalsodiscoverwaystospeeduptheprocessoflayingfoundationsfornewprojects,andyou’llpickupafewothertipstohelpyoubeamoreefficientdeveloper.
TaskRunnersTaskrunners,alsoknownas‘taskautomationframeworks’,helpyoumanageandexecutesetsofdefinedtasks.Ataskcanbeseenasanyrepetitiveactionyouhavetotakewhendevelopingfortheweb.TaskscanincludeactionssuchascopyingafilebetweendirectoriesorcheckingyourJavaScriptforsyntaxerrors.
Withataskrunner,youcanbuild,previewandtestyourapplicationmuchmoreeasilythanifyoudidalltheworkyourself.Thestrongestattributeoftaskrunnersistheirflexibility,whichmeansyoucanconfigureandrunpre-definedtasksexactlyhowyouwant.Furthermore,ifyoucan’tfindapre-definedtaskthatmeetsyourneeds,youcanevenwriteyourown.
Inthischapteryou’lllearnabitaboutthepopulartaskrunnersGrunt,Gulp,CodeKitandYeoman,includinghowtoinstallthemandhowtoconfigurethemtomeetyourspecificneeds.
Grunt
Gruntisoneofafront-enddeveloper’sbestfriendsandisthemostpopularandwidelyusedtaskrunner.ItisaframeworkbasedonJavaScriptandhasathrivingecosystem.Inaddition,thereareahugenumberofpluginsavailableforGrunt.Ifyou’renotsurewheretostartwithtaskrunners,thenstarthere.
InstallingGrunt
Let’sworkthroughtheinstallationtogether.We’llinstallGruntusingnpmfromthecommandline.(Ifyouhaven’tdonesoalready,pleasereadtheDependencyManagementchapterfirsttolearnmoreabouthowtosetupandusenpm.)
Thefirsttoolwe’regoingtoinstallisGrunt’scommandlineinterface,grunt-cli.It’sworthnotingthatthisdoesnotactuallyinstallGruntitself.ItonlyprovidesusglobalaccesstothegruntkeywordfromthecommandlinewhichisthenusedtoloadandruntheversionofGruntthathasbeeninstalledlocallywithintheproject.Itisimportanttoremembertoinstallgrunt-cligloballybutgruntlocally.Formoreinformationonglobalvslocalinstallation,checkoutthisarticleoverattheofficialNodeJSblog.
We’llheadovertoourcommandlineandrun:1npminstall-ggrunt-cli
ToverifythatGruntCLIwasinstalledsuccessfullyandthatwecanaccessthegruntcommandwe’llrunthefollowing:1grunt--version
Nowlet’sinstallGrunt.We’llneedtonavigatetoourproject’srootdirectoryfirst:1npminstallgrunt--save-dev
ThiswillinstallGruntlocallyandsaveadependencyreferencetoitfromwithinthepackage.jsonfile.
TheGruntfileNowthatwe’veinstalledGrunt,it’stimetointroduceGruntfile.js.Likepackage.jsonfornpm,theGruntfilebelongsintherootdirectoryofourproject.ItdefinesourprojectandtaskconfigurationsandtellsGruntwhichpluginswewanttousetorunthesetasks.We’renotgoingtodefineanytasksjustyetbutlet’screatethebarebonesoftheGruntfilesothatwecanaddtoitlater.CreateGruntfile.jsintheproject’srootdirectoryandaddthefollowing:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig();
4
5//Defaulttasks.
6grunt.registerTask('default',[]);
7};
You’llnoticethatthisfile,asthefilenamesuggests,isaJavaScriptfile.Allwe’vedonehereiscalledinitConfigwhichinitializesourGruntconfiguration.Thisiswherewewilldefineourtaskslater.We’vealsocalledregisterTasktoinitializeasetoftaskslabelleddefault.TherearenoactualtaskshereyetbutGruntrequiresustodefinethisforittorun.AftersavingGruntfile.jswecantryrunninggruntinthecommandlinefromtheprojectroot.Weshouldgetashinygreenmessagestating:1Done,withouterrors.
ThismeansGruntwasabletorunallofthetasksdefinedindefaultwithoutfailure.
Yay!We’vejustrunGruntforthefirsttime.ThemoreyouuseGrunt,themoreyou’llloveit!
PopularPluginsTherearethousandsofpluginsavailableintheopensourcecommunityforGrunt.Butthereareafewthathavebecomestaplesforthemodernfront-enddeveloper.Dependingontheproject,youmaynotneedallofthese,butitdoesn’thurttoknowaboutthem.Obviously,theseareonlyaselectfewandmaynotincludeeveryone’sfavorites.
Watch,LiveReloadandConnect
QuitepossiblythemostpopularpluginisWatch.Itallowsyoutorunspecifiedtaskswheneverawatchedfilechanges.So,forexample,youcouldminimizeyourCSSfileswheneveroneofthemchangesorrunJavaScripttestswheneveryousavescriptchanges.TheWatchpluginonlydoesthe‘watching’part.Youhavetospecifyexactlywhichtasksaretoberunwhenafilechangeisdetected.
ToinstallWatch,we’llrunthefollowingfromthecommandline:1npminstallgrunt-contrib-watch--save-dev
EdittheGruntfile.jssothattheplugingetsloaded:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig();
4
5//LoadourGruntplugins.
6grunt.loadNpmTasks('grunt-contrib-watch');
7
8//Defaulttasks.
9grunt.registerTask('default',[]);
10};
AllwehavedonehereisusetheloadNpmTaskscallbacktotellGrunttoloadtheWatchplugin.
TodemonstratehowtouseWatch,let’ssetupsomethingcoolusingLiveReloadandConnect.We’llsetupalocalwebserverthatwillautomaticallyrefreshanybrowserviewingourwebsitewhenafilechangeoccurs.Thisisgreatbecauseitmeanswedon’thavetorefresheverytimeachangeismadetooneofthefiles.TheofficialhomepageforLiveReloaddescribesthetoolas“Ahappylandwherebrowsersdon’tneedarefreshbutton”!.
Usually,whensettingupalocalwebserveryouhavetoconfiguresoftwaresystem-wide.YoumayhaveusedtoolssuchasMAMPoryoumayhaveconfiguredalocalLAMPstackyourself.ButagoodalternativeistouseamorelightweightapproachtotestingwebapplicationsusingacombinationofWatch,ConnectandLiveReloadplugins.
ConnectisanHTTPwebserverframeworkbuiltforNodeJS.It’salightweightstaticwebserverthatprovidespluginsknownas‘middleware’.Connectcomeswithabunchofmiddlewaresuchassessionsupport,acookieparserandmore.
ThereisaGruntpluginforConnectwhichallowsustostartaConnectwebserversolet’sgetthatinstalled:1npminstallgrunt-contrib-connect—save-dev
WenowneedtotellGruntwhenandhowtousetheplugin.EverypluginyouinstallshouldhaveaREADMEfileavailableinitsrootdirectory(inthiscase,node_modules/grunt-contrib-connect/README.md)whichwilloutlinehowtoconfigurethetask.Let’seditourGruntfile.jsagaintosetuptheconnecttask:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4 //DefinesourConnectwebserver.
5connect:{
6server:{
7options:{
8port:9000,
9hostname:'localhost',
10base:['dist/']
11},
12},
13},
14});
15
16//LoadourGruntplugins.
17grunt.loadNpmTasks('grunt-contrib-watch');
18grunt.loadNpmTasks('grunt-contrib-connect');
19
20//Defaulttasks.
21grunt.registerTask('default',[]);
22
23//Running`gruntserver`runsourConnectserveruntilterminated.
24grunt.registerTask('server',['connect:server:keepalive']);
25};
Here,wetoldGrunttoloadthepluginusingloadNpmTasksexactlyaswedidwithWatch.TaskconfigurationsareaddedwithintheinitConfigcallback.We’vedefinedataskcalledconnectthathasasub-taskcalledservercontainingthesettingsforthetask.We’vedefinedourConnectservertorunonport9000athttp://localhost:9000andtoservethefolderdist(shortfordistribution).
Asdescribedinthecodecommentnearthebottom,runninggruntserverfromthecommandlinewilllaunchtheConnectserveruntilterminated(madepossiblethroughthekeepaliveparameter).Youwillthenbeabletoaccessthewebapplicationbypointingyourbrowsertohttp://localhost:9000.However,thereiscurrentlynodistfoldersolet’smoveontocreatingthatandanexampleindex.html:1mkdirdist
2cddist
3touchindex.html
Openupindex.htmltoaddsomebasicHTMLsuchasthefollowing:1<!doctypehtml>
2<html>
3<head>
4<metacharset=“utf-8">
5<title>MyApp</title>
6</head>
7<body>
8<p>Helloworld!Welcometomyapplication.</p>
9</body>
10</html>
Now,ifyougotothecommandline,rungruntserverandthenpointyourbrowsertohttp://localhost:9000youshouldseethe“Helloworld”message.
Tofinish,weneedtoconfiguretheWatchtasksothatwhenanychangeoccursinindex.html,abrowserrefreshistriggered.Let’supdateourGruntfile.jstolooksomethinglikethis:1//Projectconfiguration.
2grunt.initConfig({
3//DefinesourConnectwebserver.
4connect:{
5server:{
6options:{
7port:9000,
8hostname:'localhost',
9base:['dist/'],
10livereload:true
11},
12},
13},
14
15//Watchourfilesforchanges.
16watch:{
17options:{
18livereload:true
19},
20html:{
21files:['dist/index.html'],
22tasks:[]
23}
24},
25});
26
27//LoadourGruntplugins.
28grunt.loadNpmTasks('grunt-contrib-watch');
29grunt.loadNpmTasks('grunt-contrib-connect');
30
31//Defaulttasks.
32grunt.registerTask('default',['watch']);
33
34//Running`gruntserver`runsourConnectserveruntilterminated.
35grunt.registerTask('server',['connect:server:keepalive']);
Thefirstthingyoumaynoticeiswe’veaddedourwatchtaskconfiguration.We’vesetupasub-taskcalledhtmlthatwatchesforchangestoindex.html.Wehaven’tactuallytoldWatchtoexecuteanyspecifictasksonceachangeisdetected.However,we’veenabledthelivereloadbooleanbothintheConnectandWatchoptions.ThisbasicallytellsConnectandWatchtouseLiveReloadintandem.Connectcomescompletewiththeconnect-livereloadmiddlewareNodepackageand,asofrecently,Watchnowcomeswith
LiveReloadserverfunctionalityincludedsoallweneedtodoissetthosetwobooleans.Lastly,weupdatedourdefaulttasksothatthewatchtaskisrun.
Totestthis,you’regoingtoneedtwoterminalwindows.Inthefirst,rungruntserverandinthesecondrungrunt.Youshouldseethefollowingstatusmessagesrespectively:1Running"connect:server:keepalive"(connect)task
2Waitingforever…
3Startedconnectwebserveronhttp://localhost:9000
And:1Running"watch"task
2Waiting…
Makesureyourbrowserislookingathttp://localhost:9000andthenmakeachangetothe“Helloworld”messageinyourindex.html.Don’tforgettosavethefile!Onceyousavethefile,switchbackovertoyourbrowserandyou’llseethatthepagehadrefreshed(probablybeforeyouevenmadeitbackover)andreflectsthechangeyoumade.Neathuh?
ThepowerofWatchreallyshineswhenyoustarttoconfigureitforlargerfront-endapplications.Formany,itbecomesthecenteroftheirautomationworkflow-thetriggertoeverytask.Itreallyisagreatplug-in.
CopyandConcat
Asprogrammers,weaimtodevelopourprojectstobeasmodularaspossible.Thisquicklyleadstoalotoffilesandoneofthebiggestchallengesiskeepingontopofthemall.CopyandConcatareacoupleofthehandiestfilemanagementpluginstoknowabout.
Copy
CopysimplyallowsyoutocopyfilesandfoldersfromGrunt.Copyhasendlessusesbutforthepurposesofdemonstrationlet’screateanexamplescenario.Let’ssaythatwewanttocopysrc/index.htmltodist/index.htmlaspartofourGruntbuildprocessbecausethedistfolderistheonethatistobeservedbyourwebserverandsrciswhereweactuallyeditourcode.Obviouslywitharealapplicationtherewouldlikelybemorefilestocopythanjustasinglefilebutlet’srollwiththisanyway.
Beforegettingstarted,don’tforgettoinstalltheplugin:1npminstallgrunt-contrib-copy--save-dev
ThisishowwemightgoaboutsettingupourGruntfile.jstoreflectthisscenario:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4copy:{
5html:{
6files:[
7{
8src:['src/index.html'],
9dest:'dist/index.html'
10}
11]
12}
13},
14});
15
16//LoadourGruntplugins.
17grunt.loadNpmTasks('grunt-contrib-copy');
18
19//Defaulttasks.
20grunt.registerTask('default',['copy:html']);
21};
We’vesetupasub-taskwithinourcopytaskconfigurationcalledhtmlwhichwillcopyindex.htmlforus.Thisallowsustoaddthecopy:htmltasktoourdefaulttasklist.SimplyenteringcopyasthetasknamewouldhavealsoworkedasthatwouldtriggerGrunttoexecuteallofitssub-tasks,butit’spreferabletoexplicitlycallthenameofthesub-taskyouwishtoexecutetopreventanyissuesinthefuturewhenyouexpandonyourconfigurations.
Totest,makesurethatthesrc/index.htmlfileexistsandthensimplyrungruntfromthecommandline.Youshouldseethefollowingsuccessmessage:1Running"copy:html"(copy)task
2Copied1files
3
4Done,withouterrors.
Tospecifymoreadvancedfilepaths,suchasusingwildcardsandmatchingonfileextensions,readtheofficialdocumentationonGlobbingPatternswithinGrunt.Copyalsohassomeusefuloptionssuchasencodingtouseaspecificfileencodingwhencopyingormodetospecifycopiedfilepermissions.
Concat
Concatletsyouconcatenatefiles.Concatenationistheactofjoiningthingstogether.Whenoptimizingyourapplicationsforperformance,lookingattechniquestoreducethenumberofrequestsisextremelyimportant.Byconcatenatingfilestogether,youareabletoreducethenumberofrequestsrequiredbyclientstogetatyourcode.
ToinstallConcat,we’llrunthefollowingfromourprojectfolder:1npminstallgrunt-contrib-concat--save-dev
Let’ssaywewanttoconcatenatealloftheJavaScriptfilesthatwehavewithinthefoldersrc/jsintoasinglefile.ThisishowwewouldgoaboutsettingupourGruntfile.js:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4 //ConcatenateallJavaScriptfiles.
5concat:{
6js:{
7src:['src/js/*.js'],
8dest:'dist/scripts.js'
9},
10},
11});
12
13//LoadourGruntplugins.
14grunt.loadNpmTasks('grunt-contrib-concat');
15
16//Defaulttasks.
17grunt.registerTask('default',['concat:js']);
18};
YoucantestthisbyaddingabunchofJavaScriptfilesintothesrc/jsfolderandthenrunninggrunt.YoumaynoticethatConcatcouldpossiblynullifytheneedforCopyasyoucanspecifythedestinationoftheconcatenatedfile.However,ifyouwantedtorunothertasksontheconcatenatedfilebeforemovingittothedistfolder,youmaywellstillneedCopy.
OneconfigurationoptionofConcatthatyoucanuseisbanner.Itallowsyoutosetastringheaderthatwillbeprependedtothebeginningoftheconcatenatedfile.Itisworthnotingthattheheaderstringisprocessedusinggrunt.template.process.
HereisanexampleofhowtosetupabannerinGruntfile.jsforatypicalapplication:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4pkg:grunt.file.readJSON('package.json'),
5
6//ThebannerisplacedattheheadofourconcatenatedJavaScriptfile.
7banner:'/*!\n*<%=pkg.title||pkg.name%>-v<%=pkg.version%>'+
8'\n*Copyright(c)<%=grunt.template.today("yyyy")%><%=pkg.autho\
9r%>'+
10'\n*<%=pkg.homepage?pkg.homepage:""%>'+
11'\n*/\n\n',
12
13//ConcatenateallJavaScriptfiles.
14concat:{
15options:{
16banner:'<%=banner%>'
17},
18js:{
19src:['src/js/*.js'],
20dest:'dist/scripts.js'
21},
22},
23});
24
25//LoadourGruntplugins.
26grunt.loadNpmTasks('grunt-contrib-concat');
27
28//Defaulttasks.
29grunt.registerTask('default',['concat:js']);
30};
Whatthisdoesisloadinthepackage.jsonJSONfileintoavariablecalledpkgwhichisthenusedtofilloutbanner.Dependingonhowyourpackage.jsonissetup,youshouldseesomethinglikethisatthetopoftheoutputtedscripts.jsfile:1/*!
2*myApp-v0.0.0
3*Copyright(c)2014JoeFender
4*http://www.myapp.com
5*/
LintingwithJSHintandCSSLint
Lintingistheprocessofanalyzingcodeforpotentialerrors.Intraditionalmethods,youwouldseeJavaScripterrorsintheconsoleifsomethingwentwrong.However,bylintingyourcode,youareabletogetfeedbackonyourcodingqualitydirectlywithinyourGruntbuildprocess.Mostlinterswillgiveyoumoreinformationaboutsyntaxerrorsthanyourtraditionalbrowserconsole.Lintingyourcodeishighlyrecommendedasitcansavealotoftimedebuggingsyntaxerrors.
JSHint
ForJavaScript,weusegrunt-contrib-jshint.ToinstallJSHint,we’llrunthisfromourprojectfolder:1npminstallgrunt-contrib-jshint--save-dev
ThisGruntpluginpassesthroughalloftheoptionsyousetinitsconfigurationtaskdirectlytoJSHint.ThatmeansyouwillwanttobereadingtheofficialJSHintdocumentationforalistofsupportedoptions.
HereisanexampleofhowwegoaboutsettingupourowntypicalJSHintconfigurationinGruntfile.js:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4pkg:grunt.file.readJSON('package.json'),
5
6//ConfiguresJavaScriptlinting.Seehttp://www.jshint.com/docs/options/
7//formoreontheavailableoptions.
8jshint:{
9js:{
10files:{
11src:['src/js/*.js']
12},
13options:{
14curly:true,
15immed:true,
16newcap:true,
17noarg:true,
18sub:true,
19boss:true,
20eqnull:true,
21strict:false,
22globalstrict:true,
23globals:{
24angular:false
25}
26}
27}
28}
29});
30
31//LoadourGruntplugins.
32grunt.loadNpmTasks('grunt-contrib-jshint');
33
34//Defaulttasks.
35grunt.registerTask('default',['jshint']);
36};
We’vetoldJSHinttocheckalloftheJavaScriptfilesinthesrc/jsfolderaspartofourdefaulttasklist.Totestthis,createanexampleJavaScriptfilethathasadeliberatesyntaxerror.Suchas:1varexample={
2bar:'bar',
3foo:'foo'
4}
Rungruntandyouwillseethefollowing:1Running"jshint:js"(jshint)task
2
3src/js/example.js
44|}
5^Missingsemicolon.
6
7>>1errorin1file
8Warning:Task"jshint:files"failed.Use--forcetocontinue.
9
10Abortedduetowarnings.
JSHintfoundthemissingsemicolonandgaveusaveryclearindicationofwherethesyntaxerrorwas.Becausetherewasanerror,itabortedtheGruntbuildprocess.
Ifyouhaveabuildprocessthattakesseveralsecondstocomplete,bringyourlintertaskasfartowardsthefrontofthelistasyoucansothatyou’llgetsnappierfeedbackaboutpossiblesyntaxerrors.Thatway,you’renotwastingGrunt’soryourowntimegoingthroughabunchoftasksthatyou’lljusthavetogothroughagainonceyoufixthesyntaxerror.
CSSLint
ForCSS,grunt-contrib-csslintisrecommended.AswithJSHint,viewtheCSSLintdocumentationforafulllistofconfigurationoptions.ToinstallCSSLintwe’llrunthiscommand:1npminstallgrunt-contrib-csslint--save-dev
HereisanexampleGruntfile.jstogetCSSLintworkingwithdefaultsettings:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4//ConfiguresCSSlinting.Seehttps://github.com/CSSLint/csslint
5//formoreontheavailableoptions.
6csslint:{
7css:{
8src:['src/css/*.css']
9}
10}
11});
12
13//LoadourGruntplugins.
14grunt.loadNpmTasks('grunt-contrib-csslint');
15
16//Defaulttasks.
17grunt.registerTask('default',['csslint']);
18};
TotestCSSLintcreateabrokencssfileinsrc/css-perhapssomethinglikethis:1.boo{
2height:100px;
3widths:100px;
4}
Afterrunninggruntyoushouldbegreetedwiththislovelywarningmessage:1Running"csslint:css"(csslint)task
2Lintingsrc/css/example.css…ERROR
3[L3:C3]
4WARNING:Unknownproperty'widths'.Propertiesshouldbeknown(listedinCSS3
5specification)orbeavendor-prefixedproperty.(known-properties)
6Browsers:All
7>>1filelintfree.
8
9Done,withouterrors.
ItisworthnotingthatunlikeJSLint,theGruntbuildprocesswillnotfailwhenthereisacodewarning.Thisseemstobeadesigndecision.
MinificationwithUglifyJSandCSSMin
Minificationistheprocessofremovingunnecessarycharactersfromcode(suchaswhitespaceandcomments)toreduceitsfilesize.TherearelotsofpluginsavailablethatofferdifferenttypesofminificationbutacoupleoffavoritesareUglifyJSandCSSMin.
UglifyJS
UglifyJSisaJavaScriptcompressorandminifier.ToinstallUglifyJS,we’llrunthiscommandfromourprojectroot:1npminstallgrunt-contrib-uglify--save-dev
WecansetuptheuglifytaskinourGruntfile.jswithonlyafewlines:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4//MinifiesourJavaScriptfiles.
5uglify:{
6js:{
7files:{
8'dist/scripts.min.js':['src/js/*.js']
9}
10}
11},
12});
13
14//LoadourGruntplugins.
15grunt.loadNpmTasks('grunt-contrib-uglify');
16
17//Defaulttasks.
18grunt.registerTask('default',['uglify']);
19};
Intheaboveexamplewe’veconfiguredouruglifytasktolookforallJavaScriptfilesinsrc/jsandcompressthemallintotheoutputteddist/scripts.min.js.Bydefault,UglifyJSwillnotonlyremovewhitespaceandcommentsbutwillalsotrytosimplifythe
codebygroupingtogethercodeofsimilarfunctionality(suchasvariabledeclarations)andbydeletingunusedfunctions.
CheckoutallofthetaskconfigurationoptionsavailableinthepluginREADME,especiallymangle.Bysettingthistotrue,UglifyJSreducesthenamesoflocalvariablesandfunctionstosingleletters.ThiscangreatlyminimizetheamountoftextusedintheJavaScriptfile,thusreducingtheoutputtedfilesize.
CSSMin
CSSMinisprobablythemostpopularCSSminifierGruntplugin.Itusesclean-csstoperformtheactualcompression.
ToinstallCSSMin,we’llrunthiscommandfromourprojectroot:1npminstallgrunt-contrib-cssmin--save-dev
HereisanexamplecssmintaskforourGruntfile.jsthatwilltakealloftheCSSfilesinsrc/cssandminifythemintoasinglefile,dist/styles.min.css:1module.exports=function(grunt){
2//Projectconfiguration.
3grunt.initConfig({
4//MinifiesourCSSfiles.
5cssmin:{
6css:{
7files:{
8'dist/styles.min.css':['src/css/*.css']
9}
10}
11},
12});
13
14//LoadourGruntplugins.
15grunt.loadNpmTasks('grunt-contrib-cssmin');
16
17//Defaulttasks.
18grunt.registerTask('default',['cssmin']);
19};
IfyoulookattheUglifyJSexamplewedidpreviously,you’llnoticethattheyareverysimilar.Yayforstandardization!
Gulp
Atthetimeofwritingthisbook,gulp.jsisthenewkidontheblock.JustlikeGrunt,Gulpisataskrunner.Theybasicallydothesamething.Someofthekeydifferencesare:
Gulpcallsitself‘Thestreamingbuildsystem’.ItharnessesNode’sstreamstogreatlyincreasebuildtimes.Formoreonstreams,checkoutthestreamhandbook.GulphasasimplerAPI.IthasyouwritingJavaScriptcodeinsteadofwritingconfigurationsinGrunt.Gulppluginsarewrittenforonespecifictaskonly,whereasGruntpluginscanoftendomultiplethings.YoucanfindasearchablelistofGulppluginshere.
InstallingGulpInstallingGulpisfairlysimpleusingnpm:1npminstallgulp-g
Let’sinstallGulpglobally.Next,navigatetoyourprojectrootfolder(orcreateoneifyouhaven’talready)andmakesureyouhaveapackage.jsonsetup.Don’tforget,youcandothisbyrunningnpminitorbyreferringtotheDependencyManagementchapterofthisbook.Then,runthefollowingcommand:1npminstallgulp--save-dev
ThiswillalsoinstallGulpbutthistimelocally.TheimportantpartisthatwemakeareferencetoitinthedevDependenciessectionofpackage.jsonbyusingthe--save-devparameter.
ExampleUsageTodemonstratehowtouseGulp,let’ssetupatasktouglifyJavaScriptfilesusingthegulp-uglifyplugin.We’llstartbyinstallingthepluign:1npminstallgulp-uglify--save-dev
JustaswithGrunt,Gulpalsohasasinglefilefordefiningtasks.We’llcreateafilecalledgulpfile.jsintherootofourprojectandusesomethinglikethefollowingsnippettogetstarted:1vargulp=require('gulp'),
2 uglify=require('gulp-uglify');
3
4//ThistaskuglifiesourJavaScriptfiles.
5gulp.task('compress',function(){
6gulp.src('src/js/*.js')
7.pipe(uglify())
8.pipe(gulp.dest('dist'))
9});
You’llwanttocreateafewdummyJavaScriptfilesinthesrc/jsfoldertotestwith.Fromthecommandline,rungulpcompresstorunthecompresstaskthatyoudefined.Youshouldthenseethefollowingstatusmessages:1[11:36:18]Usinggulpfile~/myApp/gulpfile.js
2[11:36:18]Starting'compress'...
3[11:36:18]Finished'compress'after8.87ms
Ifyoulookinthedistfolder,you’llnoticethateachoftheJavaScriptfileswecreatedhavebeenuglifiedbutunliketheGruntplugintheyhavenotbeenconcatenated.ThisisbecauseGulpmaintainstheidealthateachpluginshouldonlydoonething.Soifwewantedtoconcatenatefirstandthenuglifywewouldfirstlyneedtoinstallthegulp-concatplugin:1npminstallgulp-concat--save-dev
Then,wecouldupdateourgulpfile.jstolooksomethinglikethis:1vargulp=require('gulp'),
2uglify=require('gulp-uglify'),
3concat=require('gulp-concat');
4
5//ThistaskconcatenatesanduglifiesourJavaScriptfiles.
6gulp.task('compress',function(){
7gulp.src('src/js/*.js')
8.pipe(concat('scripts.js'))
9.pipe(gulp.dest('dist'))
10.pipe(uglify())
11.pipe(gulp.dest('dist'))
12});
Now,onceyourungulpcompress,youwillendupwithasingleconcatenatedanduglifiedfileatdist/scripts.js.
BeforemovingonfromGulp,weneedtoknowhowtosetupfilewatching.Gruntrequiredustodownloadaseparateplugintodothis,whereasGulpincludeswatchfunctionalityfromtheget-go.Let’ssaywewanttowatchforanychangestoourJavaScriptfilesandrunthecompresstaskoncethatoccurs.Wewouldneedtoalterourgulpfile.jsasshownbelow:1vargulp=require('gulp'),
2uglify=require('gulp-uglify'),
3concat=require('gulp-concat');
4
5//ThistaskconcatenatesanduglifiesourJavaScriptfiles.
6gulp.task('compress',function(){
7gulp.src('src/js/*.js')
8.pipe(concat('scripts.js'))
9.pipe(gulp.dest('dist'))
10.pipe(uglify())
11.pipe(gulp.dest('dist'))
12});
13
14//Watchourfilesforchanges.
15gulp.task('watch',function(){
16gulp.watch('src/js/*.js',['compress']);
17});
18
19//Thedefaulttaskisusedwhen`gulp`iscalledfromthecommandline.
20gulp.task('default',['compress','watch']);
We’vesetupanewtaskcalledwatchwhichisfairlyself-explanatory.Also,tosimplifythingsfurther,wehavedefinedanothernewtaskcalleddefaultwhichrunsourothertasks.Soallweneedtodonowishopovertothecommandlineandrungulp.Now,wheneverwemakeanychangestoourJavaScriptfiles,thecompresstaskwillrunautomagically.
Gulpissimpleandfast.OnceyoulearnitsAPI,theideaofcodeoverconfigisaverywelcomeone.We’veonlytouchedthesurfaceofhowpowerfulGulpcanbe.Formoreinformation,readtheofficialdocumentation.
Sowhichoneshouldyoubeusing?ProbablyGrunt.Gulpisstillonthebleedingedge.ItsecosystemneedsmoretimebeforeitshouldbeconsideredasareplacementforGrunt.Thatbeingsaid,Gulpisanexcellenttoolandhopefullywillcontinuetodevelop.
CodeKit
CodeKitisaMacappcreatedbyBryanJones.Itaimstotakealotofthepainoutofsettingupanautomatedworkflowbyprovidinganarrayofthemostcommonandusefulfeaturesforwebdevelopers:
Hasabuiltinwebserverwhichsupportslivebrowserrefreshingacrossallofyourdevices.Watchesforcodefilechangesandcompilesthemwithlintingandminification.HasbuiltinBowersupportforinstallingandmanagingcomponents.
ThemostappealingpartofCodeKitisthatitisveryeasytogetupandrunning.Allyouneedtodoisinstalltheappanditjustworks.Thereisnofumblingthroughconfigurationfilestryingtofigureoutwhyyourbuildscriptisn’tworkingcorrectly.Ontopofthat,theapp’sUIiscleanlydesignedandapleasuretouse.
RememberthoughthatCodeKitisaMaconlyapp,soifyouareworkingwithotherdevelopersyoumaywanttochecktheycanactuallyrunitbeforedivingin.
Tofindoutmore,watchthesedemonstrationvideosontheCodeKithomepage.Althoughthereisafreetrial,theapprequiresyoutopurchaseaonetimelicenseforfulluse.
Yeoman
Yeomandefinesapowerfulworkflowthathelpsyoukickstartnewprojectsbyinstallingandconfiguringalmosteverythingyoucouldneedtostartdevelopingyourwebapplications.Itgreatlyreducestheamountoftimeyouneedtowriteboilerplatecodesothatyoucanstartdevelopingsooner.
Yeomanprovidesanecosystemof‘generators’whichcanbeusedforsettingupthefoundationsofdifferenttypesofapplications.Thegeneratorsareopinionated,meaningthatalthoughbeststandardsarefollowedforthemostpart,theymaynotbeperfectlyconfiguredforyourspecificneeds.
ThereisareasonforleavingYeomanuntillast.Itcanbequitetemptingtouseageneratortolaythescaffoldingforyourapplicationwithoutactuallyknowingeverythingthatitisdoing.Ideally,youshouldlearnatleastthebasicsofallthedifferenttoolsthatYeomaninstallsandconfiguresbeforeactuallyusingYeoman.Thatway,itismucheasierforyoutoconfigureyourapplicationafterYeomanhasdoneitsmagic.
TheYeomanworkflowiscomprisedofthreeseparatelymaintainedtools:Yo,BowerandGrunt.YoisthenameoftheactualscaffoldingappfromYeoman,BoweriscoveredintheDependencyManagementchapterofthisbookandGrunthasbeencoveredatthebeginningofthischapter.
InstallingYoYoofferswebapplicationscaffolding,utilizingscaffoldingtemplatesreferredtoasgenerators.AllweneedisnpmtoinstallYo:1npminstallyo-g
OnceYoisinstalled,youwillneedtopickageneratorbestsuitedtoyourrequirements.Asanexamplelet’sinstallgenerator-angular,whichutilizesabunchoffeaturesthatmayberequiredofatypicalAngularJSfront-endwebapplicationworkflow:1npminstallgenerator-angular-g
AsisthecasewithYo,weinstallgeneratorsgloballysothattheycanbeaccessedfromanywhereinthecommandline.Oncethegeneratorisinstalled,wecancreateanewdirectoryforourprojectandfromwithinitrun:
1yoangular
Youwillbepromptedwithafewquestionswhichwillallowyoutoconfigurewhichtoolswillbeinstalled.Onceanswered,sitbackandrelaxasYotakescareofdownloading,installingandconfiguringeverything.Thereisalotoftextscrollduringthisprocesssomakesureyourterminalwindowisfullscreensothatpeoplearoundyouthinkyou’reworkinginthematrix.
Oncecomplete,you’llwanttospendsometimelookingthroughallofthefilesandfoldersthathavenowbeenplacedinyourprojectroot.Inthisparticulargenerator,awholebunchoftaskshavebeendefinedinyourGruntfile.jsfromwhichyoucanlearnalotaboutsomeofthebestpracticesusedinthedevelopercommunity.
Thereareliterallyhundredsofgeneratorsavailable,manyofwhichareunofficialandcontributedbythecommunity.YoucanviewthelistoverattheYeomanofficialpage.
OtherTipsThegoalofthischapteristohelpyoubecomethemostefficientdeveloperyoucanbe.Tohelpyouonyourway,hereareafewtipsandtrickstomakeyourlifealittleeasier.
zThemoreprojectsyouworkon,themorefrustratingitcanbetonavigatebetweenalloftheirdirectoriesinthecommandline.Enter‘z’,ahandybashscriptthathelpsyoutojumparounddirectoriesinthecommandline.Ittracksyourmostfrequentlyuseddirectoriesandallowsyoutonavigatethembysimplyenteringpartoftheirname.Forexample:1zmyApp
Forexample,thismaytakeyouto/var/www/myAppifthatisadirectorythatyoufrequntlyaccess.
Toinstall,grabthez.shfilefromhttps://github.com/rupa/zandwritesomethinglikethisinyour$HOME/.bashrcor$HOME/.zshrc:1./path/to/z.sh
Alfred.appAlfred,perhapsnamedafterthefamousbutlerofBatman,isaMacappthatcanbeusedasareplacementfortheOSXSpotlight.Amongmanyotherusefulproductivitytasks,Alfredhelpsyousavestimebylettingyousearchtheweboryourcomputerforfilesandapplicationswithafewkeystrokes.AlfredcomeshighlyrecommendedtoanyhardcoreMacuser.However,itisworthnotingthatApplemadesomegreatchangestotheOSXSpotlightinYosemiteandyoumayfindthatsufficientenough.
Alfredboostsproductivity
ChromeDevToolsOneofthestrongestattributesoftheGoogleChromebrowserisitssetofDeveloperTools-morecommonlyknownasDevTools.Theyareasetofwebdebuggingtoolsdirectlyaccessiblefromwithinyourbrowser.Althoughotherbrowsers,suchasFirefox,docomecompletewiththeirownsetoftoolsfordevelopers,Chromeprobablyprovidesthebestimplementation.Ifyou’venotusedDevToolsbefore,goaheadandinstallChromeandreadtheoverviewdocumentation.
ChromeDevToolsistheessentialwebpagedebuggingtool
SublimeTextLookingforanewtexteditor?Somethingwithasuperslickinterfaceandhugerangeoffeatures?Looknofurther.SublimeTextforMacisquicklybecomingoneofthemostpopulartexteditorsfordevelopers.
SublimeTexthasasuperslickinterface
Itsflagshipfeature,‘GotoAnything’,hasquicklybecomeoneofthemainreasonsfordevelopersfallinginlovewithSublimeText.BysimplypressingCommand-Pyoucanquicklynavigatetoanyfileinyourproject.Forafulllistofitsfeatures,andguidelinesonhowtoinstall,checkouttheofficialhomepage.
SummaryInthischapter,wetookafairydetailedlookatthetaskrunnerGruntandtouchedonotheroptionssuchasGulpandCodeKit.You’veseenhowimportanttaskrunnerscanbe,andhowtheycanhelpyoubecomeamoreefficientdeveloper.Wethenlookedatafewtipsandtrickstospeedupyourworkflowwhendeveloping.
Summary
Inthisbookwe’vecoveredthebasicsofcurrentfront-enddevelopmentpracticesallthewayfromwhatmightbeexpectedofaFront-endDeveloperintheworkplacetoautomatingyourdevelopmentworkflow.
Beforeproceedingtoapplytheconceptsyou’vepickedupfromthisbook,makesuretoknowallyouroptionsandpickonethatisabestfitforyouandyourproject.Youmayhavefoundthatsomethinginthisbookconflictswiththewayyou’recomfortabledoingthings.Thatisn’taproblem!Theconceptsandtoolsintroducedmaynotbethebestfitforeverydeveloperorproject.Sobyallmeans,continuetodowhatyou’recomfortablewith!Simplyjustbyreading,you’vetakenanimportantstepinimprovingyourgraspofthefront-endlandscape.
Aswithmostwebtechnology,thefront-enddevelopmentworldchangesrapidly.Soifyou’dliketotalkmoreaboutanythingpresentedinthisbookorjustshareideas,don’tforgetabouttheFront-EndFundamentalsGoogleGroup.
Thankyouverymuchforreading!
Appendix
Belowisacondensedlistoftheexternalresourcesthatwereferenceinthisbook.
AccessibilityWebAccessibilityChecklist:http://a11yproject.com/checklist.html
AutomationGrunt:http://gruntjs.comGulp:http://gulpjs.comYeoman:http://yeoman.io/z.sh:https://github.com/rupa/zAlfred:http://www.alfredapp.com/
DeploymentContinuousIntegration:http://www.thoughtworks.com/continuous-integration
LearningResourcesFront-endRapport:https://flipboard.com/section/front-end-rapport-bML9ITListofpopularAngularJSresources:https://github.com/jmcunningham/AngularJS-LearningLearningJavaScriptDesignPatterns:http://addyosmani.com/resources/essentialjsdesignpatterns/book/byAddyOsmaniDevelopingBackbone.jsApplications:http://addyosmani.github.io/backbone-fundamentals/byAddyOsmaniResponsiveWebDesign:http://www.abookapart.com/products/responsive-web-designbyEthanMarcotte
FrameworksAngularJS:https://angularjs.orgAngularJSBatarangChromeExtension:https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=enBackbone.js:http://backbonejs.org
BackboneDebuggerChromeExtension:https://chrome.google.com/webstore/detail/backbone-debugger/bhljhndlimiafopmmhjlgfpnnchjjbhd?hl=enUnderscore.js:http://underscorejs.orgEmber.js:http://emberjs.comExpress:http://expressjs.com/JourneyThroughTheJavaScriptMVCJungle:http://www.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle/byAddyOsmani
LanguagesRuby:https://www.ruby-lang.org/
OnlineServicesPrerender.io:https://prerender.ioCanIuse…:http://caniuse.com/#feat=history
PackageManagersNodePackageManager(npm):https://www.npmjs.org/Homebrew:http://brew.sh/Bower:http://bower.ioBundler:http://bundler.io/
PerformanceYahoobestpractices:https://developer.yahoo.com/performance/rules.htmlYSlow:http://yslow.orgCSSMin:https://github.com/gruntjs/grunt-contrib-cssminGoogle’sPageSpeedInsights:https://developers.google.com/speed/docs/insights/rulesUglifyJS:http://lisperator.net/uglifyjs/
SoftwareNode.js:http://nodejs.org/CodeKit:http://incident57.com/codekit/Scout:http://mhs.github.io/scout-app/SublimeText:http://www.sublimetext.com/
Styling
BEM:https://bem.info/Sass:http://sass-lang.comCompass:http://compass-style.org/Bourbon:http://bourbon.io/Less:http://lesscss.orgStylus:http://learnboost.github.io/stylus/MDNCSSmediaqueries:https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries
TemplatingMustache:http://mustache.github.ioHandlebars.js:http://handlebarsjs.com
TestingWraith:https://github.com/BBC-News/wraithPhantomJS:https://github.com/ariya/phantomjs/PhantomCSS:https://github.com/Huddle/PhantomCSSKarma:https://github.com/karma-runner/karmaJasmine:https://github.com/jasmine/jasmineJSHint:http://www.jshint.comJSHintGruntPlugin:https://www.npmjs.org/package/grunt-contrib-jshintCSSLint:https://github.com/CSSLint/csslintCSSLintGruntPlugin:https://www.npmjs.org/package/grunt-contrib-csslintChromeDevTools:https://developer.chrome.com/devtools
VersionControlGithub:https://github.comgit-flow:http://nvie.com/posts/a-successful-git-branching-model/Githubflow:http://scottchacon.com/2011/08/31/github-flow.htmlSemanticVersioning:http://semver.org/
WebServersMAMP:http://www.mamp.info/en/