This presentation is an HTML5 website
Press → key to advance.
Having issues seeing the presentation? Read the disclaimer
*Including other next generation technologies of the Web Development stack
Expect the unexpected
// use localStorage for persistent storage
// use sessionStorage for per tab storage
saveButton.addEventListener('click', function () {
window.localStorage.setItem('value', area.value);
window.localStorage.setItem('timestamp', (new Date()).getTime());
}, false);
textarea.value = window.localStorage.getItem('value');
Save text value on the client side (crash-safe)
var db = window.openDatabase("DBName", "1.0", "description", 5*1024*1024); //5MB
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM test", [], successCallback, errorCallback);
});
See the generated database: Developer > Developer Tools > Storage
var idbRequest = window.indexedDB.open('Database Name');
idbRequest.onsuccess = function(event) {
var db = event.srcElement.result;
var transaction = db.transaction([], IDBTransaction.READ_ONLY);
var curRequest = transaction.objectStore('ObjectStore Name').openCursor();
curRequest.onsuccess = ...;
};
<html manifest="cache.appcache">
window.applicationCache.addEventListener('updateready', function(e) {cache.appcache:
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
}
}, false);
CACHE MANIFEST
# version 1.0.0
CACHE:
/html5/src/logic.js
/html5/src/style.css
/html5/src/background.png
NETWORK:
*
Turn off your internet connection and refresh this page!
// Request Status
webkitStorageInfo.queryUsageAndQuota(webkitStorageInfo.TEMPORARY, function(used, remaining) {
console.log("Used quota: " + used + ", remaining quota: " + remaining);
}
);
// Request Quota (only for File System API)
webkitStorageInfo.requestQuota(webkitStorageInfo.PERSISTENT, 10 * 1024 * 1024, function(used) {
console.log("Used quota: " + used + ", remaining quota: " + remaining);
}
);
Default (Temporary) | Quota Requested (Persistent) | |
---|---|---|
Web Storage | 5Mb | N/A |
App Cache | 10% of available disk in total Used: BytesRemaining: Bytes | |
IndexedDB | ||
WebSQL | ||
File System API | Arbitrary in Bytes Used: Bytes |
Stay connected
var worker = new Worker('task.js');task.js:
worker.onmessage = function(event) { alert(event.data); };
worker.postMessage('data');
self.onmessage = function(event) {
// Do some work.
self.postMessage("recv'd: " + event.data);
};
Loading Route...
Try dragging the map while the complex route is being calculated (you will only be able to do that with Workers!)
var socket = new WebSocket('ws://html5rocks.websocket.org/echo');
socket.onopen = function(event) {
socket.send('Hello, WebSocket');
};
socket.onmessage = function(event) { alert(event.data); }
socket.onclose = function(event) { alert('closed'); }
Full-duplex, bi-directional communication over the Web: Both the server and client can send data at any time, or even at the same time. Only the data itself is sent, without the overhead of HTTP headers, dramatically reducing bandwidth.
if (window.webkitNotifications.checkPermission() == 0) {
// you can pass any url as a parameter
window.webkitNotifications.createNotification(tweet.picture, tweet.title,
tweet.text).show();
} else {
window.webkitNotifications.requestPermission();
}
Note: Use this button if you also want to reset the permissions
Enter your twitter user name to show your last tweet as a notification
Deeper integration with the Operating System
document.addEventListener('dragstart', function(event) {
event.dataTransfer.setData('text', 'Customized text');
event.dataTransfer.effectAllowed = 'copy';
}, false);
document.querySelector('#dropzone').addEventListener('drop', function(e) {
var reader = new FileReader();
reader.onload = function(evt) {
document.querySelector('img').src = evt.target.result;
};
reader.readAsDataURL(e.dataTransfer.files[0]);
}, false);
<a href="src/star.mp3" draggable="true" class="dragout"
data-downloadurl="MIMETYPE:FILENAME:ABSOLUTE_URI_TO_FILE">download</a>
var files = document.querySelectorAll('.dragout');
for (var i = 0, file; file = files[i]; ++i) {
file.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('DownloadURL', this.dataset.downloadurl);
}, false);
}
( this feature is only available in Google Chrome )
window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function(fs) {
// fs.root is a DirectoryEntry object.
fs.root.getFile('log.txt', {create: true}, function(fileEntry) {
fileEntry.createWriter(function(writer) { // writer is a FileWriter object.
writer.onwrite = function(e) { ... };
writer.onerror = function(e) { ... };
var bb = new BlobBuilder();
bb.append('Hello World!');
writer.write(bb.getBlob('text/plain'));
}, opt_errorHandler);
}
}, opt_errorHandler);
( The FileSystem API is currently only implemented in Google Chrome 9+ )
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var latLng = new google.maps.LatLng(
position.coords.latitude, position.coords.longitude);
var marker = new google.maps.Marker({position: latLng, map: map});
map.setCenter(latLng);
}, errorHandler);
}
window.addEventListener('deviceorientation', function(event) {
var a = event.alpha;
var b = event.beta;
var g = event.gamma;
}, false);
This sample requires FF3.6+ or Google Chrome on a device with a built in accelerometer or iOS 4.2.
<input type="text" x-webkit-speech />
Speech input is not enabled in your browser.
Try running Google Chrome with the --enable-speech-input
flag.
More meaningful elements
<body>
<header>
<hgroup>
<h1>Page title</h1>
<h2>Page subtitle</h2>
</hgroup>
</header>
<nav>
<ul>
Navigation...
</ul>
</nav>
<section>
<article>
<header>
<h1>Title</h1>
</header>
<section>
Content...
</section>
</article>
<article>
<header>
<h1>Title</h1>
</header>
<section>
Content...
</section>
</article>
</section>
<aside>
Top links...
</aside>
<figure>
<img src="..."/>
<figcaption>Chart 1.1</figcaption>
</figure>
<footer>
Copyright ©
<time datetime="2010-11-08">2010</time>.
</footer>
</body>
<input list="cars"/>
<datalist id="cars">
<option value="BMW"/>
<option value="Ford"/>
<option value="Volvo"/>
</datalist>
<menu>
<command type="command" disabled label="Publish" />
</menu>
<details>
<summary>HTML 5</summary>
This slide deck teaches you everything you need to know about HTML 5.
</details>HTML 5
This slide deck teaches you everything you need to know about HTML 5.
<meter min="0" max="100" low="40" high="90" optimum="100" value="91">A+</meter>
Your score is:A+
<progress>working...</progress>
Download is:
<progress value="75" max="100">3/4 complete</progress>
Goal is:
<link rel="alternate" type="application/rss+xml" href="http://myblog.com/feed"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="pingback" href="http://myblog.com/xmlrpc.php"/>
<link rel="prefetch" href="http://myblog.com/main.php"/>
...
<a rel="archives" href="http://myblog.com/archives">old posts</a>
<a rel="external" href="http://notmysite.com">tutorial</a>
<a rel="license" href="http://www.apache.org/licenses/LICENSE-2.0">license</a>
<a rel="nofollow" href="http://notmysite.com/sample">wannabe</a>
<a rel="tag" href="http://myblog.com/category/games">games posts</a>
...
<div itemscope itemtype="http://example.org/band">
<p>My name is <span itemprop="name">Neil</span>.</p>
<p>My band is called <span itemprop="band">Four Parts Water</span>.</p>
<p>I am <span itemprop="nationality">British</span>.</p>
</div>
<ul id="tree1"
role="tree"
tabindex="0"
aria-labelledby="label_1">
<li role="treeitem" tabindex="-1" aria-expanded="true">Fruits</li>
<li role="group">
<ul>
<li role="treeitem" tabindex="-1">Oranges</li>
<li role="treeitem" tabindex="-1">Pineapples</li>
...
</ul>
</li>
</ul>
<style>
[required] {
border-color: #88a;
-webkit-box-shadow: 0 0 3px rgba(0, 0, 255, .5);
}
:invalid {
border-color: #e88;
-webkit-box-shadow: 0 0 5px rgba(255, 0, 0, .8);
}
</style>
<input type="text" required />
<input type="email" value="some@email.com" />
<input type="date" min="2010-08-14" max="2011-08-14" value="2010-08-14"/>
<input type="range" min="0" max="50" value="10" />
<input type="search" results="10" placeholder="Search..." />
<input type="tel" placeholder="(555) 555-5555"
pattern="^\(?\d{3}\)?[-\s]\d{3}[-\s]\d{4}.*?$" />
<input type="color" placeholder="e.g. #bbbbbb" />
<input type="number" step="1" min="-5" max="10" value="0" />
type="text"
type="number"
type="email"
type="tel"
2D & 3D
<audio id="audio" src="sound.mp3" controls></audio>
document.getElementById("audio").muted = false;
<video id="video" src="movie.webm" autoplay controls></video>
document.getElementById("video").play();
<video width="390" id="clip" controls>
<source src="Google_Developer_Stories.webm"
type='video/webm; codecs="vp8, vorbis"' />
<track label="English subtitles" kind="subtitles" srclang="en"
src="video-subtitles-en.vtt" default />
</video>
if (elem.webkitRequestFullScreen) {
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.requestFullScreen){
elem.requestFullScreen();
}
:-webkit-full-screen-ancestor:root {
overflow: hidden;
}
:-webkit-full-screen-ancestor {
z-index: auto;
-webkit-transform: none;
-webkit-transition: none;
}
pre:-webkit-full-screen {
background-color: white;
}
<canvas id="canvas" width="838" height="220"></canvas>
<script>
var canvasContext = document.getElementById("canvas").getContext("2d");
canvasContext.fillRect(250, 25, 150, 100);
canvasContext.beginPath();
canvasContext.arc(450, 110, 100, Math.PI * 1/2, Math.PI * 3/2);
canvasContext.lineWidth = 15;
canvasContext.lineCap = 'round';
canvasContext.strokeStyle = 'rgba(255, 127, 0, 0.5)';
canvasContext.stroke();
</script>
<canvas id="canvas" width="838" height="220"></canvas>
<script>
var gl = document.getElementById("canvas").getContext("experimental-webgl");
gl.viewport(0, 0, canvas.width, canvas.height);
...
</script>
<html>
<svg width="300px" height="300px">
<defs>
<linearGradient id="myGradient" x1="0%" y1="100%" x2="100%" y2="0%">
<stop offset="5%" stop-color="red"></stop>
<stop offset="95%" stop-color="blue" stop-opacity="0.5"></stop>
</linearGradient>
</defs>
<circle id="myCircle" class="important" cx="50%" cy="50%" r="100"
fill="url(#myGradient)" onmousedown="alert('hello');"/>
</svg>
</html>
Presentation & Styling
.row:nth-child(even) {
background: #dde;
}
.row:nth-child(odd) {
background: white;
}
div {
display: inline-block;
}
input[type="text"] {
background: #eee;
}
:not(.box) {
color: #00c;
}
:not(span) {
display: block;
}
h2:first-child { ... }
div.text > div { ... }
h2 + header { ... }
@font-face {
font-family: 'LeagueGothic';
src: url(LeagueGothic.otf);
}
@font-face {
font-family: 'Droid Sans';
src: url(Droid_Sans.ttf);
}
header {
font-family: 'LeagueGothic';
}
div {
text-overflow: ellipsis;
}
Play with the slider on this and further pages!
-webkit-column-count: 2;
-webkit-column-rule: 1px solid #bbb;
-webkit-column-gap: 2em;
In March 1936, an unusual confluence of forces occurred in Santa Clara County.
A long cold winter delayed the blossoming of the millions of cherry, apricot, peach, and prune plum trees covering hundreds of square miles of the Valley floor. Then, unlike many years, the rains that followed were light and too early to knock the blossoms from their branches.
Instead, by the billions, they all burst open at once. Seemingly overnight, the ocean of green that was the Valley turned into a low, soft, dizzyingly perfumed cloud of pink and white. Uncounted bees and yellow jackets, newly born, raced out of their hives and holes, overwhelmed by this impossible banquet.
Then came the wind.
It roared off the Pacific Ocean, through the nearly uninhabited passes of the Santa Cruz Mountains and then, flattening out, poured down into the great alluvial plains of the Valley. A tidal bore of warm air, it tore along the columns of trees, ripped the blossoms apart and carried them off in a fluttering flood of petals like foam rolling up a beach.
This perfumed blizzard hit Stevens Creek Boulevard, a two-lane road with a streetcar line down its center, that was the main road in the West Valley. It froze traffic, as drivers found themselves lost in a soft, muted whiteout. Only the streetcar, its path predetermined, passed on...
div {
-webkit-text-fill-color: black;
-webkit-text-stroke-color: red;
-webkit-text-stroke-width: 0.00px;
}
color: rgba(255, 0, 0, 0.75);
background: rgba(0, 0, 255, 0.75);
color: hsla(
128
75%,
33%,
1.00);
face: border-radius: 0px;
left eye: border-radius: 0px;
right eye: border-radius: 0px;
base white: border-radius: 0px;
mouth: border-radius: 0px;
nose: border-radius: 0px;
left black eye: border-radius: 0px;
right black eye: border-radius: 0px;
background-image: linear-gradient(top, #00abeb 0%, #fff 50%,
#66cc00 50%, #fff 100%);
background-image: radial-gradient(center, circle cover, red, #000 40%);
text-shadow:
rgba(64, 64, 64, 0.5)
0px
0px
0px;
box-shadow:
rgba(0, 0, 128, 0.25)
0px
0px
0px;
text-shadow: rgba(0, 0, 0, 0.5) 0 0px 0px;
background:
-webkit-gradient(linear, left top, left bottom,
from(rgba(200, 200, 240, 0)), to(rgba(255, 255, 255, 0)));
border-radius: 0px;
-webkit-box-reflect: below 10px
-webkit-gradient(linear, left top, left bottom,
from(transparent), to(rgba(255, 255, 255, 0)));
#logo {
background: url(logo.gif) center center no-repeat;
background-size:
;
}
Resize me! »
div {
background: url(src/zippy-plus.png) 10px center no-repeat,
url(src/gray_lines_bg.png) 0 center repeat-x;
}
-webkit-border-image: url(border_image_1.png) ;
border-width: 10px;
.box {
display: -webkit-box;
-webkit-box-orient: ;
}
.box .one, .box .two {
-webkit-box-flex: 1;
}
.box .three {
-webkit-box-flex: 3;
}
.box {
display: -webkit-box;
-webkit-box-pack: ;
-webkit-box-align: ;
}
#box.left {
margin-left: 0;
}
#box.right {
margin-left: 1000px;
}
document.getElementById('box').className = 'left';
document.getElementById('box').className = 'right';
#box {
-webkit-transition: margin-left 1s ease-in-out;
}
document.getElementById('box').className = 'left';
document.getElementById('box').className = 'right';
Hover over me:
-webkit-transform: rotateY(45deg);
-webkit-transform: scaleX(25deg);
-webkit-transform: translate3d(0, 0, 90deg);
-webkit-transform: perspective(500px)
#threed-example {
-webkit-transform: rotateZ(5deg);
-webkit-transition: -webkit-transform 2s ease-in-out;
}
#threed-example:hover {
-webkit-transform: rotateZ(-5deg);
}
Now press 3!
@-webkit-keyframes pulse {
from {
opacity: 0.0;
font-size: 100%;
}
to {
opacity: 1.0;
font-size: 200%;
}
}
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;
}
*Please make a better use of it. We don't want a new blink tag ;)
Improvements to the core platform
var el = document.getElementById('section1');
el.focus();
var els = document.getElementsByTagName('div');
els[0].focus();
var els = document.getElementsByClassName('section');
els[0].focus();
var els = document.querySelectorAll("ul li:nth-child(odd)");
var tds = document.querySelectorAll("table.test > tr > td");
var el = document.querySelector("table.test > tr > td"); // el == tds[0]
<div id="out" data-id="good" data-name="joe" data-screen-name="user1"></div>
// Add new data attributes via JS.
var el = document.querySelector('#out');
el.setAttribute('data-foo', 'bar');
var html = [];
for (var key in el.dataset) {
html.push(key, ': ', el.dataset[key], '<br>');
}
el.innerHTML = html.join('');
Output:
id: good
name: joe
screenName: user1
foo: bar
<div id="main" class="shadow rounded"></div>
var el = document.querySelector('#main').classList;
el.add('highlight');
el.remove('shadow');
el.toggle('highlight');
console.log(el.contains('highlight')); // false
console.log(el.contains('shadow')); // false
console.log(el.classList.toString() == el.className); // true
Output:
<div id="main" class="rounded"></div>
link.addEventListener('click', function(event) {
// manually add a value to the history stack
// without making the browser load any new page
history.pushState('Contact Page Form', 'Contact Page', '/contact');
});
// capture navigation in case we want to change,
// for instance, some content when it changes
window.addEventListener('popstate', function(event) {
document.querySelector('h1').innerHTML = event.state; // 'Contact Page Form'
});
Client side:
<meta http-equiv="X-UA-Compatible" content="chrome=1">
Server side:
X-UA-Compatible: chrome=1
Try to load this presentation in IE!
HTML5 = Next Generation Features for Modern Web Development