The Latest in HTML

Eric Bidelman ( G+, @ebidel )
Developer Relations Brown Bag

( View this presentation in a nightly build of Chrome, FF, or WebKit. Also, some of this presentation does require Chrome 14+ dev. )

There's a lot this presentation doesn't cover!

http://www.htmlfivewow.com

OK...then what does it cover?

Semantics & Markup

<details> / <summary>

<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>
Information

If your browser supports this element, it should allow you to expand and collapse these details.

<output>

<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>
0100 + =

<mark>

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.

Speech input

    <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);

Core Platform ( JS )

Element Properties

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 latter

Define, 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, XD

  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';
  


Global Methods & Properties

matchMedia() - form-factor detection

Testing 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 animating

Common 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 */);

Example - requestAnimationFrame

function 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);

window.performance - know the numbers

Events

Page Visibility API & Prerendering- keep things transparent

Prerendering 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.
};

Capabilities

Pasting files

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 )

Custom (web-based) protocol handlers

  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
send data

( chrome://settings/handlers )

Typed Arrays

Example - Audio Synthesis ( 1 )

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.

Original source
WAVE PCM file format

Example - Audio Synthesis ( 2 )

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');
}

Example - Audio Synthesis ( 3 )

// 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

Bleeding Edge

Web Audio API

Comparison to Mozilla's Audio Data API

Scheduled playback

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:

Audio processing

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); });
};

Generating audio

Coming attractions...

Spellcheck API - no more mistakes!

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/

Fullscreen API

Enable fullscreen on a element. Still a work in progress.

Element.webkitRequestFullScreen();
Element.webkitRequestFullScreenWithKeys();
document.webkitCancelFullScreen(); // only on Document

Example usage - Fullscreen API

<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 moment

Camera, 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.
  });
};

WebRTC - fo realz...time

Too much?

chromestatus.com

updates.html5rocks.com

Tools