HTML5 Rocks

HTML5 Rocks

Collecting and Iterating, the ES6 Way

By Jeff Posnick at

The ECMAScript 6 specification, while still in draft form, brings the promise of many exciting new tools to add to the JavaScript programmer’s belt. New classes such as Set and Map offer native solutions to working with specific types of collections, and the for...of statement provides an elegant alternative to traditional ways of iterating over data.

Sets offer a way of keeping track of a collection of items in which each item can appear at most once. Maps offer more functionality than was previously possible by using Objects to associate keys with values—with a Map, your keys don’t have to be strings, and you don’t have to worry about accidentally choosing a key name that clashes with an Object’s method names. Lookup operations on native Maps and Sets are done in constant time, which offers efficiency gains over what’s possible with simulated implementations.

The following sample demonstrates constructing a Set, and using for...of to iterate over its elements:

<pre id="log"></pre>

<script>
  function log() {
    document.querySelector('#log').textContent += Array.prototype.join.call(arguments, '') + '\n';
  }

  log('Creating, using, and iterating over a Set:');
  var randomIntegers = new Set();
  // Generate a random integer in the range [1..10] five times,
  // and use a Set to keep track of the distinct integers that were generated.
  for (var i = 0; i < 5; i++) {
    randomIntegers.add(Math.floor(Math.random() * 10) + 1);
  }
  log(randomIntegers.size, ' distinct integers were generated.');
  log('The number 10 was ', randomIntegers.has(10) ? '' : 'not ', 'one of them.');
  log('Here\'s all of them:');
  // Use for...of to iterate over the items in the Set.
  // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-iteration-statements
  // The Set iterator yields a single value corresponding to each entry in the Set.
  for (var item of randomIntegers) {
    log(item);
  }
</script>

Here’s a corresponding sample that illustrates using and iterating over a Map:

<script>
  log('\nCreating and iterating over a Map:');
  // Maps can be initialized by passing in an iterable value (https://people.mozilla.org/~jorendorff/es6-draft.html#sec-map-iterable)
  // Here, we use an Array of Arrays to initialize. The first value in each sub-Array is the new
  // Map entry's key, and the second is the item's value.
  var typesOfKeys = new Map([
    ['one', 'My key is a string.'],
    ['1', 'My key is also a string'],
    [1, 'My key is a number'],
    [document.querySelector('#log'), 'My key is an object']
  ]);
  // You can also call set() to add new keys/values to an existing Map.
  typesOfKeys.set('!!!!', 'My key is excited!');

  // Use for...of to iterate over the items in the Map.
  // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-iteration-statements
  // There are several types of Map iterators available.
  // typesOfKeys.keys() can be used to iterate over just the keys:
  log('Just the keys:');
  for (var key of typesOfKeys.keys()) {
    log('  key: ', key);
  }

  // typesOfKeys.values() can be used to iterate over just the values:
  log('Just the values:');
  for (var value of typesOfKeys.values()) {
    log('  value: ', value);
  }

  // The default Map iterator yields an Array with two items; the first is the Map entry's key and the
  // second is the Map entry's value. This default iterator is equivalent to typesOfKeys.entries().
  log('Keys and values:');
  // Alternative, ES6-idiomatic syntax currently supported in Safari & Firefox:
  // for (var [key, value] of typesOfKeys) { … } 
  for (var item of typesOfKeys) {
    log('  ', item[0], ' -> ', item[1]);
  }
</script>

Some browsers, like Chrome, Internet Explorer, and Firefox have already added support for Sets and Maps. Native support complemented by polyfill libraries like es6-collections or es6-shim means that JavaScript developers can start building with these new collection types today. There are no polyfills available for the for...of statement (though it is possible to transpile support via Traceur), but native support is available today in Chrome and Firefox.

Update, September 2014: Linked to an additional polyfill option, es6-shim

Easier ArrayBuffer <-> String conversion with the Encoding API

By Jeff Posnick at

