Lots of JavaScript frameworks using MVC or MDV need to respond to changes to the objects that model the state inside a web application. This capability is a fundamental part of a data-binding model.
There are a number of different ways to monitor JavaScript objects and DOM properties to trigger some sort of action, but most of the techniques aren’t ideal for various reasons such as performance, etc.
In order to improve the performance of web applications, a new method called Object.observe() has been proposed to TC39 – the standards body overseeing development of ECMAScript (JavaScript).
Object.observe() lets you add a listener to any JavaScript object that gets called whenever that object, or its properties, change.
You can try it out now in Chrome Canary version 25.
To experiment with this feature, you need to enable the Enable Experimental JavaScript flag in Chrome Canary and restart the browser. The flag can be found under 'about:flags’ as shown in the image below:

Here’s a simple example of how to set up an observer on an object:
var beingWatched = {};
// Define callback function to get notified on changes
function somethingChanged(changes) {
// do something
}
Object.observe(beingWatched, somethingChanged);
When the object 'beingWatched’ is modified, it will trigger the callback function 'somethingChanged’ which receives an array of changes that were applied to the object.
So the JavaScript engine is free to buffer up a number of changes and pass them all in a single call to the callback function. This helps with optimizing the callbacks so that your code can do lots of JavaScript manipulation but process only a few callbacks by batching the updates together.
The callback function will be triggered whenever a property is added, modified, deleted or reconfigured.
Another really nice thing when observing arrays is that if an array has had a number of changes made to it, you can use a Change Summary helper library to create a minimal change set, so that client side JavaScript doesn’t have to manually scan the array to check each item.
You can iterate through each change quite easily, by doing something like the following in your 'somethingChanged’ callback function:
function whatHappened(change) {
console.log(change.name + " was " + change.type + " and is now " + change.object[change.name]);
}
function somethingChanged(changes) {
changes.forEach(whatHappened);
}
The type property identifies what happened to the object. Some examples of properties being set and the associated type can be seen in the code below.
beingWatched.a = "foo"; // new
beingWatched.a = "bar"; // updated
beingWatched.a = "bar"; // no change
beingWatched.b = "amazing"; // new
The great thing about this technique is that all the monitoring smarts are inside the JavaScript engine which allows the browser to optimize it well and free your JavaScript up to implement functionality taking advantage of this feature.
Another really great feature for development is the ability to use Object.observe() to trigger the debugger whenever an object changes. To do that you’d use code something like the example below.
Object.observe(beingWatched, function(){ debugger; });
Here’s a great video introduction about Object.observe() that explains it in detail.
There’s also a nice descriptive write-up available and a working example here.
The TC39 standards body is seeking feedback on this feature, so go ahead and try it and let us know what you think.
It's official! The W3C has advanced the Content Security Policy 1.0 specification from Working Draft to Candidate Recommendation, and issued a call for implementations. Cross-site scripting attacks are one step closer to being (mostly) a thing of the past.
Chrome Canary and WebKit nightlies now support the unprefixed Content-Security-Policy header, and will be using the prefixed X-WebKit-CSP header to begin experimenting with some new behavior that's being specified as part of Content Security Policy 1.1. Instead of writing:
X-WebKit-CSP: script-src 'self'; object-src 'none'
You'll write:
Content-Security-Policy: script-src 'self'; object-src 'none'
We expect other browser vendors to follow suit within the next few revisions, so it's a great idea to start sending the canonical header today.
Content Security Policy! It helps you reduce the risk of cross-site scripting and other content injection attacks in your applications. It's a huge step forward in terms of the protection you can offer your users, and we highly recommend taking a hard look at implementing it. You can get all the details in the ever so cleverly named "An Introduction to Content Security Policy".
Recently, I have read news on Webmonkey that Adobe’s CSS Shaders proposal, which will bring high-quality cinematic effects to the web through some new CSS tools, has been accepted by the W3C. If you haven't seen it yet, watch the video below:
Google Chrome's latest Canary added support for CSS shaders, so I decided to experiment with them.
In this experiment, I used custom vertex shader (spherify.vs) and fragment shader (spherify.fs) to create a globe with Google Maps.
<iframe
class="globe"
src="http://maps.google.com/?ie=UTF8&ll=14.597042,-15.625&spn=158.47027,316.054688&t=h&z=2&output=embed"
scrolling="no"></iframe>
.globe {
width: 550px;
height: 550px;
border: 0;
-webkit-filter: contrast(1.4) custom(url(shaders/spherify.vs) mix(url(shaders/spherify.fs) multiply source-atop),
50 50 border-box,
amount 1,
sphereRadius 0.5,
sphereAxis -0.41 1 0.19,
sphereRotation 43.5,
ambientLight 0.15,
lightPosition 1 0.87 0.25,
lightColor 1 1 1 1,
transform perspective(500));
}
Here, we're applying a vertex shader (spherify.vs) which will operate on a mesh that has 50 lines and 50 columns (50 50 border-box). Feel free to read the source of the vertex shader: spherify.vs. It's written in GLSL but you can probably follow along.
The mix() function provides basic functionalities for color manipulation like blending and alpha compositing on a fragment shader.
We can change the shere's radius, axis, rotation right in the CSS. In this example we set the value of the sphereRadius: 0.5 and it gives original sphere size.
Below is a video of the effect. If you've got shaders enabled you can play with the real thing right below!
If you just see a flat google maps above, you can enable it with the instructions below
This is currently cutting-edge, so it's only available in the latest Google Chrome Canary and WebKit nightly. To enjoy the full experience you'll need to turn a few knobs.
about:flags in the browser's navigation barI'm really excited by a new feature that went in to yesterday's Chrome Canary build (23.0.1270.0) - the ability to get low-latency access to live audio from a microphone or other audio input on OSX! (This has not yet been enabled on Windows - but don't worry, we're working on it!)
[UPDATE Oct 8, 2012: live audio input is now enabled for Windows, as long as the input and output device are using the same sample rate!]
To enable this, you need to go into chrome://flags/ and enable the "Web Audio Input" item near the bottom, and relaunch the browser; now you're ready to roll! Note: If you're using a microphone, you may need to use headphones for any output in order to avoid feedback. If you are using a different audio source, such as a guitar or external audio feed, or there's no audio output from the demo, this may not be a problem. You can test out live audio input by checking out the spectrum of your input using the live input visualizer.
For those Web Audio coders among you, here's how to request the audio input stream, and get a node to connect to any processing graph you like!
// success callback when requesting audio input stream
function gotStream(stream) {
var audioContext = new webkitAudioContext();
// Create an AudioNode from the stream.
var mediaStreamSource = audioContext.createMediaStreamSource( stream );
// Connect it to the destination to hear yourself (or any other node for processing!)
mediaStreamSource.connect( audioContext.destination );
}
navigator.webkitGetUserMedia( {audio:true}, gotStream );
There are many rich possibilities for low-latency audio input, particularly in the musical space. You can see a quick example of how to make use of this in a simple pitch detector I threw together - try plugging in a guitar, or even just whistling into the microphone.
And, as promised, I've added live audio as an input source to the Vocoder I wrote for Google IO - just select "live input" under modulator. You may need to adjust the Modulator Gain and the Synth Level. There's a slight lag due to processing (not due to input latency). Now that I have live audio input, it's time for another round of tweaking!
Finally, you may want to take a look at the collection of my web audio demos - by the time you read this, I may have some more live audio demos up!
In Chrome 22 the layout behavior of position:fixed elements is slightly different than previous versions. All position:fixed elements now form new stacking contexts. This will change the stacking order of some pages, which has the potential to break page layouts. The new behavior matches the behavior of WebKit browsers on mobile devices (iOS Safari and Chrome for Android).
Everyone knows and loves the z-index for determining depth ordering of elements on a page. Not all z-indexes are created equal, however: an element's z-index only determines its ordering relative to other elements in the same stacking context. Most elements on a page are in a single, root stacking context, but absolutely or relatively positioned elements with non-auto z-index values form their own stacking contexts (that is, all of their children will be z-ordered within the parent and not be interleaved with content from outside the parent). As of Chrome 22, position:fixed elements will also create their own stacking contexts.
For a general overview of stacking contexts this MDN article is a great read.
Compare position:fixed to the new position:sticky attribute: for reference, position:sticky always creates a new stacking context.
Mobile browsers (Mobile Safari, Android browser, Qt-based browsers) put position:fixed elements in their own stacking contexts and have for some time (since iOS5, Android Gingerbread, etc) because it allows for certain scrolling optimizations, making web pages much more responsive to touch. The change is being brought to desktop for three reasons:
Here's an example showing the different layout behaviors: http://codepen.io/paulirish/pen/CgAof
With the change, both versions will render like the right-hand version.
In this example, the green box has a z-index: 1, the pink box has a z-index: 3, and the orange box has a z-index: 2. The blue box is an ancestor of the orange box, and has position:fixed.
If the blue box gets its own stacking context, the orange box's z-index is computed relative to the blue box’s stacking context. Since the blue box has a z-index of auto, giving it a stacking level of zero in the root stacking context, this means the orange box ends up behind the green and pink boxes, which have z-indexes of 1 and 3 (respectively) in the root context.
If the blue box does not get its own stacking context, the orange box's z-index is computed relative to the root stacking context (along with the green and pink boxes). Hence, the orange box ends up interleaved with the pink and green boxes.
For more detail about criteria for stacking context creation (and how stacking contexts behave in general), again refer to this MDN article. In the example the right-hand side version always gave the blue box its own stacking context because its opacity is less than 1. The behavior change being made effectively adds another criterion for creating a separate stacking context, namely an element being position:fixed.
To test if your page is going to change, go to Chrome's about:flags and turn on/off "fixed position elements create stacking contexts". If your layout behaves the same in both cases, you're set. If not, make sure it looks acceptable to you with that flag enabled, as that will be the default in Chrome 22.
This change removes one capability - the ability to interleave content within a position:fixed subtree with non-scrolling content from outside. It's unlikely that any web developers are doing this on purpose, and the same effect can be created by giving multiple position:fixed elements the different portions of DOM. As an example, consider these two examples:
http://codepen.io/wiltzius/pen/gcjCk
This page attempts to take two child divs (overlayA and overlayB) of a position:fixed element and place one above a separate content div and one below that same separate content div. It’s now impossible to do this because the position:fixed element is its own stacking context, and it (along with all of its children) will be either entirely above or entirely below the content div. Note that this example works in Chrome 21 and earlier but no longer in Chrome 22.
To fix this, the two overlays can be broken up into their own position:fixed elements. Each is its own stacking context, one of which can go above the content div and one of which can go below the content div. See the fixed example, which works in Chrome 21 and 22:
http://codepen.io/wiltzius/pen/vhFzG
Credit for the genesis of this example goes to the inimitable hixie.
Chrome is the first desktop browser to cause position:fixed elements to create their own stacking contexts. The relevant standard is the CSS z-index specification (see e.g. http://www.w3.org/TR/CSS21/zindex.html). There's not yet consensus over what to do about the difference between mobile and desktop browsers here, but given the confusion of having two different behaviors on mobile and desktop, Chrome has chosen to move to this single behavior on both platforms for the time being.
Updated Oct 1, 2012: The original version of this article suggested that the CSS z-index specification had already been changed to reflect the new behavior of position: fixed elements. This is inaccurate; it has been discussed on the www-style list but as of yet no change has been taken into the spec.
position: sticky is a new way to position elements and is conceptually similar to position: fixed. The difference is that an element with position: sticky behaves like position: relative within its parent, until a given offset threshold is met in the viewport.
Paraphrasing from Edward O’Connor's original proposal of this feature:
Many web sites have elements that alternate between being in-flow and having
position: fixed, depending on the user's scroll position. This is often done for elements in a sidebar that the page author wants to be always visible as the user scrolls, but which slot into a space on the page when scrolled to the top. Good examples are news.google.com (the "Top Stories" sidebar) and yelp.com (search results map).
By simply adding position: sticky (vendor prefixed), we can tell an element to be position: relative until the user scrolls the item (or its parent) to be 15px from the top:
.sticky {
position: -webkit-sticky;
position: -moz-sticky;
position: -ms-sticky;
position: -o-sticky;
top: 15px;
}
At top: 15px, the element becomes fixed.
To illustrate this feature in a practical setting, I've put together a DEMO which sticks blog titles as you scroll.
Until now, to achieve the sticky effect, sites setup scroll event listeners in JS. We actually use this technique as well on html5rocks tutorials. On screens smaller than 1200px, our table of contents sidebar changes to position: fixed after a certain amount of scrolling.
Here's the (now old way) to have a header that sticks to the top of the viewport when the user scrolls down, and falls back into place when the user scrolls up:
<style>
.sticky {
position: fixed;
top: 0;
}
.header {
width: 100%;
background: #F6D565;
padding: 25px 0;
}
</style>
<div class="header"></div>
<script>
var header = document.querySelector('.header');
var origOffsetY = header.offsetTop;
function onScroll(e) {
window.scrollY >= origOffsetY ? header.classList.add('sticky') :
header.classList.remove('sticky');
}
document.addEventListener('scroll', onScroll);
</script>
Try it: http://jsbin.com/omanut/2/
This is easy enough, but this model quickly breaks down if you want to do
this for a bunch of DOM nodes, say, every <h1> title of a blog as the user scrolls.
Why JS is not ideal
In general, scroll handlers are never a good idea. Folks tend to do too much work and wonder why their UI is janky.
Something else to consider is that more and more browsers are implementing hardware accelerated scrolling to improve performance. The problem with this is that on JS scroll handlers are in play, browsers may fall back into a slower (software) mode. Now we're no longer running on the GPU. Instead, we're back on the CPU. The result? User's perceive more jank when scrolling your page.
Thus, it makes a lot of sense to have such feature be declarative in CSS.
Unfortunately, there isn't a spec for this one. It was proposed on www-style back in June and just landed in WebKit. That means there's no good documentation to point to. One thing to note however, according to this bug, if both left and right are specified, left wins. Likewise, if top and bottom are used at the same time, top wins.
Support right now is Chrome 23.0.1247.0+ (current Canary) and WebKit nightly.
Let's say you have a photo editing app and you'd like users to be able to drag in hundreds of photos and copy them into your app. Ok, what do you do?
In a recent post, Eiji Kitamura highlighted a subtle, yet powerful new feature in the drag and drop APIs; the ability to drag in folders and retrieve them as HTML5 Filesystem API FileEntry and DirectoryEntry objects (done by accessing a new method on the DataTransferItem, .webkitGetAsEntry()).
What's remarkably cool about the .webkitGetAsEntry() extension is how elegant it makes importing files and entire folders. Once you have a FileEntry or DirectoryEntry from a drop event, it's a matter of using the Filesystem API's copyTo() to get it imported into your app.
An example of copying multiple dropped folders over to the filesystem:
var fs = null; // Cache filesystem for later.
// Not shown: setup drag and drop event listeners.
function onDrop(e) {
e.preventDefault();
e.stopPropagation();
var items = e.dataTransfer.items;
for (var i = 0, item; item = items[i]; ++i) {
var entry = item.webkitGetAsEntry();
// Folder? Copy the DirectoryEntry over to our local filesystem.
if (entry.isDirectory) {
entry.copyTo(fs.root, null, function(copiedEntry) {
// ...
}, onError);
}
}
}
window.webkitRequestFileSystem(TEMPORARY, 1024 * 1204, function(fileSystem) {
fs = fileSystem;
}, function(e) {
console.log('Error', e);
});
Very nice! Again, the simplicity comes from integrating DnD with the Filesystem API calls.
Taking this one step further, we also have the ability to drag and drop a folder and/or files onto a normal <input type="file">, then access the entries as Filesystem directory or file entries. That is done through .webkitEntries:
<input type="file" multiple>
function onChange(e) {
e.stopPropagation();
e.preventDefault();
var entries = e.target.webkitEntries; // Get all dropped items as FS API entries.
[].forEach.call(entries, function(entry) {
// Copy the entry into our local filesystem.
entry.copyTo(fs.root, null, function(copiedEntry) {
...
}, onError);
});
}
document.querySelector('input[type="file"]').addEventListener('change', onChange);
I've put together a photo gallery demo to demonstrate these different techniques for importing files/folders.
To learn more about the HTML5 Filesystem API, see Exploring the Filesystem APIs.
The High Resolution Timer was added by the WebPerf Working Group to allow measurement in the Web Platform that's more precise than what we've had with +new Date and the newer Date.now().
So just to compare, here are the sorts of values you'd get back:
Date.now() // 1337376068250 performance.now() // 20303.427000007
You'll notice the two above values are many orders of magnitude different. performance.now() is a measurement of floating point milliseconds since that particular page started to load (the performance.timing.navigationStart timeStamp to be specific). You could argue that it could have been the number of milliseconds since the unix epoch, but rarely does a web app need to know the distance between now and 1970. This number stays relative to the page because you'll be comparing two or more measurements against eachother.
Another added benefit here is that you can rely on the time being monotonic. Let's let WebKit engineer Tony Gentilcore explain this one:
Perhaps less often considered is that
Date, based on system time, isn't ideal for real user monitoring either. Most systems run a daemon which regularly synchronizes the time. It is common for the clock to be tweaked a few milliseconds every 15-20 minutes. At that rate about 1% of 10 second intervals measured would be inaccurate.
There are a few situations where you'd use this high resolution timer instead of grabbing a basic timestamp:
The high resolution timer is currently available in Chrome (Stable) as window.performance.webkitNow(), and this value is generally equal to the new argument value passed into the requestAnimationFrame callback. Pretty soon, WebKit will drop its prefix and this will be available through performance.now(). The WebPerfWG in particular, led by Jatinder Mann of Microsoft, has been very successful in unprefixing their features quite quickly.
In summary, performance.now() is...
navigationStart of the page rather than to the UNIX epoch
Chrome started to support datalist for input[type=text] in Chrome 20. datalist helps developers provide recommended values, while allowing users the liberty to write arbitrary values at the same time. Beginning with Chrome 23, you can use datalist for input[type=range] and input[type=color] as well!
datalist for input[type=range] introduces the ability for developers to show indicators beside the slider as shown below:
<input type="range" value="0" min="0" max="100" list="numbers" />
<datalist id="numbers">
<option>10</option>
<option>15</option>
<option>30</option>
<option>50</option>
<option>90</option>
</datalist>
Moving the slider thumb on the input snaps to each of the ticks so that users can easily adjust to those values.
input[type=color] is already supported in Chrome and Opera. Users can pick arbitrary color without any help from JavaScript plugins.
By adding datalist to input[type=color], users can now pick a color from developer selected color swatches as well as choosing arbitrary color from a color picker by themselves.
<input type="color" value="#000000" list="colors" />
<datalist id="colors">
<option>#ff0000</option>
<option>#0000ff</option>
<option>#00ff00</option>
<option>#ffff00</option>
<option>#00ffff</option>
</datalist>
Note that datalist for input[type=color] only accepts the hex color values (ex. #ff0000) and values such as #f00 or red won’t work.
To see these new features in action, visit a demo page.
As you might have already noticed, Google Chrome supports a datepicker since Chrome 20. Just by setting the type attribute of the input element to date, the user can click the arrow button and Chrome will pop up a nice calendar widget.
As we have received a lot of feedback from developers, we'd like to clarify a few things about how to get the best out of using the date picker in this article.
Users can type a date value into the text field of an input[type=date] with the date format shown in the box as gray text. This format is obtained from the operating system's setting.
Web authors have no way to change the date format because there currently is no standards to specify the format.
The date value interpreted from input[type=date] in the HTTP Request such as GET / POST will be formatted as yyyy-mm-dd.
The input.value always returns as yyyy-mm-dd regardless of the presentation format.
When setting the input.value programmatically, the value accepts only yyyy-mm-dd style regardless of the presentation format for both the initial value and the JavaScript injected value.
You cannot currently style the appearance of the date picker. In WebKit, we have previously provided ways to style form controls with the -webkit-appearance CSS property or the ::-webkit-foo pseudo class selector. However the calendar popup does not provide such ways in WebKit because it is separate from the document, like a popup menu for <select>, and there is not currently a standard for how to control styling on its sub-elements.
If you have tried jQuery Datepicker on input[type=date] in Google Chrome, you might have noticed overlapping calendars of both the jQuery UI and the native calendar popup.
If you'd like to apply jQuery Datepicker on all platforms, use input[type=text] instead of input[type=date]. Not only Google Chrome but also iOS Safari, the BlackBerry browser, and Opera have their own UI for input[type=date], and there is currently no way to achieve a unified UI on all platforms using input[type=date].
If you'd like to apply jQuery Datepicker only on platforms without input[type=date] support, you may use Modernizr, or the following code:
var isDateInputSupported = function(){
var elem = document.createElement('input');
elem.setAttribute('type','date');
elem.value = 'foo';
return (elem.type == 'date' && elem.value != 'foo');
}
if (!isDateInputSupported()) // or.. !Modernizr.inputtypes.date
$('input[type="date"]').datepicker();