The Latest in HTML 
Eric Bidelman ( G+, @ebidel )
Developer Relations Brown Bag

Eric Bidelman ( G+, @ebidel )
Developer Relations Brown Bag
<details open="open"> <summary>Information</summary> <p>If your browser supports this element, it should allow you to expand and collapse these details.</p> </details>
If your browser supports this element, it should allow you to expand and collapse these details.
<form oninput="result.value=a.valueAsNumber + b.valueAsNumber"> 0<input type="range" name="b">100 +<input type="number" name="a"> = <output name="result"></output> </form>
Semantically highlight parts of your text:
Lorem ipsum dolor, <mark>consectetur</mark> adipiscing...
Lorem ipsum dolor, consectetur adipiscing elit. Maecenas egestas facilisis lectus a sagittis. Integer imperdiet nisl non tortor blandit lobortis at eu justo. Etiam viverra nunc id dolor hendrerit vestibulum lobortis tellus ornare.
<input type="text" x-webkit-speech>
function inputChange(e) {
if (e.results) { // e.type == 'webkitspeechchange'
for (var i = 0, result; result = e.results[i]; ++i) {
console.log(result.utterance, result.confidence);
}
console.log('Best result: ' + this.value);
}
}
var input = document.querySelector('[x-webkit-speech]');
input.addEventListener('change', inputChange, false);
input.addEventListener('webkitspeechchange', inputChange, false);
Element.classList - cut the cord ( library deps )<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>
Element.dataset - stash things for latterDefine, store, and retrieve custom data on the DOM.
<p id="out" data-id="good" data-name="joe" data-screen-name="user1"></p>
var el = document.querySelector('#out');
// Add new data attributes via JS.
el.dataset.foo = 'bar'; // == 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
Element.matchesSelector() - If the shoe fits, wear it!Check if an element matches a given CSS selector:
<div id="main" class="active" data-type="tweet">140 chars</div>
document.querySelector('div').matchesSelector('[data-type="tweet"]') // == true
Note: [webkit|moz|ms]MatchesSelector
MediaElement.crossOrigin - read pixels, XDSECURITY_ERR )
using CORS.
var img = document.createElement('img');
img.onload = function(e) {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var url = canvas.toDataURL(); // Succeeds. Canvas won't be dirty.
};
img.crossOrigin = 'anonymous';
img.src = 'http://other-domain.com/image.jpg';
matchMedia() - form-factor detectionTesting CSS media queries in JS:
if (window.matchMedia('only screen and (max-device-width: 480px)').matches) {
// Asynchronously load iphone.js
} else if (window.matchMedia('only screen and (min-device-width: 481px) and ' +
'(max-device-width: 1024px) and ' +
'(orientation: portrait)').matches) {
// Asynchronously load ipad-portrait.js
}
You are on a:

( resize window )
requestAnimationFrame - Tell the browser you're animatingCommon technique for JS animations:
window.setInterval(function() {
// move elem
}, 1000 / 60);
window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
function animate(time) { // time is the Unix time.
// move element.
window.requestAnimationFrame(animate, opt_elem);
}
window.requestAnimationFrame(animate, opt_elem /* bounding elem */);
display:none )requestAnimationFramefunction draw() {
var now = new Date().getTime();
updateModels(now - last);
last = now;
paintScene(canvas);
window.setTimeout(draw, 10);
});
draw();
function draw(future) {
updateModels(future - last);
last = future;
paintScene(canvas);
window.requestAnimationFrame(draw, canvas);
};
draw();
window.crypto - pseudorandom number generator// Fill typed array with 5 8-bit unsigned random integers. var uInt8Arr = new Uint8Array(5); window.crypto.getRandomValues(uInt8Arr);
/dev/random):Math.random(), which is seeded by current time.NOT_SUPPORTED_ERR thrown if UA is unable to obtain "true" randomnesswindow.performance - know the numbersperformance.timingperformance.navigationperformance.memory - via --enable-memory-info flagPrerendering pages ( test ):
<link rel="prerender" href="http://example.org/index.html">
Determine if your app is visible or not:
document.addEventListener('visibilitychange', function(e) {
console.log('hidden:' + document.hidden,
'state:' + document.visibilityState)
}, false);
Demo: change tabs
navigator.onLine - know when you're all alone
if (navigator.onLine) {
console.log('ONLINE!');
} else {
console.log('Connection flaky');
}
window.addEventListener('online', function(e) {
// Re-sync data with server.
}, false);
window.addEventListener('offline', function(e) {
// Queue up events for server.
}, false);
window.onerror - let nothing go unnoticed
window.onerror = function(msg, url, line) {
// Track JS errors in GA.
_gaq.push(['_trackEvent', 'Error Log', msg, url + '_' + line]);
// Log to your server.
var xhr = new XMLHttpRequest();
xhr.open('POST', '/jserror', true);
xhr.send('Line ' + num + ': (' + url + ') - ' + msg);
return false; // false prevents default error handling.
};
document.body.onpaste = function(e) {
var items = e.clipboardData.items;
for (var i = 0; i < items.length; ++i) {
if (items[i].kind == 'file' && items[i].type == 'image/png') {
var blob = items[i].getAsFile();
var img = document.createElement('img');
img.src = window.URL.createObjectURL(blob);
document.body.appendChild(img);
}
}
};
Click in this window and paste and image ( Copy image from a right-click )
navigator.registerProtocolHandler(
'web+myscheme', 'http://example.com/handler?q=%s', 'My App');

