© 2013 Amazon.com, Inc. and its affiliates. All rights reserved. May not be copied, modified, or distributed in whole or in part without the express consent of Amazon.com, Inc.
Russell Beattie, Amazon
November 15, 2013
Coding Tips You Should Know Before Distributing
Your HTML5 Web App on Mobile Devices
Agenda
• Welcome
• HTML5 Mobile Web App Basics
• Setup
• DevTools Demo
• Coding Tips and Best Practices
• HTML
• CSS
• Javascript
• Offline
HTML5 Mobile
Web App Basics
Setup
• Text Editor
• Modern Desktop Browser
• Chrome, Firefox, Safari, IE 11, etc.
• Modern Mobile Browser or Tester
• Amazon Web App Tester, Chrome, etc.
• Android SDK
• For remote debugging
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head>
<body>
<div id="content"></div>
<script src="main.js"></script> </body>
</html>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css">
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head>
<body>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head>
<body>
<div id="content"></div>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head>
<body>
<div id="content"></div>
<script src="main.js"></script>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8">
<title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head>
<body>
<div id="content"></div>
<script src="main.js"></script> </body>
</html>
Basic HTML5 Web App Template
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Template</title> <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"> <style>
html, body {height: 100%; margin: 0; padding: 0; background: #000; color: #FFF} </style>
<link rel="stylesheet" href="main.css"> </head> <body> <div id="content"></div> <script src="main.js"></script> </body> </html> Friday, November 15, 13
DevTools Demo
• Connecting (via WebSockets or TCP/IP)
• Live modification of page (CSS, HTML)
• Interaction with device via inspection button
• Debugging (JavaScript, Network, etc.)
• Frame-rate / GPU
• Profiling
Coding Tips and
Best Practices
HTML
<meta
name
=
"apple-touch-fullscreen"
content
=
"yes"
>
<meta
name
=
"apple-mobile-web-app-capable"
content
=
"yes"
>
<meta
name
=
"apple-mobile-web-app-status-bar-style"
content
=
"black"
>
<meta
name
=
"apple-mobile-web-app-title"
content
=
"UVM Home"
>
<link
rel
=
"apple-touch-startup-image"
href
=
"/startup.png"
>
<link
rel
=
"apple-touch-icon"
sizes
=
"128x128"
href
=
"niceicon.png"
>
<link
rel
=
"apple-touch-icon-precomposed"
sizes
=
"144x144"
href
=
"img/touch/apple-touch-icon-144x144-precomposed.png"
>
<link
rel
=
"apple-touch-icon-precomposed"
href
=
"img/touch/apple-touch-icon-57x57-precomposed.png"
>
<link
rel
=
"shortcut icon"
href
=
"img/touch/apple-touch-icon.png"
>
Headers - Apple
<!-- Google -->
<meta
name
=
"mobile-web-app-capable"
content
=
"yes"
>
<link
rel
=
"shortcut icon"
sizes
=
"196x196"
href
=
"nice-highres.png"
>
<link
rel
=
"shortcut icon"
sizes
=
"128x128"
href
=
"niceicon.png"
>
<!-- Blackberry -->
<meta
name
=
"HandheldFriendly"
content
=
"true"
>
<meta
name
=
"cursor-event-mode"
value
=
"native"
>
<meta
http-equiv
=
"x-rim-auto-match"
content
=
"none"
>
<!-- Microsoft -->
<meta
http-equiv
=
"X-UA-Compatible"
content
=
"IE=edge"
>
<meta
http-equiv
=
"cleartype"
content
=
"on"
>
<meta
name
=
"msapplication-TileImage"
content
=
"pics/logowin8pin.png"
/>
<meta
name
=
"msapplication-TileColor"
content
=
"#B20099"
/>
<meta
name
=
"msapplication-badge"
value
=
"frequency=1440;polling-uri=http://
www.example.com/Win8TileNotification.php"
/>
Headers - Google, Blackberry, Microsoft
<link
rel
=
"shortcut icon"
type
=
"image/x-icon"
href
=
"favicon.ico"
/>
<link
rel
=
"shortcut icon"
href
=
"favicon.ico"
/>
<meta
name
=
"format-detection"
content
=
"telephone=no"
>
<meta
name
=
"format-detection"
content
=
"address=no"
/>
<link
rel
=
"dns-prefetch"
href
=
"page2.html"
>
<meta
name
=
"description"
content
=
""
/>
<meta
name
=
"keywords"
content
=
""
/>
Headers - Firefox, W3C, etc.
<form>
Date: <input
type
=
"date"
value
=
"2013-09-03"
name
=
"dateInput"
>
Datetime: <input
type
=
"datetime"
name
=
"datetimeInput"
>
Datetime-local:
<input
type
=
"datetime-local"
value
=
"2013-09-03T20:00"
name
=
"datetime-local"
>
Email: <input
type
=
"email"
name
=
"emailInput"
>
Month: <input
type
=
"month"
value
=
"2013-09"
name
=
"monthInput"
>
Number: <input
type
=
"number"
name
=
"numberInput"
>
Password: <input
type
=
"password"
name
=
"passwordInput"
>
Range: <input
type
=
"range"
name
=
"rangeInput"
>
Search: <input
type
=
"search"
name
=
"searchInput"
>
Tel: <input
type
=
"tel"
name
=
"telInput"
>
Time: <input
type
=
"time"
name
=
"timeInput"
>
Url: <input
type
=
"url"
name
=
"urlInput"
>
</form>
Input Field Types
<input
type
=
"url"
name
=
"urlInput"
required
pattern
=
"https?://.+"
>
...
input
:required:invalid
,
input
:
focus:invalid
{
background-image: url(/images/invalid.png);
background-position: right top;
background-repeat: no-repeat;
}
input
:required:valid {
background-image: url(/images/valid.png);
background-position: right top;
background-repeat: no-repeat;
}
Required and Validation
CSS
html
{
-webkit-tap-highlight-color: rgba(
0
,
0
,
0
,
0
);
-webkit-user-select: none;
}
body
{
-webkit-font-smoothing: antialiased;
/* Fix for webkit rendering */
-webkit-text-size-adjust:
100
%;
}
/* don't let "actions" dialog to come up when element is touch/held */
.prevent-action
{
-webkit-touch-callout: none;
}
Interaction
/* no dragging of element at all */
.content p.no-drag
{
-webkit-user-drag: none;
}
/* drags entire element, not the text/selection */
.sidebar div.element-drag
{
-webkit-user-drag: element;
}
/* change the character used to hide user passwords */
input
[type=
"password"
] {
-webkit-text-security: square;
}
textarea
[contenteditable] {
-webkit-appearance: none;
}
Interaction continued
Friday, November 15, 13/* position */
-webkit-transform: translate(
0, 0
);
-webkit-transform: translateZ(
0
);
-webkit-transform: translate3d(
0
,
0
,
0
);
/* scale */
-webkit-transform: scale(
0
);
/* rotation */
-webkit-transform: rotate(
0
);
/* opacity */
opacity:
0.5
;
Hardware acceleration
Friday, November 15, 13@-webkit-keyframes pulse { from { -webkit-transform: scale(.1); } to { -webkit-transform: scale(1); } } div { -webkit-animation-name: pulse; -webkit-animation-duration: 2s; -webkit-animation-iteration-count: infinite; -webkit-animation-timing-function: ease-in-out; -webkit-animation-direction: alternate; } // Javascript
content.addEventListener("webkitAnimationIteration", countAnims, false);
Animations
// CSS
.box
{
left:
40px
;
-webkit-transition: all
0.3s
ease-out;
}
div.box.totheleft
{
-webkit-transform: translate3d(
0
,
0
,
0
);
}
div.box.totheright
{
-webkit-transform: translate3d(
80px
,
0
,
0
);
}
// Javascript
window.
addEventListener
(
"webkitTransitionEnd"
, function() {
// Handle the end of the transition
}, false);
Transitions
@media screen and (orientation:portrait)
{
/* portrait-specific styles */
}
@media screen and (orientation:landscape)
{
/* landscape-specific styles */
}
@media only screen and (-webkit-min-device-pixel-ratio:
1.5
), only screen and
(min-resolution:
144dpi
)
{
/* Style adjustments for viewports that meet the condition */
}
Media Queries
JavaScript
var mql = window.
matchMedia
(
"(orientation: portrait)"
);
if(mql.matches) {
// Portrait orientation
} else {
// Landscape orientation
}
mql.
addListener
(function(m) {
if(m.matches) {
// Changed to portrait
} else {
// Changed to landscape
}
});
// Resizing
window.
addEventListener
(
"resize"
, function() {
// window.innerHeight > window.innerWidth ...
}, false);
Orientation Detection
var meta = document.
createElement
(
"meta"
);
meta.
setAttribute
(
'name'
,
'viewport'
);
var content =
'initial-scale='
;
content +=
1
/ window.devicePixelRatio;
content +=
',user-scalable=no'
;
meta.
setAttribute
(
'content'
, content);
document.
getElementsByTagName
(
'head'
)[0].
appendChild
(meta);
Device Resolution
// no pre-rendering
function
render
() {
drawSpaceShip
(context);
requestAnimationFrame
(render);
}
// pre-rendering
var sprite = document.
createElement
(
'canvas'
);
sprite.width =
64
;
sprite.height =
64
;
var spriteCtx = sprite.
getContext
('
2d
');
drawSpaceShip
(spriteCtx);
function
render
() {
context.
drawImage
(sprite,
0
,
0
);
requestAnimationFrame
(render);
}
Canvas pre-rendering
// Slower
for (var i =
0
; i < points.length -
1
; i++) {
var p1 = points[i];
var p2 = points[i+
1
];
context.
beginPath
();
context.
moveTo
(p1.x, p1.y);
context.
lineTo
(p2.x, p2.y);
context.
stroke
();
}
// Faster
context.
beginPath
();
for (var i =
0
; i < points.length -
1
; i++) {
var p1 = points[i];
var p2 = points[i+
1
];
context.
moveTo
(p1.x, p1.y);
context.
lineTo
(p2.x, p2.y);
}
context.
stroke
();
Canvas batch calls
// redraw entire canvas
context.fillRect(0, 0, canvas.width, canvas.height);
// redraw only 'dirty' areas
context.fillRect(last.x, last.y, last.width, last.height);
// faster way to redraw canvas
context.clearRect(0, 0, canvas.width, canvas.height)
Canvas Redraw Regions
function
animate
(time){
//random drawing
context.
clearRect
(
0
,
0
, canvas.width, canvas.height);
context.
beginPath
();
context.
rect
(x, y,
50
,
50
);
context.fillStyle =
'#8ED6FF'
;
context.
fill
();
// Old way:
// window.setTimeout(animate, 1000/60);
// New way:
window.
requestAnimationFrame
(animate);
}
RequestAnimationFrame
var bstyle = document.body.style;
// cache
bstyle.padding =
"20px"
;
// reflow, repaint
bstyle.border =
"10px solid red"
;
// another reflow and a repaint
bstyle.color =
"blue"
;
// repaint only, no dimensions changed
bstyle.backgroundColor =
"#fad"
;
// repaint
bstyle.fontSize =
"2em"
;
// reflow, repaint
// new DOM element - reflow, repaint
document.body.
appendChild
(document.
createTextNode
(
'hello...'
));
Reflows and repaints
// Slower
var elem = document.
getElementById
(
'animated'
);
elem.style.fontSize = (elem.offsetWidth /
10
) +
'px'
;
elem.firstChild.style.marginleft = (elem.offsetWidth /
20
) +
'px'
;
// Faster
var elem = document.
getElementById
(
'animated'
),
elemWidth = elem.offsetWidth;
elem.style.fontSize = (elemWidth /
10
) +
'px'
;
elem.firstChild.style.marginleft = (elemWidth /
20
) +
'px'
;
Minimize dimension or location queries
function SomeObject() { var self = this;
this.lastExecThrottle = 500; // limit to one call every "n" msec
this.lastExec = new Date();
this.timer = null;
this.resizeHandler = function() {
var d = new Date();
if (d-self.lastExec < self.lastExecThrottle) { if (self.timer) {
window.clearTimeout(self.timer);
}
self.timer = window.setTimeout(self.resizeHandler, self.lastExecThrottle);
return false; // exit
}
self.lastExec = d; // update "last exec" time
self.callResizeHandlerFunctions(); }
}
var someObject = new SomeObject();
window.onresize = someObject.resizeHandler;