Is HTML5 ready for production?
Hi, I’m Remy @rem remy@leftlogic.com I <3 JavaScript Questions: interrupt & ask!
There's a lot more down here.
HTML5 is a spec
sort of "HTML5" is a ^brand
MOAR!!! "HTML5" Geolocation Web Workers Web Sockets Web SQL Databases Web Storage Offline applications HTML5 Offline events Canvas Video Web Forms
H TM L5 N OT NOT HTM L5
CSS != HTML But maybe we should have been more careful
caniuse.com
When can I use "HTML5"?
Making it work in the "other" browser
Polyfill A shim that mimics a future API providing a fallback to older browsers http://goo.gl/0Z9eI
Tools Modernizr to detect support yepnode.js to conditionally load (available as part of Modernizr)
Tools Modernizr.load({ test: Modernizr.geolocation, nope: 'geo-polyfill.js', complete: initMyGeoApp });
Oh, and learn JavaScript
Web Storage
Cookies suck.
Not the edible ones, duh. Cookies suck.
The code for cookies is a pain - I always google it.
"Session" cookies leak across sessions.
Persistent cookies require special date format
Deleting a cookie, isn't really deleting, but setting in the past.
Varying degrees of limitations on size and number.
Fuck cookies.
Sexy Web Storage FTW
One Interface localStorage sessionStorage http://dev.w3.org/html5/webstorage/
localStorage • Persists • Applied to document origin, i.e. scheme/host/port tuple • No expiry
sessionStorage • Lasts whilst on the document origin • Doesn't leak • Exactly the same API as localStorage
5mb? Done! o/ However: utf-16 ∴ 2½Mb
API void setItem(key, value) any* getItem(key) void removeItem(key) string key(index) void clear() readonly number length
var store = sessionStorage; Play store.setItem('name','rem'); store.getItem('name'); store.removeItem('name'); Do it in the console!
API setter setItem getter getItem deleter removeItem
var store = sessionStorage; Play store.name = 'rem'; store.name; delete store.name; Do it in the console!
t ip There's no security protecting the API // Safari debugger broken: ss.setItem('key', 12);
Values are strings Work around: JSON (and http://www.json.org/json2.js)
Play var store = sessionStorage, user = { screen_name : ‘rem’, rating : 11 }; store.user = JSON.stringify(user)); var gotUser = JSON.parse(store.user); alert(gotUser.screen_name);
Events too window.addEventListener('storage', function (event) { console.log(event); }, false); http://jsbin.com/ahafa3
Storage in all browsers
window.name
sessionStorage = (function () { var data = window.name ? JSON.parse(window.name) : {}; return { clear: function () { data = {}; window.top.name = ''; }, getItem: function (key) { return data[key] || null; }, removeItem: function (key) { delete data[key]; window.top.name = JSON.stringify(data); }, setItem: function (key, value) { data[key] = value; window.top.name = JSON.stringify(data); } }; })(); http://gist.github.com/350433
t ip Firefox cookie security applies to Storage too :(
t ip var cookiesEnabled = (function () { // the id is our test value var id = +new Date(); // generate a cookie to probe cookie access document.cookie = '__cookieprobe=' + id + ';path=/'; // if the cookie has been set, then we're good return (document.cookie.indexOf(id) !== -1); })();
Web SQL Databases http://flic.kr/p/drmCJ
IndexedDB http://flic.kr/p/5KXFwB
Questions?
Canvas Cooler than this guy.
Canvas SVG
http://vimeo.com/6691519
• It's not one is better than the other, they do different things • Select canvas when it makes sense • Don't assume interactive means canvas • Check out raphaeljs.com
http://mrdoob.com
canvas-1.html Play <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Canvas</title> </head> <body> <canvas></canvas> </body> </html> http://dev.w3.org/html5/canvas-api/canvas-2d-api.html
2D API ctx = canvas.getContext('2d')
Start Simple
fillRect, strokeRect & colours
ctx.fillRect(10, 10, 100, 100);
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00';
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00'; ctx.fillRect(100, 75, 50, 50);
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00'; ctx.fillRect(100, 75, 50, 50); ctx.strokeStyle = '#0c0';
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00'; ctx.fillRect(100, 75, 50, 50); ctx.strokeStyle = '#0c0'; ctx.lineWidth = 5;
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00'; ctx.fillRect(100, 75, 50, 50); ctx.strokeStyle = '#0c0'; ctx.lineWidth = 5; ctx.arc(100,50,25,0,Math.PI*2,true);
ctx.fillRect(10, 10, 100, 100); ctx.fillStyle = '#c00'; ctx.fillRect(100, 75, 50, 50); ctx.strokeStyle = '#0c0'; ctx.lineWidth = 5; ctx.arc(100,50,25,0,Math.PI*2,true); ctx.stroke();
t ip Math.PI == 180°
t ip Math.PI *2 == 360°
t ip d * Math.PI / 180 == radian
t ip CSS stretches
Gradient fills
1. Create a gradient object 2.Add colour stops 3.Set the gradient to the fillStyle 4.Draw
var w = canvas.width, h = canvas.height; var gradient = ctx.createLinearGradient(0, 0, w, h);
var w = canvas.width, h = canvas.height; var gradient = ctx.createLinearGradient(0, 0, w, h); gradient.addColorStop(0, '#fff'); gradient.addColorStop(0.5, '#f00'); gradient.addColorStop(1, '#000');
var w = canvas.width, h = canvas.height; var gradient = ctx.createLinearGradient(0, 0, w, h); gradient.addColorStop(0, '#fff'); gradient.addColorStop(0.5, '#f00'); gradient.addColorStop(1, '#000'); ctx.fillStyle = gradient;
var w = canvas.width, h = canvas.height; var gradient = ctx.createLinearGradient(0, 0, w, h); gradient.addColorStop(0, '#fff'); gradient.addColorStop(0.5, '#f00'); gradient.addColorStop(1, '#000'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, w, h);
Play createRadialGradient(x0,y0,r0,x1,y1,r1)
Pattern Fills
var img = new Image(); img.src = url; var pattern = ctx.createPattern(img,'repeat'); ctx.fillStyle = pattern; ctx.fillRect(0, 0, w, h);
t ip invalid_state img.onload = function () { // now use image for canvas };
Playing with paths
• For drawing irregular shapes • Can be filled • ...or stroked.
lineWidth rect(x,y,h,w) lineTo(x,y) arc(x,y,r,s,e, anticw) moveTo(x,y) fill() beginPath() stroke() closePath()
drawImage
<img> <canvas> <video> <canvas>
drawImage(src, dx, dy) drawImage(src, dx, dy, dw, dh) drawImage(src, sx, sy, sw, sh, dx, dy, dw, dh)
Play img.onload = function () { // this == img canvas.width = this.width; canvas.height = this.height; ctx.drawImage(this, 0, 0); }; canvas-10.html
pixel pushing
ctx.getImageData(0,0,w,h)
ctx.getImageData(0, 0, w, h); 0 1 2 3 i = 0 r g b a i = 1 r g b a i... r g b a
pixels.data[i * 4 + 0]; 0 1 2 3 i = 0 r g b a i = 1 r g b a i... r g b a
pixels.data[i * 4 + 1]; 0 1 2 3 i = 0 r g b a i = 1 r g b a i... r g b a
pixels.data[i * 4 + 2]; 0 1 2 3 i = 0 r g b a i = 1 r g b a i... r g b a
pixels.data[i * 4 + 3]; 0 1 2 3 i = 0 r g b a i = 1 r g b a i... r g b a
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { }
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { This says loop over each pixel }
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { // red: pixels.data[i+0] }
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { // red: pixels.data[i+0] // green: pixels.data[i+1] }
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { // red: pixels.data[i+0] // green: pixels.data[i+1] // blue: pixels.data[i+2] }
var pixels = ctx.getImageData(0, 0, w, h), l = pixels.data.length, i; for (i = 0; i < l; i += 4) { // red: pixels.data[i+0] // green: pixels.data[i+1] // blue: pixels.data[i+2] // alpha: pixels.data[i+3] }
ctx.putImageData(pixels, 0, 0)
ctx.putImageData(pixels, 0, 0) Needs to be a CanvasPixelArray object
t ip security_err To use getImageData, your document must be served over http (or https) - i.e. it doesn't work offline.
/workshop/authors-large.jpg Play greyscale = r*.3 + g*.59 + b*.11; r = g = b = greyscale; saturation = (Math.max(r,g,b) + Math.min(r,g,b))/2; r = g = b = saturation; http://jsbin.com/aguho3/2/edit
canvas.toDataURL('image/png');
Play save.onclick = function () { window.open( canvas.toDataURL('image/png') ); }; canvas-13.html
Questions?
Offline Applications
Offline Apps • Application cache / manifest • Events: offline, online • navigator.onLine property
http://icanhaz.com/rubiks
Using a Manifest <!DOCTYPE html> <html manifest="my.appcache"> <body> <!-- my page --> </body> </html>
my.appcache CACHE MANIFEST app.html css/style.css js/app.js #version 13
The Manifest 1. Serve as text/manifest, by adding to mime.types: text/cache-manifest appcache
t ip Firefox caching <IfModule mod_expires.c> ExpiresActive on ExpiresByType text/cache-manifest ↪ “access plus 0 seconds” </IfModule>
The Manifest 2. First line must be: CACHE MANIFEST
The Manifest 3. Including page is implicitly included in the cache.
The Manifest 4. Two futher namespaces: NETWORK & FALLBACK FALLBACK: / offline.html
CACHE MANIFEST / index.html range.js datastore.js FALLBACK: # force everything through # the index url / / # this won't match # anything unless it's on # another domain NETWORK: * # v4
CACHE MANIFEST / index.html Served from cache range.js datastore.js FALLBACK: # force everything through # the index url / / # this won't match # anything unless it's on # another domain NETWORK: * # v4
CACHE MANIFEST / index.html range.js Requests for files not datastore.js found in the cache, are FALLBACK: # force everything through directed to / # the index url / / i.e. index.html # this won't match (when offline). # anything unless it's on # another domain NETWORK: * # v4
CACHE MANIFEST / index.html range.js datastore.js Any requests to urls FALLBACK: that don't match / - # force everything through # the index url i.e. on another / / domain, will be # this won't match # anything unless it's on served through the # another domain NETWORK: web. * # v4
CACHE MANIFEST / index.html range.js datastore.js FALLBACK: # force everything through # the index url / / Also ensures # this won't match browser even # anything unless it's on # another domain attempts to load the NETWORK: * asset # v4
CACHE MANIFEST / index.html range.js datastore.js FALLBACK: # force everything through # the index url / / The contents of the # this won't match manifest must # anything unless it's on # another domain change to trigger an NETWORK: * update # v4
The Manifest 5. Include some versioning to cache bust your manifest # version 16
The process
Browser: I have a Browser: request Server: serve all manifest, cache assets Browser: Server: serve applicationCache Browser: reload manifest assets updated Browser: only Browser: serve Server: 304 Not request manifest locally Modified file
Browser: I have a Problem: serve all Browser: requestServer: manifest, cache assets Change of content requires 2 refreshes reload Server: serve Browser: applicationCache Browser: manifest assets updated Browser: only Browser: serve Server: 304 Not request manifest locally Modified file
document.body.onOnline = function () { // fire an update to the cache applicationCache.update(); };
applicationCache.onUpdateReady = function () { if (confirm("New version ready. Refresh?")) { // reload window.location.refresh(); } };
t ip Do offline last
Questions?
Web Sockets
Native or polyfilled
http://github.com/gimite/web-socket-js/
new WebSocket(url) http://dev.w3.org/html5/websockets/
ws://node.remysharp.com:8000 http://github.com/miksago/node-websocket-server
onopen onmessage onclose onerror
var data = JSON.parse(event.data);
.send
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); };
var url = 'ws://node.remysharp.com:8000', conn = new WebSocket(url); conn.onopen = function () { conn.send('hello world'); }; conn.onmessage = function (event) { console.log(event.data); }; Play
Questions? To contact me after my presentation – text NHT to INTRO (46876) Or -- remy@leftlogic.com @rem

Is HTML5 Ready? (workshop)