Over two years ago, Renato Mangini described a method for converting between raw ArrayBuffers and the corresponding string representation of that data. At the end of the post, Renato mentioned that an official standardized API to handle the conversion was in the process of being drafted. The specification has now matured, and both Firefox and Google Chrome have added native support for the TextDecoder and TextEncoder interfaces.

As demonstrated by this live sample, excerpted below, the Encoding API makes it simple to translate between raw bytes and native JavaScript strings, regardless of which of the many standard encodings you need to work with.

<pre id="results"></pre>

<script>
  if ('TextDecoder' in window) {
    // The local files to be fetched, mapped to the encoding that they're using.
    var filesToEncoding = {
      'utf8.bin': 'utf-8',
      'utf16le.bin': 'utf-16le',
      'macintosh.bin': 'macintosh'
    };

    Object.keys(filesToEncoding).forEach(function(file) {
      fetchAndDecode(file, filesToEncoding[file]);
    });
  } else {
    document.querySelector('#results').textContent = 'Your browser does not support the Encoding API.'
  }

  // Use XHR to fetch `file` and interpret its contents as being encoded with `encoding`.
  function fetchAndDecode(file, encoding) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', file);
    // Using 'arraybuffer' as the responseType ensures that the raw data is returned,
    // rather than letting XMLHttpRequest decode the data first.
    xhr.responseType = 'arraybuffer';
    xhr.onload = function() {
      if (this.status == 200) {
        // The decode() method takes a DataView as a parameter, which is a wrapper on top of the ArrayBuffer.
        var dataView = new DataView(this.response);
        // The TextDecoder interface is documented at http://encoding.spec.whatwg.org/#interface-textdecoder
        var decoder = new TextDecoder(encoding);
        var decodedString = decoder.decode(dataView);
        // Add the decoded file's text to the <pre> element on the page.
        document.querySelector('#results').textContent += decodedString + '\n';
      } else {
        console.error('Error while requesting', file, this);
      }
    };
    xhr.send();
  }
</script>

The sample above uses feature detection to determine whether the required TextDecoder interface is available in the current browser, and displays an error message if it’s not. In a real application, you would normally want to fall back on an alternative implementation if native support isn’t available. Fortunately, the text-encoding library that Renato mentioned in his original article is still a good choice. The library uses the native methods on browsers that support them, and offers polyfills for the Encoding API on browsers that haven’t yet added support.

Update, September 2014: Modified the sample to illustrate checking whether the Encoding API is available in the current browser.

Web Audio Changes in m36

By Chris Wilson at

Web Audio changes

At Google, we love standards. We’re on a mission to build out the standards-defined Web platform. One of the small warts on that for some time has been the webkit- prefixed implementation of the Web Audio API (notably the webkitAudioContext object), and some of the deprecated bits of Web Audio that we’ve continued to support.

It was originally planned that m36 would remove support for the prefixed webkitAudioContext, since we had begun supporting the unprefixed AudioContext object. This turned out to be more troublesome than expected, so m36 supports both unprefixed and prefixed - however, even in the reintroduced webkitAudioContext, several legacy methods and attributes like createGainNode and createJavaScriptNode have been removed. In short, in m36 webkitAudioContext and AudioContext are aliases of each other; there is no difference in functionality between the two.

We will remove the support for the prefix completely after m36, likely in a couple of releases. We'll make an announcement here when the change is imminent, and we are continuing to reach out to authors to fix their Web Audio applications.

Why have we done this, rather than reverting to the previous implementation? Well, in part, we’ve been reticent to move too far backwards; we’ve already removed those APIs, and as a nice side-effect to this aliasing, applications can then work nicely on Firefox, which has never supported a prefixed AudioContext object (and quite right, too!) in their Web Audio support initially released last fall.

The rest of this update provides a guide to fixing things that may be broken in your code due to this change. The great thing about fixing these problems is that your code is then quite likely to just work in Firefox, too! (I’d thought for a long time that my Vocoder application was broken due to Firefox’s implementation, but it turned out to be one of these problems!)

