Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Forget Native: Learn to Write HTML5 Mobile Apps

Forget Native: Learn to Write HTML5 Mobile Apps

A workshop given at South By Southwest Interactive 2013. In it, you'll learn about new features, helpful tips, and best practices for building great mobile web applications that not only look good, but are fast and responsive to the user.

As part of this workshop, a demo application was built to show off several of the techniques that were discussed. The source code for this demo app can be found here: https://github.com/skookum/webchat

In addition, a collection of links to helpful resources, guides and tutorials can be found here: https://pinboard.in/u:dbecher/t:sxswtalk/

David Becher

March 10, 2013
Tweet

Other Decks in Programming

Transcript

  1. WHAT I DO - Developer at SDW - try not

    to say front-end or back-end, but I’ve done a lot of front-end recently
  2. ✔ JavaScript ✔ HTML ✔ CSS - should be experienced

    building desktop web apps using JS - familiar with frameworks like JQuery/backbone/etc. but not overly dependent on them - Knowledge of new HTML5 stuff is beneficial but not absolutely required - Strong understanding of CSS fundamentals - knowledge of new CSS3 properties extremely beneficial - know what a vendor prefix is - techniques for working with images: sprites, etc.
  3. SIMPLE DOCUMENT/ DATA VIEWING - Small(ish) lists - Data that

    is (mainly) created somewhere else - Or if it’s not created somewhere else, it should be...
  4. SIMPLE DATA ENTRY - Text fields, dates, numbers, etc. -

    No long-form prose or rich text - A lot of this applies to native mobile apps as well
  5. - don’t need to do everything - simple browse -

    don’t add sessions from mobile - links back to main site - simple text entry limited to 250 chars - offline storage
  6. - don’t need to do everything - simple browse -

    don’t add sessions from mobile - links back to main site - simple text entry limited to 250 chars - offline storage
  7. - don’t need to do everything - simple browse -

    don’t add sessions from mobile - links back to main site - simple text entry limited to 250 chars - offline storage
  8. EASIER CROSS- DEVICE COMPATIBILITY - DON'T assume creating a mobile

    web app means it automatically works across all devices - you still must test on different devices which for whatever reason have browsers that work differently
  9. NOT GOOD FOR: •Games •Complex data viewing/editing •Anything that might

    require complex data processing on the client 1. Things mobile apps just outright aren’t good for: - things like WebGL may make games more feasible in the future - rich text, complex data structures, some types of media - JS is SLOW
  10. HOW TO MAKE SURE A WEB APP IS THE RIGHT

    FIT - need to know at the outset whether or not a web app will work for you - save yourself lots of pain down the road
  11. REFINE THE PROBLEM BEFORE YOU START - need to know

    from the outset how complex the problem is to make the decision if it will work as a web app - not just throwing features on the page to see what works
  12. CUT ALL NON- ESSENTIAL FEATURES - what’s essential for a

    mobile user? what isn’t? - lanyrd: probably no creating a conference from mobile - talk about golf scoring: wanted web app to do everything the website could; slowed down the most important parts
  13. TAKE INVENTORY OF EVERY SCREEN - double digits is probably

    too many for a web app - either go back to step 2 or re-evaluate your strategy
  14. WHAT’S DIFFERENT ABOUT A MOBILE WEB APP coming from desktop

    websites/web apps (even responsive sites), how are mobile web apps different?
  15. MORE TIME- CONSUMING TO BUILD - more careful about all

    the code you write to avoid wasting time
  16. SLOWER - weaker processors mean they can do less -

    Every item you add to the page slows it down - Constantly re-evaluating what's really necessary and appropriate for a mobile app - trade-offs for new features
  17. CLIENT/SERVER - we’ve found this a good fit for most

    of our web apps - Web app assets served with a cache manifest or aggressively server cached - Minified assets on client - Server API that web app talks to - 1 API for everything is possible
  18. OFFLINE-ABLE - Greatly aided by the client/server arch - Localstorage

    or something similar for data (usually data you get from the API) - Cache manifest for assets needed to render the page - Need to think about what happens when a user has no connection
  19. USER-TESTED - Arguably true for desktop sites too - Mobile

    web apps are a relatively new thing, users may not know what to expect - Easier to screw things up!
  20. BLEEDING-EDGE - Don't worry about old browsers - Mobile browsers

    rapidly adding features and standards - Many times necessary to use bleeding edge things for performance/optimization reasons - This DOES NOT MEAN only testing in WebKit! - Mobile FF: https://wiki.mozilla.org/Platform/Layout/CSS_Compatibility - Google Maps/Windows Phone 8 kerfuffle (http://arstechnica.com/gadgets/2013/01/google-maps- windows-phone-and-an-avoidable-mess/)
  21. LESS FOCUSED ON SEO - usually traffic would come from

    some other marketing site, which would have all the normal SEO things
  22. <META> TAGS - tell mobile browsers what kind of screen

    size/device the web app is designed for - add features to make the web app more native-like http://developer.android.com/guide/webapps/targeting.html https://developer.apple.com/library/safari/#codinghowtos/Mobile/UserExperience/_index.html#//apple_ref/ doc/uid/DTS40008248
  23. <meta&name="viewport" &&&&&&content=" &&&&&&&&&&height&=&[pixel_value&|&device:height]&, &&&&&&&&&&width&=&[pixel_value&|&device:width&]&, &&&&&&&&&&initial:scale&=&float_value&, &&&&&&&&&&minimum:scale&=&float_value&, &&&&&&&&&&maximum:scale&=&float_value&, &&&&&&&&&&user:scalable&=&[yes&|&no]&, &&&&&&&&&&target:densitydpi&=&[dpi_value&|&device:dpi&| &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&high:dpi&|&medium:dpi&|

    &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&low:dpi] &&&&&&&&&&"&/> VIEWPORT - by default the browser renders a zoomed-out overview of the page - for web apps, we usually need to tell the browser that our app should be the same size as the screen - also says we don’t want them to pinch to zoom
  24. @:ms:viewport{ &&&&width:&device:width; } VIEWPORT - IE10 ignores the meta viewport

    tag for any viewport smaller than 400 pixels in width - use css3 device adaptation http://dev.w3.org/csswg/css-device-adapt/ - there’s also a property for user-scale - the @viewport rule is in the process of becoming a standard (the Apple way is not) - also meta name=”mobileoptimized”
  25. ICONS <link&rel="apple:touch:icon" &&&&&&href="touch:icon:iphone.png"&/> <link&rel="apple:touch:icon" &&&&&&sizes="72x72" &&&&&&href="touch:icon:ipad.png"&/> <link&rel="apple:touch:icon" &&&sizes="114x114" &&&href="touch:icon:iphone:retina.png"&/> <link&rel="apple:touch:icon"

    &&&&&&sizes="144x144" &&&&&&href="touch:icon:ipad:retina.png"&/> <meta&name="msapplication:TileImage" &&&&&&content="/metro:tile.png"/> <meta&name="msapplication:TileColor"&content="#015565"/> - users can add your app to their home screen - when they do, they need an icon - android also uses this icon - precomposed vs. non-precomposed versions - Windows phone/ie10: - 144x144px image with your logo/icon filling the whole canvas and a transparent background
  26. STARTUP IMAGE - like on a native iOS app, you

    can specify a startup image to be displayed while the app loads - should be a screenshot of the interface without text - on an iPhone 5, apps saved to home screen will be letterboxed if you don’t supply the right sized image!
  27. NEW FORM INPUT TYPES - really important to get the

    UX right for any form input fields - OSs can change keyboard/input mechanism based on data type
  28. DATES - date: month, day of month and year -

    datetime/datetime-local: date & time - month - time: hour, minute, am/pm - week
  29. - url iphone - tel iphone/android - number iphone -

    supplying a pattern makes the iphone keyboard more like the tel/android version
  30. - url iphone - tel iphone/android - number iphone -

    supplying a pattern makes the iphone keyboard more like the tel/android version
  31. - url iphone - tel iphone/android - number iphone -

    supplying a pattern makes the iphone keyboard more like the tel/android version
  32. - url iphone - tel iphone/android - number iphone -

    supplying a pattern makes the iphone keyboard more like the tel/android version
  33. HTML5 AUDIO/VIDEO TAGS - plays back audio/video files in a

    way that is optimized for mobile devices - most devices support h.264 - JS API for interacting with the video playback
  34. CACHE MANIFEST - tells browser to download assets and store

    locally for offline use - makes it so application can launch even when there’s no internet connection - good article on a list apart
  35. CACHE MANIFEST CACHE&MANIFEST #&version&1 styl/compiled/style.css js/compiled/vendor.js js/compiled/app.js images/icon:144x144.png images/startup:ipad:landscape.png images/startup:ipad:portrait.png

    images/noise:dark.png images/[email protected] images/[email protected] NETWORK: * <html &manifest="offline.appcache"> - text file - referenced from <html manifest="offline.appcache"> - starts with CACHE MANIFEST - list out all the assets that should be saved for offline use - tell the browser to allow network requests for things not in the cache (needed for ajax, etc.)
  36. FILES ALWAYS COME FROM THE CACHE - even when online!

    - this provides a good experience when the internet connection may be intermittent, no need to wait to see if cached files should be used - once the app loads, it checks the manifest to see if there are updates to download
  37. NEW FILES ONLY DOWNLOAD WHEN THE MANIFEST CHANGES - the

    actual text contents of the manifest - usually fix this by: - updating the URLs to assets when they change (e.g on a CDN) - changing a comment in the manifest - as a consequence: DO NOT EVER cache the manifest file! - additional cache, not an alternative one!
  38. NO CONDITIONAL DOWNLOADS - if you have, for example, 2X

    images for retina displays they need to be used for every browser
  39. BEST PRACTICES - some things you should do (and should

    already be doing) for good experiences on mobile
  40. DON’T HIDE YOUR CONTENT - mostly applicable to mobile versions

    of full sites - if redirecting from a desktop site, make every effort to redirect to the relevant page - try to include all the necessary useful information - dont use hover states
  41. USE SUPPORTED CONTENT TYPES - PNGS with transparency - H.264

    video has to be encoded a certain way - h.264 is supported on android - ff may add support in the future - ff does not play mp3s
  42. USE NATIVE ELEMENTS WHEN POSSIBLE - avoid custom form controls,

    dialogs, etc. - browser/OS has optimized display of native controls, so take advantage of them - e.g.: <select> tags show a nice picker on iOS
  43. USE FEATURE DETECTION - mobile browsers are changing fast and

    things can differ between even minor versions - use something like Modernizr to detect if a browser is capable of some new feature
  44. DEFER PARSING OF JS - tricky with mobile apps where

    caching for offline use is a concern - mobile gmail loads JS in comments and eval()s it when needed - probably not a good optimization until the app is really big! - also: defer/async attribute on scripts
  45. USE AS FEW ELEMENTS AS POSSIBLE - each element on

    the page slows down rendering - simple apps should have simple HTML structure - leverage new CSS properties (multiple BGs etc.) to reduce presentational elements
  46. CSS - use new CSS features to optimize network usage

    - be careful how you write your CSS; some rules are expensive and you need to know how the browser parses CSS
  47. A NOTE ON CSS PRE- PROCESSORS - can be very

    useful for reducing the amount of code you need to write - abstract vendor prefixes - usually end up with more bloated CSS - very easy to write non-performant CSS - use if you want, be aware of the drawbacks
  48. GRADIENTS background:&:webkit:linear:gradient( &&top,&blue,&white); background:&linear:gradient( &&to&bottom,&blue,&white); (go through these next 3

    slides quickly) - use instead of images - linear, radial - only need old webkit prefixed and official version - things like repeating, multiple color stops, angles...
  49. SHADOWS box:shadow:&5px&5px&10px&rgba(0,&0,&0,&0.5), &&&&&&&&&&&&inset&0&0&10px&rgba(0,0,0,0.5); text:shadow:&rgba(0,&0,&0,&0.9)&1px&1px&0; - text and box shadows -

    control over offset, blur, spread, and color - blackberry 7 (not 10) is the only one that needs a -webkit- prefix - talk about rgba
  50. TRANSFORMS transform:origin:&bottom&left; transform:&none transform:&matrix(1.0,&2.0,&3.0,&4.0,&5.0,&6.0) transform:&matrix3d(1.0,&2.0,&3.0,&4.0,&5.0,&6.0,&7.0,& 8.0,&9.0,&10.0,&11.0,&12.0,&13.0,&14.0,&15.0,&16.0) transform:&translate(12px,&50%) transform:&translate3d(12px,&50%,&3em) transform:&scale(2,&0.5) transform:&scale3d(2.5,&1.2,&0.3)

    transform:&rotate(0.5turn) transform:&rotate3d(1,&2.0,&3.0,&10deg) transform:&skewX(30deg) transform:&skewY(1.07rad) transform:&perspective(17px) - all these really need vendor prefixes on the transform: part - use 3d, although it might seem slower it actually can be faster since it is offloaded to the GPU
  51. ANIMATING CONTENT - CSS supports 2 kinds: animations and transitions

    - JS animations are SLOW, do NOT use! - CSS animations can be optimized by the browser - turning off in inactive windows - combining paint cycles - frame-skipping - (some) CSS animations are hardware-accelerated on some browsers, makes for a much better experience - use transforms to move instead of animating left and top - some modern libraries like zepto use CSS animations by default
  52. ANIMATIONS #spinner.go&{ &&animation:duration:&1s; &&animation:name:&spinnerRotate; &&animation:iteration:count:&infinite; &&animation:timing:function:&linear; } & @keyframes&spinnerRotate&{ &&from&{&/*&Can&omit&this&keyframe&*/

    &&&&transform:&rotate(0deg); &&} &&to&{ &&&&transform:&rotate(360deg); &&} } - can be looping, multiple iterations, back-and-forth - can have intermediate keyframes - unlike transitions, css properties are not retained when the animation ends! (by default, but you can make this work)
  53. CSS PERFORMANCE CONSIDERATIONS - not just about file size of

    css - how the browser lays out and repaints - how browser parses selectors
  54. SOME PROPERTIES ARE SLOW - you should be mindful of

    how much CSS is needed to accomplish the design you want - complicated designs will be slow even using fancy CSS - simple is better
  55. LIKE, REALLY SLOW http://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/ - x axis is repaint time

    in ms - In both Opera and WebKit, "border-radius" is among the most expensive CSS properties to affect rendering time
  56. SELECTOR MATCHING CAN BE SLOW - to fully understand why,

    we must know how the browser parses CSS
  57. HOW DOES THE BROWSER PARSE CSS SELECTORS? - ask audience

    if they know - differences between the renderers, but the same general rules apply - gecko and webkit similar
  58. .form:actions&button#backButton div&>&[hidden="true"] form&label&+&input[type="checkbox"] .toolbar&button.toolbarButton KEY SELECTOR - let’s take this

    example - parser has what’s known as a key selector: rightmost part of the rule that matches the element being matched rather than its ancestors - multiple for comma-separated selectors
  59. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton ID CLASS TAG UNIVERSAL - 4

    types of key selectors - id: pretty much anything with an ID selector - class: anything with a class - tag: rules with a tag but no ID or class specified - universal: all other rules - why does this matter?
  60. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton <body> &&<form&method="post"&action="/"> &&& <p> &&& &&<label>Input&1</label>

    &&& &&<input&type="text"&/> &&& </p> &&& <p> &&& &&<label>Input&2</label> &&& &&<input&type="checkbox"&/> &&& </p> &&& <div&class="form:actions"> &&& &&<button&id="backButton"& class="toolbarButton">Back</button> &&& &&<input&class="toolbarButton"& type="submit">Submit</input> &&& </div> &&</form> </body> - starts with the key selector - browser keeps record of classes and IDs in the dom - only matches relevant nodes for that key selector
  61. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton <body> &&<form&method="post"&action="/"> &&& <p> &&& &&<label>Input&1</label>

    &&& &&<input&type="text"&/> &&& </p> &&& <p> &&& &&<label>Input&2</label> &&& &&<input&type="checkbox"&/> &&& </p> &&& <div&class="form:actions"> &&& &&<button&id="backButton"& class="toolbarButton">Back</button> &&& &&<input&class="toolbarButton"& type="submit">Submit</input> &&& </div> &&</form> </body> - starts with the key selector - browser keeps record of classes and IDs in the dom - only matches relevant nodes for that key selector
  62. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton <body> &&<form&method="post"&action="/"> &&& <p> &&& &&<label>Input&1</label>

    &&& &&<input&type="text"&/> &&& </p> &&& <p> &&& &&<label>Input&2</label> &&& &&<input&type="checkbox"&/> &&& </p> &&& <div&class="form:actions"> &&& &&<button&id="backButton"& class="toolbarButton">Back</button> &&& &&<input&class="toolbarButton"& type="submit">Submit</input> &&& </div> &&</form> </body> - starts with the key selector - browser keeps record of classes and IDs in the dom - only matches relevant nodes for that key selector
  63. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton <body> &&<form&method="post"&action="/"> &&& <p> &&& &&<label>Input&1</label>

    &&& &&<input&type="text"&/> &&& </p> &&& <p> &&& &&<label>Input&2</label> &&& &&<input&type="checkbox"&/> &&& </p> &&& <div&class="form:actions"> &&& &&<button&id="backButton"& class="toolbarButton">Back</button> &&& &&<input&class="toolbarButton"& type="submit">Submit</input> &&& </div> &&</form> </body> - starts with the key selector - browser keeps record of classes and IDs in the dom - only matches relevant nodes for that key selector
  64. s&button#backButton >&[hidden="true"] +&input[type="checkbox"] r&button.toolbarButton <body> &&<form&method="post"&action="/"> &&& <p> &&& &&<label>Input&1</label>

    &&& &&<input&type="text"&/> &&& </p> &&& <p> &&& &&<label>Input&2</label> &&& &&<input&type="checkbox"&/> &&& </p> &&& <div&class="form:actions"> &&& &&<button&id="backButton"& class="toolbarButton">Back</button> &&& &&<input&class="toolbarButton"& type="submit">Submit</input> &&& </div> &&</form> </body> - starts with the key selector - browser keeps record of classes and IDs in the dom - only matches relevant nodes for that key selector
  65. form&label&+&input[type="checkbox"] - checks key selector - moves left, going all

    the way up the dom tree if needed - discards elements that don’t match and moves on - so what can we do to improve performance?
  66. REMOVE UNUSED SELECTORS - for mobile web apps this is

    a tradeoff - difficult on single-page apps - be conscious of unused rules
  67. DON’T USE UNIVERSAL RULES - really bad, must check every

    element on the page! - things like the * operator are obvious, but things like attribute selectors are less so - at least qualify with a tag name
  68. BE CAREFUL WHEN USING RESET STYLESHEETS - usually (obviously) have

    a lot of universal and tag selectors - normalize.css is the best I’ve found
  69. DON’T OVERLY QUALIFY ID AND CLASS SELECTORS - IDs are

    unique, usually no need for tag or class selectors on them - sometimes you need to add a class name that’s reused in other places, that’s probably OK
  70. USE THE MOST SPECIFIC CATEGORY POSSIBLE - e.g. adding a

    class to elements to move it from the tag category to the class category - remove descendant and child selectors with tags, replace with classes
  71. AVOID THE DESCENDANT SELECTOR - “space” selector - the most

    expensive selector in CSS! - doubly bad with tag or universal rules - actually banned in Firefox' User Interface CSS - maybe use child selector (still bad though)
  72. USE INHERITANCE - rules can cascade down to children in

    some cases, no need to select all children when you can select the parent - e.g. list-style-type on container element instead of li
  73. WEBKIT CSS PROFILER - several useful tools for finding bottlenecks

    and optimizing - same tools are also useful for JS (later)
  74. - CSS profiler - shows which selectors take up the

    most parsing time - as shown here, the most expensive are mostly the universal/tag selectors
  75. - audits - shows unused CSS selectors on the current

    page (among other things) - not perfect, use judgement when removing rules found with this tool
  76. JAVASCRIPT - the most important things to optimize for any

    reasonably complex mobile web app - also the hardest thing to do
  77. ZEPTO.JS zeptojs.com - a minimalist JavaScript library for modern browsers

    with a largely jQuery-compatible API - 8.4k vs. 32k for jQuery 1.9 - jquery 2.0 will be better, but the beta only shaves off ~10% of the file size
  78. ENDER.JS ender.jit.su - more of a library framework than a

    jquery replacement - 12.9k for a minimum jquery-like set of tools
  79. SMALL, LIGHT FRAMEWORKS microjs.com - underscore, backbone, lawnchair, etc -

    allow you to include only features you need without extra stuff - don’t try to do a lot for you, give you more control over what’s happening - usually don’t include as many browser hacks
  80. LOCALSTORAGE - store data in the client persistently - simple

    key/value, can encode JSON into a string with JSON.stringify - data side of offline storage (as opposed to cache manifest) - session storage is per-app-per-window
  81. CORS - cross-origin resource sharing - use XHR requests across

    domains - requires special headers to be sent from the server - useful if using the client-server architecture
  82. GEOLOCATION - get user’s current location - can be from

    GPS, cell towers, wifi access points, etc. - get current position - watch position watches for location updates and calls callback every time they update - position object can contain: latitude, longitude, altitude, accuracy, altitudeAccuracy, heading and speed - browser will always ask permission before getting location, if the user declined the error cb will be called with error PERMISSION_DENIED
  83. TOUCH EVENTS/ POINTER EVENTS - webkit and mozilla uses touch

    events - click events on iPhone are delayed (browser needs time to determine if it’s a click or double-tap, etc.) - MS uses the pointer events for touch, pen, or mouse contact.
  84. TOUCH EVENTS element.addEventListener("touchstart",&touchStart,&false); element.addEventListener("touchmove",&touchMove,&false); element.addEventListener("touchend",&touchEnd,&false); element.addEventListener("touchcancel",&touchCancel,&false); element.addEventListener("gesturestart",&gestureStart,& false); element.addEventListener("gesturechange",&gestureChange,& false);

    element.addEventListener("gestureend",&gestureEnd,&false); - work basically like regular click, etc. event listeners - touchstart/move/etc. give you all the current touches in event.touches - gesturechange gives you event.scale and event.rotation - sort of low-level, don’t have gestures like swipe, etc.
  85. TOUCH EVENTS 1.Begin gesture if you receive a touchstart event

    containing one target touch. 2.Abort gesture if, at any time, you receive an event with >1 touches. 3.Continue gesture if you receive a touchmove event mostly in the x-direction. 4.Abort gesture if you receive a touchmove event mostly the y-direction. 5.End gesture if you receive a touchend event. - example from Apple of a swipe gesture - zepto has higher-level events for swipes
  86. POINTER EVENTS - emerging spec by the w3c - in

    IE10 - abstract concept of a “pointer”, simplicity of mouse events - share code between devices, but can also write specific code for each input type
  87. POINTER EVENTS <style> &&#canvas&{ &&&&:ms:touch:action:&auto&|&none&|& &&&&&&&&&&&&&&&&&&&&&&[&[&[&pan:x&||&pan:y&||& &&&&&&&&&&&&&&&&&&&&&&&&&&&&pinch:zoom&?&] &&&&&&&&&&&&&&&&&&&&&&&&&&|&manipulation&] &&&&&&&&&&&&&&&&&&&&&&&&||&double:tap:zoom&?&] &&}

    </style> - in css, use the touch-action property to specify whether and how a given region can be manipulated by the user—for instance, by panning or zooming - if action is not allowed by this property, dom events are fired - done for performance, don’t have to wait for JS to determine what should happen when a touch happens
  88. BE OPTIMISTIC - assume AJAX requests (unless you need the

    response) will be successful, error gracefully - backbone does this by default for creates and deletes - even better when combined with localstorage
  89. USE LOCAL DATA - for example, pass data through localstorage

    before syncing to server - more robust, better able to handle slow/unreliable connections - much harder to write sync algorithm than to just save!
  90. PERFORMANCE CONSIDERATIONS - the key theme: things you have gotten

    lazy about and forgotten about, you may need to do
  91. MEMORY MANAGEMENT - memory management? but JS is garbage collected,

    I don’t need to worry about memory - yes, but you need to work with the garbage collector - garbage collection is expensive - from gmail: “Memory leaks were found to be the biggest drain on client-side performance” - event listeners found to be the biggest culprit
  92. MANAGING EVENTS - most common way for memory to get

    leaked is event listeners - not just dom events, but custom events like on backbone models - on desktop browsers, this is not a big deal because you’re usually not on the page very long and memory usage is less of a concern
  93. MANAGING EVENTS var&DocumentRow&=&Backbone.View.extend({ &&tagName:&"li", &&events:&{ &&&&"click&.button.edit":&&&"edit" &&}, &&initialize:&function()&{ &&&&this.listenTo(this.model,& &&&&&&&&&&&&&&&&&&"change",&

    &&&&&&&&&&&&&&&&&&this.render); &&} &&render:&function()&{&/*&...&*/&} }); var&DocumentList&=&Backbone.View.extend({ &&tagName:&"ul", &&initialize:&function()&{ &&&&this.listenTo(this.collection, &&&&&&&&&&&&&&&&&&"reset",& &&&&&&&&&&&&&&&&&&this.render); &&} &&render:&function()&{ &&&&this.$el.empty(); &&&&this.collection.each(function(model)&{ &&&&&&var&view&=&new&DocumentRow({ &&&&&&&&model:&model &&&&&&}); &&&&&&this.$el.append(view.el); &&&&},&this); &&} }); - a collection of data models - we want a list view of every model - each list items handles its own changes (better performance!) - list items as a “sub-view” of another backbone view
  94. DocumentList <ul> JS VIEWS JS DATA DOM VIEWS DocumentCollection Document

    Document Document DocumentRow DocumentRow DocumentRow &&<li></li> &&<li></li> &&<li></li> </ul> - diagram of event listener dependencies - collection holds all its models - DocumentList listens to its collection and has a pointer to its dom node (ul) - each DocumentRow listens to its model and has a pointer to its dom node (li) - dom node will have pointer back to view with dom event listeners - what happens when collection is reset? - don’t those models/views/dom nodes go away? - nothing for the garbage collector to collect, everything has a pointer (the event listener from the dom node is the pointer for the views)
  95. DocumentList <ul> JS VIEWS JS DATA DOM VIEWS DocumentCollection Document

    Document Document DocumentRow DocumentRow DocumentRow &&<li></li> &&<li></li> &&<li></li> </ul> Document Document Document DocumentRow DocumentRow &&<li></li> &&<li></li> &&<li></li> DocumentRow - diagram of event listener dependencies - collection holds all its models - DocumentList listens to its collection and has a pointer to its dom node (ul) - each DocumentRow listens to its model and has a pointer to its dom node (li) - dom node will have pointer back to view with dom event listeners - what happens when collection is reset? - don’t those models/views/dom nodes go away? - nothing for the garbage collector to collect, everything has a pointer (the event listener from the dom node is the pointer for the views)
  96. var&DocumentList&=&Backbone.View.extend({ &&tagName:&"ul", &&initialize:&function()&{ &&&&this.listenTo(this.collection, &&&&&&&&&&&&&&&&&&"reset",& &&&&&&&&&&&&&&&&&&this.render); &&&&this.views&=&[]; &&} &&render:&function()&{ &&&&this.$el.empty();

    &&&&this.views.forEach(function(view)&{ &&&&&&view.remove(); &&&&}); &&&&this.views&=&[]; &&&&this.collection.each(function(model)&{ &&&&&&var&view&=&new&DocumentRow({ &&&&&&&&model:&model &&&&&&}); &&&&&&this.$el.append(view.el); &&&&&&this.views.push(view); &&&&},&this); &&} }); - removes events that were added with listenTo - removes the dom node with $().remove() which removes event listeners - another option is a custom event that you call on every view before it’s removed (if you need to do more custom cleanup)
  97. HOW DO I FIND THESE THINGS? - memory leaks can

    be tricky to find - memory optimization is an art, not a science
  98. - heap snapshot - takes snapshot of your application’s memory

    usage - compare memory usage between 2 snapshots - good starting technique: take heap snapshot before and after switching states in your app - for example, take heap snapshot, switch to another section and back again, take another heap snapshot
  99. - comparison view lets you compare 2 snapshots and see

    what objects increased - good starting point is to look at detached DOM nodes - will show you how many “zombies” were created when leaving the section that you were expecting to go away
  100. - retain tree shows you what still points to this

    element and therefore why it is still in memory - sometimes you need to look at object properties to figure out exactly what object it is - then you can figure out why these objects are still in memory - the DOM nodes may not be the biggest memory hogs, but the things that are retaining them likely are
  101. - comparison view also shows objects grouped by constructor -

    in this example, “child” is all backbone objects - can see where there is an increase in the number of these objects where you don’t expect - hover over individual objects to see properties
  102. - timeline view - can show memory usage over time,

    as well as how many DOM nodes there are - in this example: - loaded page - switched sections (jump in graph) - GC ran (memory usage goes down) - switched back to original page - notice: memory usage is higher than originally, dom nodes and event listeners higher as well
  103. - after leaving page open for 2 hours - culprit:

    timer on page that showed the last sync time - eventually stopped updating and just shows the time, hence the plateau - note that the graph scale is modified, so this is only 2272-3159 dom nodes
  104. DON’T LOAD THINGS INTO MEMORY UNTIL THEY’RE NEEDED - in

    this app, collections are not loaded into memory from localstorage until they are needed by a view - actually implemented a “reference-counting” system on collections that would automatically empty them when no more views need them
  105. REDUCE REFLOWS - browser has internal tree of “frames” (similar

    to dom tree) - change in the dimensions of one cascades - browser queues up these layouts to do at once
  106. REDUCE REFLOWS //&bad for(big;&loop;&here)&{ &&&&el.style.left&=& el.offsetLeft&+&10&+&"px"; &&&&el.style.top&&=& el.offsetTop&&+&10&+&"px"; } //&better

    var&left&=&el.offsetLeft, &&&&top&&=&el.offsetTop &&&&esty&=&el.style; for(big;&loop;&here)&{ &&&&left&+=&10; &&&&top&&+=&10; &&&&esty.left&=&left&+&"px"; &&&&esty.top&&=&top&&+&"px"; } - getting style information causes a reflow - better to get the value once and cache it - offsetTop, offsetLeft, offsetWidth, offsetHeight - scrollTop/Left/Width/Height - clientTop/Left/Width/Height - getComputedStyle(), or currentStyle in IE
  107. REDUCE REFLOWS //&bad var&ul&=&$('<ul>'); ul.appendTo('#div'); for(var&i=0;&i<100;&i++)&{ &&ul.append('<li>' &&&&&&&&&&&&+&i &&&&&&&&&&&&+&'</li>'); }

    //&better var&ul&=&$('<ul>'); for(var&i=0;&i<100;&i++)&{ &&ul.append('<li>' &&&&&&&&&&&&+&i &&&&&&&&&&&&+&'</li>'); } ul.appendTo('#div'); - batch dom changes that occur in rapid succession - don’t manipulate the live dom tree - if editing a node, clone it first and then swap out when finished - do display:none first
  108. MARKETING & DELIVERY - your app is finished, how do

    you get it in the hands of customers?
  109. ADD TO HOME SCREEN - detect if browser is in

    standalone mode with window.navigator.standalone on iOS devices - ios 5 enables nitro JS engine, so just as fast as running in safari - downside: nobody really knows how to do this - must usually rely on annoying prompts to get users to do it - an android it’s just a bookmark so it still opens in the browser
  110. WRAP IN A NATIVE APP - advantages - can (easily)

    charge money to download - marketing/promotion in app store - can use a bridge to access more native features that web apps can’t access (like higher localstorage limits) - disadvantages: - slower (on iOS) due to UIWebViews not having access to Nitro engine - native apps mean an expectation of native look/feel which may not be the case for a web app