<a href="web+myscheme:some%20data%20here">send data</a>
GET http://example.com/handler?q=web+myscheme:some%20data%20here
ArrayBuffer - chunk of bytes in memory. Create "views" from it:
Int8Array, Uint8ArrayInt16Array, Uint16ArrayInt32Array, Uint32ArrayFloat32Array, Float64ArrayArrayBuffer source:
var uInt8Array = new Uint8Array(buffer); var byte3 = uInt8Array[4]; // byte at offset 4. var uInt32Array = new Uint32Array(buffer);
ArrayBufferView - view of a portion of an ArrayBufferDataView - an ArrayBufferView that understands endianness:
var data = new DataView(buffer); var byte3 = data.getUint8(4); var i = data.getInt32(0, false); // Big-endian
var header = new Uint8Array([
0x52,0x49,0x46,0x46, // "RIFF"
0, 0, 0, 0, // put total size here
0x57,0x41,0x56,0x45, // "WAVE"
0x66,0x6d,0x74,0x20, // "fmt "
16,0,0,0, // size of the following
1, 0, // PCM format
1, 0, // Mono: 1 channel
0x44,0xAC,0,0, // 44,100 samples per second
0x88,0x58,0x01,0, // byte rate: two bytes per sample
2, 0, // aligned on every two bytes
16, 0, // 16 bits per sample
0x64,0x61,0x74,0x61, // "data"
0, 0, 0, 0 // put number of samples here
]).buffer; // Note: we just want the ArrayBuffer.
function makeWave(samples) {
var bb = new WebKitBlobBuilder();
var dv = new DataView(header);
dv.setInt32(4, 36 + samples.length, true);
dv.setInt32(40, samples.length, true);
bb.append(header);
bb.append(samples.buffer);
return bb.getBlob('audio/wav');
}
// Play a note of the specifed frequency and duration
// We've hardcoded 20,000 samples per second
function playNote(frequency, duration) {
var samplespercycle = 44100 / frequency;
var samples = new Uint16Array(44100 * duration);
var da = 2 * Math.PI / samplespercycle;
for (var i = 0, a = 0; i < samples.length; i++, a += da) {
samples[i] = Math.floor(Math.sin(a) * 32768);
}
var blob = makeWave(samples);
var url = window.webkitURL.createObjectURL(blob);
var player = new Audio(url);
player.play();
player.addEventListener('ended', function(e) {
window.webkitURL.revokeObjectURL(url);
}, false);
}
Hz
seconds

window.webkitAudioContext() in Chrome 14.
var ctx = new window.AudioContext();
function playSound(arrayBuffer) { // Obtain arrayBuffer from XHR.
ctx.decodeAudioData(arrayBuffer, function(buffer) {
var src = ctx.createBufferSource();
src.buffer = buffer;
src.looping = false;
src.connect(ctx.destination);
src.noteOn(0); // Play immediately.
}, function(e) {
console.log(e);
});
}
Shoot:
var ctx = new AudioContext();
var analyser = ctx.createAnalyser();
var jsNode = ctx.createJavaScriptNode(2048 /*samples*/,
1 /*inputs*/, 1 /*outputs*/);
jsNode.onaudioprocess = function(e) {
var byteData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(byteData);
// render visualization of byteData.
};
function initAudio(arrayBuffer) {
ctx.decodeAudioData(arrayBuffer, function(buffer) {
var src = ctx.createBufferSource();
src.buffer = buffer;
src.connect(analyser); // src -> analyser -> js node -> dest
analyser.connect(jsNode);
jsNode.connect(ctx.destination);
}, function(e) { console.log('Error decoding audio file', e); });
};
Supply suggestions for mispelled ← words.
void addSpellcheckRange(unsigned long start, unsigned long length,
DOMStringList suggestions[, unsigned short options]);
void removeSpellcheckRange(SpellcheckRange range);
var input = document.querySelector('input');
input.addSpellcheckRange(
4, 9, ['Chrome', 'Firefox', 'Opera', 'Internet Explorer']);
More info: peter.sh/experiments/spellcheck-api/
Enable fullscreen on a element. Still a work in progress.
Element.webkitRequestFullScreen(); Element.webkitRequestFullScreenWithKeys(); document.webkitCancelFullScreen(); // only on Document
allowFullScreen attribute added for <iframes>-webkit-full-screen:-webkit-full-screen-doc:-webkit-full-screen-root-with-target
<video width="300" src="movie.webm" controls></video> <button onclick="enterFullscreen()">Get Huge!</button>
function enterFullscreen() {
var elem = document.getElementById("fs");
elem.onwebkitfullscreenchange = function(e) {
console.log("Entered fullscreen!");
document.getElementById("exit").style.display="inline";
elem.onwebkitfullscreenchange = onFullscreenExit;
};
elem.webkitRequestFullScreen();
}
navigator.getUserMedia() - create a momentCamera, microphone, device access.
<video autoplay controls></video>
var mediaStream, recorder;
if (navigator.getUserMedia) {
var successCallback = function(stream) {
document.querySelector('video').src = stream;
mediaStream = stream;
};
var errorCallback = function(e) {
console.log(e);
};
navigator.getUserMedia('video', successCallback, errorCallback);
}
var record = function() {
recorder = mediaStream.recorder();
};
var stop = function() {
mediaStream.stop();
recorder.getRecordedData(function(blob) {
// Upload blob using XHR2.
});
};