If you just want to get up and running, you may want to take a look at a monkey-patch library I wrote for applications that were written to the old Web Audio code - this can help you get up and running in a minimum amount of time, as it will alias the objects and methods appropriately. Indeed, the patches the library lists is a good guide to the things that have changed.

First and foremost:

Any references to window.webkitAudioContext should be made to window.AudioContext instead. Frequently, this has been fixed with a simple:

window.AudioContext = window.AudioContext || window.webkitAudioContext;

If your app is responding with something like “Unfortunately, your browser does not support Web Audio. Please use Chrome or Safari.” - it’s quite likely explicitly looking for webkitAudioContext. Bad developer! You could have been supporting Firefox for months!

But there are a few other, more subtle code removals, some of which can be less obvious.

  • The BiquadFilter enumerated type constants for the .type attribute (which is now a string) no longer appear on the BiquadFilterNode object, and we do not support them on the .type attribute. So you don’t use .LOWPASS (or 0) anymore - you set it to “lowpass”.
  • Also, the Oscillator.type attribute is similarly now a string enumerated type - no more .SAWTOOTH.
  • PannerNode.type is also now a string enumerated type.
  • PannerNode.distanceModel is also now a string enumerated type.
  • createGainNode was renamed to createGain
  • createDelayNode was renamed to createDelay
  • createJavaScriptNode was renamed to createScriptProcessor
  • AudioBufferSourceNode.noteOn() is now replaced by start()
  • AudioBufferSourceNode.noteGrainOn() is also now replaced by start()
  • AudioBufferSourceNode.noteOff() is renamed to stop()
  • OscillatorNode.noteOn() is renamed to start()
  • OscillatorNode.noteOff() is renamed to stop()
  • AudioParam.setTargetValueAtTime() is renamed to setTargetAtTime()
  • `AudioContext.createWaveTable()andOscillatorNode.setWaveTable()are now renamedcreatePeriodicWave() and setPeriodicWave().
  • AudioBufferSourceNode.looping was removed, in favor of .loop
  • AudioContext.createBuffer(ArrayBuffer, boolean) to synchronously decode a blob of encoded audio data has been removed. Synchronous calls that take a long time to complete are poor coding practice; use the asynchronous decodeAudioData call instead. This is one of the more challenging changes - you need to actually change logic flow - but a far better practice. Mozilla's Ehsan Angkari wrote a nice example of how to do this in their post on converting to standard Web Audio.

Many of these (like the renaming of createGainNode, and the removal of the synchronous decoding in createBuffer) will obviously show up in the developer tools console as an error - however, some others, like this usage:

myFilterNode.type = myFilterNode.BANDPASS;

will not show up at all, and silently fail (myFilterNode.BANDPASS will now resolve to undefined, and the attempt to set .type to undefined will simply fail to produce any effect. This, by the way, was what was causing the vocoder to fail.) Likewise, just assigning the filter.type to a number used to work:

myFilterNode.type = 2;

But now, you need to use the string enumeration:

myFilterNode.type = “bandpass”;

So, you may wish to grep your code for the following terms:

  • webkitAudioContext
  • .LOWPASS
  • .HIGHPASS
  • .BANDPASS
  • .LOWSHELF
  • .HIGHSHELF
  • .PEAKING
  • .NOTCH
  • .ALLPASS
  • .SINE
  • .SQUARE
  • .SAWTOOTH
  • .TRIANGLE
  • .noteOn
  • .noteGrainOn
  • .noteOff
  • .setWaveTable
  • .createWaveTable
  • .looping
  • .EQUALPOWER
  • .HRTF
  • .LINEAR
  • .INVERSE
  • .EXPONENTIAL
  • createGainNode
  • createDelayNode
  • .type (yes, this will have lots of false positives - but it’s the only way to catch the last example above!)

Once more, if you're in a hurry and want to get up and running, just grab a copy of my monkeypatch webkitAudioContext library and include it in your application. Happy Audio Hacking!

dialog element: shipped in Chrome 37 Beta

By Eiji Kitamura at

Chrome Beta has landed its native support for <dialog> element without needing "Enable experimental Web Platform features." flag turned on.

<dialog>
  <p>This is da dialog!</p>
  <button id="close">Close</button>
</dialog>
<button id="show">Open Dialog!</button>
<script>
  var dialog = document.querySelector('dialog');
  document.querySelector('#show').onclick = function() {
    dialog.show();
  };
  document.querySelector('#close').onclick = function() {
    dialog.close();
  };
</script>

Check out more sample codes and how it works in detail with a live demo.

There are a few changes applied to the implementation since the last announcement but notable one is:

  • Non-modal <dialog> is no longer vertically centered in the viewport. It has no special positioning behavior beyond its default CSS styling.

If you are curious about further details on the spec update, check out diffs here and here.

Blob support for IndexedDB landed on Chrome Dev

By Eiji Kitamura at

Chrome Dev has landed support for Blob on IndexedDB.

This is a long awaited feature for Chrome that allows IndexedDB API to be able to store and retrieve a Blob without converting it to a Base64 string.

IndexedDB provides large scale key-value type persistent storage that is available on most of modern browsers (Safari apparently will land support in iOS8 and Mac OS X 10.10). Check out its implementation status.

Blob is a file-like binary object modern JavaScript engines can handle. File objects inherits from Blob. You can also fetch images and files as Blob via XMLHttpRequest. Check out its implementation status.

Storing a Blob on IndexedDB

There is no way to feature detect the Blob availability in IndexedDB. You basically have to try-catch then use string instead of Blob if it is not available. Here's some sample code:

// Create an example Blob object
var blob = new Blob(['blob object'], {type: 'text/plain'});

try {
  var store = db.transaction(['entries'], 'readwrite').objectStore('entries');

  // Store the object  
  var req = store.put(blob, 'blob');
  req.onerror = function(e) {
    console.log(e);
  };
  req.onsuccess = function(event) {
    console.log('Successfully stored a blob as Blob.');
  };
} catch (e) {
  var reader = new FileReader();
  reader.onload = function(event) {
    // After exception, you have to start over from getting transaction.
    var store = db.transaction(['entries'], 'readwrite').objectStore('entries');

    // Obtain DataURL string
    var data = event.target.result;
    var req = store.put(data, 'blob');
    req.onerror = function(e) {
      console.log(e);
    };
    req.onsuccess = function(event) {
      console.log('Successfully stored a blob as String.');
    };
  };
  // Convert Blob into DataURL string
  reader.readAsDataURL(blob);
}

Blob support for IndexedDB is already available on Firefox and Internet Explorer as well. Safari support needs to be investigated.

Enjoy!

Automating Web Performance Measurement

By Addy Osmani at

Web performance can have a huge impact on your entire user experience. If you’ve been looking at improving your own site’s perf lately, you’ve probably heard of PageSpeed Insights - a tool that analyzes pages and offers advice on how to make them faster based on best practices for mobile and desktop web performance.

PageSpeed’s scores are based on a number of factors, including how well your scripts are minimized, images optimized, content gzipped, tap targets being appropriately sized and landing page redirects avoided.

With 40% of people potentially abandoning pages that take more than 3 seconds to load, caring about how quickly your pages load on your users devices is increasingly becoming an essential part of our development workflow.

Performance metrics in your build process

Although manually going to the PageSpeed Insights to find out how your scores is fine, a number of developers have been asking whether it's possible to get similar performance scoring into their build process.

The answer is: absolutely.

Introducing PSI for Node

Today we’re happy to introduce PSI for Node - a new module that works great with Gulp, Grunt and other build systems and can connect up to the PageSpeed Insights service and return a detailed report of your web performance. Let’s look at a preview of the type of reporting it enables:

The results above are good for getting a feel for the type of improvements that could be made. For example, a 5.92 for sizing content to viewport means “some” work can still be done whilst a 24 for minimizing render blocking resources may suggest you need to defer loading of JS using the async attribute.

Lowering the barrier of entry to PageSpeed Insights

If you've tried using the PageSpeed Insights API in the past or attempted to use any of the tools we build on top of it, you probably had to register for a dedicated API key. We know that although this just takes a few minutes, it can be a turn off for getting Insights as part of your regular workflow.

We're happy to inform you that the PageSpeed Insights service supports making requests without an API key for up to 1 request every 5 seconds (plenty for anyone). For more regular usage or serious production builds, you'll probably want to register for a key.

The PSI module supports both a nokey option for getting it setup in less than a few minutes and the key option for a little longer. Details on how to register for an API key are documented.

Getting started

You have two options for how you integrate PSI into your workflow. You can either integrate it into your build process or run it as a globally installed tool on your system.

Build process

Using PSI in your Grunt or Gulp build-process is fairly straight-forward. If you’re working on a Gulp project, you can install and use PSI directly.

Install

Install PSI using npm and pass --save-dev to save it to your package.json file.

npm install psi --save-dev

Then define a Gulp task for it in your gulpfile as follows (also covered in our Gulp sample project):

   var psi = require('psi');
   gulp.task('psi', function (cb) {
       psi({
           nokey: 'true', // or use key: ‘YOUR_API_KEY’
           url: site,
           strategy: 'mobile',
       }, cb);
   });
   

For the above, you can then run the task using:

gulp psi

And if you’re using Grunt, you can use grunt-pagespeed by James Cryer which now uses PSI to power it’s reporting.

Install

npm install grunt-pagespeed --save-dev

Then load the task in your Gruntfile:

grunt.loadNpmTasks('grunt-pagespeed');

and configure it for use:

   pagespeed: {
     options: {
       nokey: true,
       url: "https://www.html5rocks.com",
       strategy: "mobile"
     }
   }
   

You can then run the task using:

grunt pagespeed

Installing as a global tool

You can also install PSI as a globally available tool on your system. Once again, we can use npm to install the tool:

$ npm install -g psi

And via any terminal window, request PageSpeed Insights reports for a site (with the nokey option or an API specific key as follows):

psi http://www.html5rocks.com --nokey --strategy=mobile

or for those with a registered API key:

psi http://www.html5rocks.com --key=YOUR_API_KEY --strategy=mobile

That’s it!

Go forth and make performance part of your culture

We need to start thinking more about the impact of our designs and implementations on user experience.

Solutions like PSI can keep an eye on your web performance on desktop and mobile and are useful when used as part of your regular post-deployment workflow.

We're eager to hear of any feedback you might have and hope PSI helps improve the performance culture on your team.

Web Animations - element.animate() is now in Chrome 36

By Brendan Kenny at

Animation on the web was once the province of JavaScript, but as the world moved to mobile, animations moved to CSS for the declarative syntax and the optimizations browsers were able to make with it. With 60fps on mobile always your goal, it makes sense to never step outside of what browsers know how to efficiently display.

More tools are appearing to make JavaScript-driven animations more efficient, but the holy grail is a unification of declarative and imperative animations , where the decision of how to write your animations is based on what’s the clearest code, not what is possible in one form and not in the other.

Web Animations stand to answer that call, and the first part of it has landed in Chrome 36 in the form of element.animate(). This new function lets you create an animation purely in JavaScript and have it run as efficiently as any CSS Animation or Transition (in fact, as of Chrome 34, the exact same Web Animations engine drives all of these methods).

The syntax is simple, and its parts should be familiar to you if you’ve ever written a CSS Transition or Animation:

element.animate([
  {cssProperty: value0},
  {cssProperty: value1},
  {cssProperty: value2},
  //...
], {
    duration: timeInMs,
    iterations: iterationCount,
    delay: delayValue
});

The biggest advantage of this new function is the elimination of a lot of awkward hoops we formerly had to jump through to get a smooth, jank-free animation.

As an example, for Santa Tracker last year, we wanted to have snow falling continuously, and we decided to animate it via CSS so that it could be done so efficiently.

However, we wanted to pick the snow’s horizontal position dynamically based on screen and events going on in the scene itself, and of course the height of the snow’s fall (the height of the user’s browser window) wouldn’t be known until we were actually running. This meant we really had to use CSS Transitions, as authoring a CSS Animation at runtime gets complex quickly (and hundreds of snowflakes means hundreds of new styling rules).

So we took the following approach, which should be familiar:

snowFlake.style.transform = 'translate(' + snowLeft + 'px, -100%)';
// wait a frame
snowFlake.offsetWidth;
snowFlake.style.transitionProperty = 'transform';
snowFlake.style.transitionDuration = '1500ms';
snowFlake.style.transform = 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)';

The key is in that 'wait a frame’ comment. In order to successfully start a transition, the browser has to acknowledge that the element is in the starting position. There are a few ways to do this. One of the most common ways is to read from one of the element properties that forces the browser to compute layout, thereby ensuring it knows that the element has a starting position before transitioning to the ending position. Using this method allows you to congratulate yourself on your superior knowledge of browser internals while still feeling dirty with every keystroke.

In contrast, the equivalent `element.animate()` call couldn’t be more clear, saying exactly what is intended:

snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);

There are many more options. Just like with its CSS counterparts, Web Animations can be delayed and iterated:

snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
  duration: 1500,
  iterations: 10,
  delay: 300
});

AnimationPlayer

element.animate() actually returns an AnimationPlayer object, which will become increasingly important as more of the Web Animations spec is launched. Both JavaScript- and CSS-created animations will have associated AnimationPlayers, allowing them to be seamlessly combined in useful and interesting ways.

For now, though, AnimationPlayer only has two pieces of functionality, both very useful. You can cancel an animation at any time by using AnimationPlayer.cancel():

var player = snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
// less than 1500ms later...changed my mind
player.cancel();

And, to the relief of everyone who has attempted to build an animation system around CSS Animations or Transitions in the past, Web Animations always fire an event when they’re finished:

var player = snowFlake.animate([
  {transform: 'translate(' + snowLeft + 'px, -100%)'},
  {transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], 1500);
player.onfinish = function(e) {
  console.log('per aspera ad terra!');
}

Try it out

This is all shipping in Chrome 36, moving to beta today! If you’d like to try it, try working with the native implementation in Chrome 36. However, there is a Web Animations polyfill, which brings a significantly larger part of the full Web Animations specification to any of the modern, evergreen browsers.

A demo of the snow effect is available for you to try using both the native version of element.animate() and the polyfill.

Let us know what you think

Really, though, this is a preview of what’s to come, and is being released specifically to get developer feedback right away. We’re not sure yet if we’ve hit every use case, or sanded down every rough edge of the current APIs for animation. The only way for us to know and to really get this right is for developers to try it out and let us know what they think.

Comments on this post are of course valuable, and comments on the standard itself can be addressed to the CSS and SVG Working Groups via the public-fx mailing list.

Thanks to Addy Osmani and Max Heinritz for their assistance with this post.

A More Compatible, Smoother Touch

By Vivian Cromwell at

You and your users want mobile web apps that react and scroll smoothly to the touch. Developing them should be easy but, unfortunately, how mobile web browsers react to touch events during scrolling is left as an implementation detail in the TouchEvent specification. As a result, approaches can be broken down roughly into 4 categories. This situation exposes a fundamental tension between delivering scroll smoothness and maintaining developer control.

Four different models of touch event processing?

The behavior differences between the browsers break down into four models.

  1. Normal synchronous event processing

    Touchmove events are sent during scrolling and each scroll update blocks until touchmove handling has completed. This is good as the simplest to understand and the most powerful but bad for scroll performance because it means that each frame during the scroll must block on the main thread.

    Browsers: Android Browser (Android 4.0.4, 4.3), Mobile Safari (when scrolling div)

  2. Asynchronous touchmove processing

    Touchmove events are sent during scrolling, but scrolling can proceed asynchronously (the touchmove event is ignored after scrolling has begun). This can result in "double handling" of events, for example, continuing to scroll after the web site does something with the touchmove and calls preventDefault on the event, telling the browser not to handle it.

    Browsers: Mobile Safari (when scrolling Document), Firefox

  3. Touchmove suppressed while scrolling

    Touchmove events are not sent after scrolling starts and do not resume until after the touchend event. In this model, it's hard to tell the difference between a stationary touch and a scroll.

    Browsers: Samsung Browser (mousemove events sent)

  4. Touchcancel on scroll start

    You can't have it both ways -- scroll smoothness and developer control -- and this model makes clear the trade-off between smooth scrolling and event handling, similar to the semantics of the Pointer Events specification. Some experiences that may need to track the finger, like pull-to-refresh, are not possible.

    Browsers: Chrome Desktop M32+, Chrome Android

Why Change?

Chrome for Android currently uses Chrome's Old Model: touchcancel on scroll start, which enhances scrolling performance, but leads to developer confusion. In particular, some developers aren't aware of the touchcancel event or how to deal with it, and this has caused some web sites to break. More importantly, an entire class of UI scrolling effects and behaviors, such as pull-to-refresh, hidey bars, and snap points are difficult or impossible to implement well.

Rather than adding specifically hardcoded features to support these effects, Chrome aims to concentrate on adding platform primitives that allow developers to implement these effects directly. See A Rational Web Platform for a general exposition of this philosophy.

Chrome's New Model: The Throttled Async Touchmove Model

Chrome is introducing a new behavior designed to improve compatibility with code written for other browsers when scrolling, as well as enabling other scenarios that depend on getting touchmove events while scrolling. This feature is enabled by default and you can turn it off with the following flag, chrome://flags#touch-scrolling-mode.

The new behavior is:

  • The first touchmove is sent synchronously to allow scroll to be canceled
  • During active scrolling
    • touchmove events are sent asynchronously
    • throttled to 1 event per 200ms, or if a CSS 15px slop region is exceeded
    • Event.cancelable is false
  • Otherwise, touchmove events fire synchronously as normal when active scrolling terminates or isn't possible because the scroll limit has been hit
  • A touchend event always occurs when the user lifts their finger

You can try this demo in Chrome for Android and toggle the chrome://flags#touch-scrolling-mode flag to see the difference.

Let us know what you think

The Async Touchmove Model has the potential to improve cross-browser compatibility and enable a new class of touch gesture effects. We're interested in hearing what developers think, and in seeing the creative things you can do with it.

Get on the CSS Grid!

By Alex Danilo at

When building a Web Application, one of the first things that’s needed is a way to lay out the content of your app.

Lots of designers use imaginary grids to lay out content, whether it’s for a site or app. The CSS group has been working hard to make layouts easier, and as part of that have produced the CSS Grid Layout Module which is now emerging in browsers.

This feature is available to try out in Chrome behind an experimental flag. It’s also implemented in IE since version 10, and likely to be in most browsers soon.

Executive summary

  • CSS Grid Layout lets you define rows and columns for your layout
  • Grids can adapt to make use of available space
  • Content order can be independent from grid container display order
  • Layouts can change based on media queries, making responsive design easier
  • Content can overlap (enabling layout that’s impossible with other methods)
  • Grid Layout is fast

Here’s an overview video that explains a lot about how CSS Grid Layout works (slides are here):

Try it out

Using CSS Grid Layout in Chrome now is easy. First thing you need to do is turn on the experimental flag to enable the feature.

First load the chrome://flags URL and scroll down to the option to Enable experimental Web Platform features as shown below:

Image of experimental flag option

Just click Enable to turn the flag on, and you should see a prompt to restart the browser that looks like this:

Image of Relaunch button

Now just click the Relaunch Now button to restart your browser and you’ll be all set to try out CSS Grid Layout.

Let us know what you think

CSS Grid Layout is a great new primitive for web content, but as usual we’re all keen to hear what developers think about it. There are lots of ways to give feedback – leave a comment here, send mail to the W3C CSS Working Group list with “[css-grid]” in the subject line, log bugs, or blog and tweet what you think of it. We look forward to seeing the great layouts you build with this super useful new feature.

The Yeoman Monthly Digest #3

By Addy Osmani at

Allo! Allo! This past month saw the Yeoman community grow to 472 generators. We’re excited to see so much passion for project scaffolding and are working on some new features to enable generators to extend and build on top of each other.

We’ll talk more about this soon, but for now here’s this month’s round-up of featured articles, generators and team blogs. We hope they’re useful!

From the Team blog

Exploring A Generator For Gulp.js

Generators New Year Cleanup!

Updates To Our Grunt, Bootstrap & Other Generators

Yeoman Q1 2014 Roadmap and Roadmap For The Angular Generator

What's New In The Backbone Generator

Sindre Sorhus also created envcheck - a useful environment checker that will double-check you have everything needed to run Yeoman smoothly.

Articles

This month the community have been building full-stack webapps, Hybrid apps and even a remote desktop client with Yeoman generators. We enjoyed reading:

A Remote Desktop Client with AngularJS and Yeoman

Angular Fullstack 1.2.0 available now

Automating React With Yeoman and Grunt

Cordova + Yeoman Web Best Practices v2.0

Building a new Yeoman generator

Goodbye, Sprockets! A Grunt-based Rails Asset Pipeline

Using Yeoman along with the ng-book

Yo Polymer – A Whirlwind Tour Of Web Component Tooling

Should Yeoman Split Up It's Gruntfiles?

A Workshop On Bower, Yeoman And Flight

Samsung Smart TV app generator for Yeoman

Using Yeoman to scaffold out new websites

Announcing generator-angular-require

Web App with dream team; AngularJS, Cordova, Yeoman & Topcoat

Using the European npm mirror

Scaffold your projects with Yeoman…

Why use Yeoman?

Simple Scaffolding with the Ignite UI Yeoman Generator

Improving Your Development Workflow with Yeoman

Why you should use Yeoman

AngularJS, PhoneGap and my mobile toolset

Featured Generators

This month also saw new generators released for KrakenJS, Meteor, ChaplinJS and a number of other projects. Some of our choice picks:

Scaffold KrakenJS projects

A ChaplinJS generator

Scaffold ESLint rules

Scaffolding for Meteor projects

Scaffold Gulp plugins

Scaffold Gulp files

Scaffold KendoUI apps

Radian ~ A scalable AngularJS framework with a Yeoman generator

Scaffolding for Android

Scaffold Angular + Firebase apps

A generator for DocPad projects

Automating and Provisioning AWS Infrastructure for HTML5 Apps With Yeoman

A generator for mobile webapps

Scaffold slides with the Google I/O template

Simple webapp generator for Yeoman

Yeoman WordPress theme generator – kickstart a grunting SCSS theme

Scaffold Closure projects

Scaffolding for Gulp webapps

An ExpressionEngine Add-on generator

Scaffold AMD projects

Scaffold Finatra apps

Scaffold pattern libraries

Finally, updates have also been made to our official Ember.js, Mobile WebApp and Karma generators. Changelogs for all of them will be landing soon.

Until next time

If you’ve written an article, given a talk or created a generator you think would be useful to others, please feel free to share it with us on Twitter or Google+. We’re always interested in seeing how our tools are used.

Until the next time we run yo digest, happy scaffolding and don't be afraid - Yeoman is here to help, not replace you :D

With thanks for the rest of the Yeoman team for their reviews