Until recently, the only ways to maintain a user's data between visits to your site have been to store it on the server, or use cookies in the browser. Both present significant security challenges and quite a good deal of effort for us as developers.
Cookies are designed for communication between the browser and a server that persists between sessions. They're typically used for identifying a user on return visits and storing details about that user. Cookies are sent between the browser and server in plain text, unencrypted, each time the user opens a page. So, unless an application encrypts cookie contents, these can be quite trivially be read particularly on public wifi networks, when used over standard HTTP (though less easily over encrypted HTTPS).
Storing all client data on the server creates usability issues as well, as users need to log in each time they use that site. And of course the heavy lifting of ensuring data is secure during transmission, and on the server is left to you as the developer. And it's rather tricky to build apps which work when the user is offline if the user's data is all stored on the server.
As web applications become increasingly sophisticated, developers need ways to keep data around in the browser (particularly if we want our applications to work when the user is offline).
Two closely related but slightly different W3C technologies exist to help keep track of information solely in the browser. They enable far more structured data than cookies, are much easier for us to use as developers, and the information stored can only be transmitted to a server explicitly by the application.
sessionStorage stores data during a session and is removed once a session is finished. localStorage is almost identical, but the data stored persists indefinitely, until removed by the application. Let's start with sessionStorage, keeping in mind that we use localStorage almost identically.
sessionStorage
What is a session?
The key feature of sessionStorage is that data only persists for a session. But just what is a session? HTML5 has the concept of a "top-level browsing context". This is, in essence, a window or tab. A session lasts for that top-level browsing context while it is open, and while that top-level browsing context is pointed at the same domain (or strictly speaking, the same origin).
During the session, a user could visit other pages of the domain, or other sites entirely, then return to the original domain. Any data saved in sessionStorage during that session will remain available, but only to pages in the original domain, until the tab or window is closed.
If the user opens a link to your site in another tab or window, then there is no access to this sessionStorage, since this new tab or window is a new session. It's worth noting that sessionStorage is also shared with pages inside subframes in the same domain as the top level document in the window.
So, if we
- visit
http://webdirections.orgin a tab and save data tosessionStorage - then follow a link to
http://westciv.comin this same tab - and then return to
http://webdirections.orgin the same tab - we return to the same session for
http://webdirections.org - the data in the original
sessionStorageis still available
If however we
- visit
http://webdirections.orgin a tab and save data tosessionStorage - then follow a link to
http://webdirections.orgin a new tab - the data in the original
sessionStorageis not available to this new tab (but is in the original tab)
The one exception to this is when a browser crashes, and is restarted. Typically, browsers will in this case reopen all the windows that were open when the browser crashed. The specification allows in this situation for sessionStorage to persist for reopened windows from before the crash (WebKit, Mozilla and Opera browsers support this, IE8 does not, though IE9 and up do).
Which may sound like a great boon for the user, but, as an application developer, you may wish to consider whether you in fact want to persist session data after a crash. A user may consider that when their browser crashes using a service like web mail or online banking at an internet café or other shared computer that their login details have been purged, but if these were stored in sessionStorage then the next user to launch the browser will resume the session that were current when the user crashed. Ruh-roh.
What good is sessionStorage?
So, what good is sessionStorage? Well, one very useful application would be to maintain sensitive information during a transaction, signup, sign in and so on, which will be purged as soon as the user closes a window or tab. It can be used to create a multi-page form or application, where the information in each page can persist, and then be sent to the server all at once when the transaction is complete. It also moves some of the heavy lifting for protecting sensitive data away from application developers to the browser developer.
Using sessionStorage
sessionStorage is a property of the window object in the DOM. Because it is as yet not universally supported, we'll want to check that this property exists before we use it:
if (window.sessionStorage) {
//we use sessionStorage
}
else {
//we do something else, perhaps use cookies, or another fallback
}Right, so now we have our sessionStorage object, how do we use it?
Key-Value Pairs
sessionStorage stores "key-value pairs". Each pair is a piece of information (the value), identified by a unique identifier (the key). Both the key and the value are strings (more on the implications of this in a moment).
We use the setItem method of the sessionStorage object to store data like so:
//get the value of the input with id="name"
var name = document.querySelector('#name').value;
//store this value with the key "name"
window.sessionStorage.setItem('name', name);
Now we've stored the value of the input "name" in an item of the sessionStorage object called 'name'. It will remain there until this window or tab is closed and it will then automatically be purged by the browser when the user closes the window or tab.
reading from sessionStorage
There's not much point in storing these details if we can't get them back at some point. We do this by using the function getItem of the sessionStorage object, using a single parameter, the key we used to set the item.
So, to get the value of the name, we'd use:
var name = window.sessionStorage.getItem('name');Non-existent items
Now, what happens if for some reason there's no item in sessionStorage with the key we are trying to access? In place of a string value, it returns null, not the empty string. So, it's worthwhile testing whether the result returned is not null before using it
var email = window.sessionStorage.getItem('email');
if(email!==null){
document.querySelector('#email').innerHTML = email;
}Saving Data Between Sessions
When information is less sensitive, it may make sense to store it between sessions. Particularly as web sites become more application-like, and can increasingly work offline, saving preferences or the state of a document can make for much better usability.
My HTML5 and CSS developer tools do just this, using localStorage. That way, when someone returns to the tools, the last gradient or layout or transformation they built is waiting for them.
Best of all, using localStorage, for persistence between sessions, is almost identical to using sessionStorage.
window.localStorage
Instead of the sessionStorage object of the window, we use the localStorage object. All the methods of localStorage are the same as sessionStorage.
- we set items with
setItem - we get items with
getItem
Because items on the localStorage will persist forever, we may want to delete them. We can do this with localStorage.removeItem(key), using the key for the item we want to remove.
If we want to delete the entire localStorage, we can use localStorage.clear(). But, be warned, anything your app has saved to localStorage for this user is gone for good.
sessionStorage in fact also has these methods, though we're less likely to need them, as all of a sessionStorage is discarded by the browser when a session finishes.
Gotchas, Tips and Tricks
sessionStorage and localStorage store all data as strings
As mentioned earlier, the values stored in local and session storage are strings, which has a number of implications for developers.
In particular, when we store boolean values, integers, floating point numbers, dates, objects and other non-string values, we need to convert to and from a string when writing to and reading from storage.
There's also a more subtle side effect of storing values as strings. JavaScript strings are UTF-16 encoded, which means each character is 2 bytes (in UTF-8 characters are one byte). This effectively halves the available storage space.
Private Browsing
Many browsers now have private (or 'incognito') browsing modes, where no history or other details are stored between sessions. In this situation, what happens with sessionStorage and localStorage varies widely by browser.
- Safari returns
nullfor any item set usinglocalStorage.setItemeither before or during the private browsing session. In essence, neithersessionStoragenorlocalStorageare available in private browsing mode. - Chrome and Opera return items set previous to private ("incognito") browsing commencing, but once private browsing commences, treat
localStoragelikesessionStorage(only items set on thelocalStorageby that session will be returned) but likelocalStoragefor other private windows and tabs - Firefox, like Chrome will not retrieve items set on
localStorageprior to a private session starting, but in private browsing treatslocalStoragelikesessionStoragefor non-private windows and tabs, but likelocalStoragefor other private windows and tabs
Getters and Setters
In addition to using getItem and setItem we can use a key directly to get and set an item in sessionStorage and localStorage, like so (where the key is "keyName"):
var itemValue = window.localStorage.keyName;
localStorage and sessionStorage Limits
The webStorage specification recommends browsers implement a 5MB limit on the amount of data localStorage or sessionStorage can save for a given domain. If you try to exceed the limit that various browsers have in place (for some browsers users can change this allowance) setItem throws an error. There's no way of asking localStorage for the amount of space remaining, so it's best to set item values with a try and catch for any error:
try {
window.localStorage.setItem(key, value);
}
catch (exception) {
//test if this is a QUOTA_EXCEEDED_ERR
}If the available space for this localStorage is exceeded, the exception object will have the name "QUOTA_EXCEEDED_ERR" and a code of 22.
As mentioned, in JavaScript strings are UTF-16 encoded, which means they are 2-byte. So, when saving the string "John", we are actually using 8 bytes, not 4. Which means instead of 5MB of storage space per storage area, we effectively have 2.5MB.
If the storage needs of your application are likely to exceed 5MB, then web databases are likely to be a better solution. However, the situation with web databases is complicated, with two different standards, one, webSQL widely supported but deprecated, and the other IndexedDB, currently supported only in Firefox, Chrome and IE10.
Storage Events
We can add an event listener to the window for storage events so that when a storage object has been changed (there's a reason for the emphasis) then we can be notified and respond to those changes.
window.addEventListener('storage', storageChanged, false);Here, when localStorage is changed (by setting a new item, deleting an item or changing an existing item) our function storageChanged(event) will be called. The event passed as a parameter to this function has a property storageArea, which is the window's localStorage object.
There are two things to be aware of with storage events.
- The event only fires if the storage is changed (not if it is simply accessed and not if we set an item to the same value that it currently has!)
- In the specification, the event is not received in the window or tab where the change occurred, only in other open windows and tabs that have access to this localStorage. Some browsers have implemented storage events in such a way that the event is also received by the window or tab which causes the change, but don't rely on this.
webStorage Performance
Of late, a number of high profile, widely read articles critical of localStorage have been published, centring on its asserted performance shortcomings. The key criticism relates to the fact that webStorage is synchronous. This means a script using sessionStorage or localStorage waits while getItem, setItem and other storage methods are invoked. In theory, this can impact both the browser's response to user input and execution of JavaScript in a page. In practice, I'd argue that this is not likely to be a significant problem for most cases.
I recently conducted some testing across a number of devices and browsers which demonstrates that even for poorly implemented code that does a very significant number of getItem and setItem operations, the performance of webStorage is unlikely to have significant impact.
Origin restrictions
We said earlier that that sessionStorage and localStorage are restricted to windows or tabs in the same domain, but in fact, the restriction is tighter than simply the top-level domain (such as webdirections.org)
To have access to each other's webStorage, tabs or windows must have the same top-level domain (for example webdirections.org), subdomains (for example test.webdirections.org), and protocol (https://webdirections.org has a different localStorage from http://webdirections.org).
At first glance this might seem overly restrictive but imagine john.geocities.com having access to the localStorage of fred.geocities.org?
Browser Support and Backwards Compatibility
localStorage and sessionStorage are widely supported, from IE8 upwards and in all modern browsers, including mobile devices.
There are also several polyfills that allow for webStorage in browsers which don't support it natively.
The Wrap
webStorage solves a long standing challenge for web developers - reliably and more securely storing data between sessions entirely on the client side. While there are assertions that performance limitations make localStorage "harmful", in the real world, services like Google and Bing are using localStorage and performance experts like Steve Souders and Nicholas Zakas defend and advocate their use. That's not to say webStorage is perfect or ideal in all situations. The synchronous nature of the API and 5MB limit per origin do mean that in certain circumstances an alternative may be required. webStorage is however eminently usable for a great many client side data storage needs.
Further Reading
- The ever reliable HTML5 Doctors on webStorage
- Opera Developer Network, another always useful place to visit, on webStorage
- All about local storage, from Mark Pilgrim's excellent "Dive into HTML5"
- Remy Sharp on the state of support for offline events, and some workarounds
- A couple of articles from the Mozilla Hacks blog on web databases, the Road to IndexedDB and an introduction to IndexedDB
- MSDN's introduction to IndexedDB (supported in IE10)
- An introduction to webStorage, also from MSDN
Our program
To give you some idea of how the program works, the conference has 3 tracks:- Design: user experience, design strategy, design process, design case studies, CSS3, interaction design, interface design, content strategy, usability, ethnography.- Development: JavaScript and HTML5, though feel free to pique our curiosity with ideas that fall outside these two very broad headings :)- Big Picture: interesting or unique perspectives at the edges of the web. What are the possibilities that are open to us as people who work on the web? What ideas are just over the horizon? Ideas that get the audience excited about working in this awesome field at this incredible time.Don't feel too troubled with trying to slot your idea into one of these if that doesn't come easily to you: this is a challenge we are used to!Most of our sessions have traditionally been 45-50 minutes long. This year we are also exploring the idea of having shorter sessions - 15ish minutes - in the dev track. This is the perfect amount of time to show off a neat JS library, or demo some other problem solving wizardry you have mastered. So if you've got an idea that you don't think is quite up for a 50 minute session, but would work in this shorter format, please do send it in!Where do I apply?
Do you think you've got what it takes? We definitely want to hear from you some time in the next couple of days then. It doesn't have to be a fully formed session proposal or anything like that, so don't labour it, give us as much detail as you can, and just get it in! To keep all these ideas in one place, please help me out by entering it via this form.Very much looking forward to hearing from you." ["post_title"]=> string(54) "Web Directions South 2012 - got an idea for a session?" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(51) "web-directions-south-2012-got-an-idea-for-a-session" ["to_ping"]=> string(0) "" ["pinged"]=> string(0) "" ["post_modified"]=> string(19) "2012-03-12 12:02:36" ["post_modified_gmt"]=> string(19) "2012-03-12 02:02:36" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=4050" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(1) "0" ["filter"]=> string(3) "raw" } [2]=> object(stdClass)#118 (25) { ["ID"]=> int(4017) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2012-03-08 14:42:12" ["post_date_gmt"]=> string(19) "2012-03-08 04:42:12" ["post_content"]=> string(9013) "Recently, Christian Heilmann and Taras Glek, both at Mozilla, posted articles critical of localStorage. The arguments in each of these really didn't gel with my experience, and both felt unduly alarmist ("considered harmful" as argued elsewhere really should be retired as a post heading). So, I wanted to run the ruler over the arguments, and see whether they had validity.Christian's and Tara's positions are very similar, so I'll address them via Christian's post. The essence of Christian's criticism iswe have to stop advocating localStorage as a great opportunity for storing data as it performs badly
Strong words indeed. Is there evidence to back this up?
Christian then outlines these concerns in more detail
localStorage is synchronous in nature, meaning when it loads it can block the main document from rendering
Now, this is certainly true, and in theory, we could block the main UI in most browsers (Opera threads the main browser UI separately from scripts), as well as blocking the remainder of a script which uses localStorage while it waits on a read or write. But, in practice, is this a big deal? I'm a bit of a fan of evidence and testing, rather than theory alone, so I set out to test this. You can follow along at home if you like
Reading, writing and arithmetic
Here's what I did. The test page allows you to specify what you want to save (as everything is saved to localStorage as a string, you just input a string value), and how many times you want to save it. Then, I just save the string that number of times to localStorage in a tight loop, measuring how long it takes, and report that back. Keep in mind this is very quick and dirty, and can fall over without any warning if you over fill your localStorage.
The test is really simple, but we should get a sense of how big a performance hit localStorage is. Here are results for writing 18bytes 10,000 times inside a tight loop (results are meant to be indicative), then reading them back out again
| Browser | Write 10K | Read 10K |
|---|---|---|
| IE10 | 1250ms | 24ms |
| Chrome (19) | 2097ms | 1578ms |
| Safari 5 | 58ms | 3ms |
| Firefox 8 | 232ms | 130ms |
| Opera 11.6 | 764ms | 597ms |
| iPhone 4G iOS 5.0 | 115ms | 82ms |
| iPad 1 iOS 5.0 | 102ms | 69ms |
At worst, we have a 2s wait (in, of all browsers Chrome 19, a developer preview). In many browsers the wait is less than a tenth of a second. Remember this is for reading or writing 10,000 times to localStorage.
What about for larger amounts of data? Here's the results for reading and writing 128 bytes
| Browser | Write 10K | Read 10K | Chrome (19) | 2081ms | 1514ms |
|---|---|---|
| Safari 5 | 57ms | 3ms |
| Firefox 8 | 250ms | 136ms |
| Opera 11.6 | 664ms | 614ms |
| iPhone 4G iOS 5.0 | 221ms | 112ms |
| iPad 1 iOS 5.0 | 161ms | 69ms |
So, in essence, we're really not seeing the performance impacted even when we increase the amount of data stored by a factor of 6 or so.
Given that writing 10,000 times to localStorage in a tight loop is I'd argue an edge case (and probably not advisable), on the face of it, the assertion that localStorage, being synchronous, and for that very reason has performance issues is largely misplaced in fact.
Christian continues
localStorage does file I/O meaning it writes to your hard drive, which can take long depending on what your system does (indexing, virus scanning…)
On a developer machine these issues can look deceptively minor as the operating system cached these requests – for an end user on the web they could mean a few seconds of waiting during which the web site stalls depending on what your system does (indexing, virus scanning…)
emphasis mine
Again in theory this looks worrisome, but I think in practice browser developers will use some sort of in memory caching and then asynchronously write to disk (actually based on my tests below I suspect that's what they are doing)
In order to appear snappy, web browsers load the data into memory on the first request – which could mean a lot of memory use if lots of tabs do it
They may do, but this is an implementation detail for browser developers, and regardless of whatever mechanism they implement for persistent client side data (IndexedDB, webSQL…) in order to appear snappy, some sort of memory caching will be required.
localStorage is persistent. If you don’t use a service or never visit a web site again, the data is still loaded when you start the browser
Again this is an implementation detail for browser developers, and I doubt browsers have to load the localStorage DBs for all pages ever stored when they launch. I can imagine all kinds of caching strategies to balance responsiveness with memory use, application developers (speaking as one for many years) do this sort of thing all the time.
But above all, in practice, are we actually seeing any of this taking place in the wild? Can Christian point to sites that are suffering because of these theoretical limitations in localStorage?
Christian then makes something of a curious leap when he argues
In essence this means that a lot of articles saying you can use localStorage for better performance are just wrong
So, when the likes of Steve Souders, and teams of very smart people at places like Google and Bing research and develop techniques using localStorage for client side resource caching, they are "just wrong" because in theory localStorage has performance issues. To be direct, I find this position baffling. Especially when not a single datapoint is quoted (the tests cases I developed here took in the order of an hour, so it's not overly taxing to do).
None of this is to say localStorage is perfect. Its limitations as to storage size, the fact that everything stored is simply a string, and that JavaScript strings are UTF-16, thereby halving the effective storage space are all real issues. Is the synchronous nature of localStorage an issue? Synchronous APIs are easier for developers to use (no need for callback functions), and so the idea of a simple client side storage mechanism, webStorage, coupled with a more sophisticated, high performance database standard (currently mired in standardization issues, far from localStorage's fault) makes excellent architectural sense IMO.
But frankly, Christian and Tara do developers no benefit asserting localStorage is "harmful", or that "there is no simple solution for local storage". localStorage is very widely supported (IE8 up), very straightforward to use, has excellent performance for most use cases, and has been implemented in a large number of real world projects, including by Bing and Google. Sure, if you're writing an app that does 10K simultaneous writes to a database, you'll have the chops to get around a 2s write time in the worst case, but you will doubtless have all kinds of other performance and memory use challenges. So by all means help developers understand the potential shortcomings of a given technology, outline the gotchas, and workarounds, and propose improvements. But do so based on evidence, and also with the acknowledgement that localStorage is here to stay, and that any alternative also faces many similar, as well as their own additional challenges, and in the case of client side storage is perhaps years away from the same level of support as webStorage. The perfect, as is often stated, is the enemy of the good, or adequate. Let's by all means make the adequate good, and the good excellent, but let's not wait around forever for the perfect. We'll be waiting a while.
Edited to correct Taras' name
" ["post_title"]=> string(36) "localStorage, perhaps not so harmful" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(35) "localstorage-perhaps-not-so-harmful" ["to_ping"]=> string(0) "" ["pinged"]=> string(0) "" ["post_modified"]=> string(19) "2012-03-09 08:25:42" ["post_modified_gmt"]=> string(19) "2012-03-08 22:25:42" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=4017" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(2) "17" ["filter"]=> string(3) "raw" } [3]=> object(stdClass)#119 (25) { ["ID"]=> int(4016) ["post_author"]=> string(1) "2" ["post_date"]=> string(19) "2012-03-08 13:45:10" ["post_date_gmt"]=> string(19) "2012-03-08 03:45:10" ["post_content"]=> string(20328) "The 8th of March is International Women's Day. In honour of the occasion I decided to create a listing of all the presentations by people who happen to be women that we've had at Web Directions conferences over the years.Many hours later I had come up with the list below.I invite you to sit back, take a little stroll down memory lane, and remember all the important things we've learned from these amazing women.Rachel Andrew spoke about Core CSS3.
Relly Annett-Baker told us about content strategy for web apps as well as microcopy.
Angela Beesley - spoke on wikis and community collaboration.
Jina Bolton told us how to create sexy style sheets.
Paula Bray on connected digital initiatives and strategy.
Wendy Chisholm told us all about universal access for apps.
Esther Derby showed what happens when agile meets UI.
Hannah Donovan told stories through design, designed without the browser and then reprised designing without the browser.
Natalie Downe told us the Lanyrd story.
Kimberly Elam showed us essential composition tools for web typography.
Ruth Ellison showed us how to integrate accessibility into design.
Anne Galloway took us to a 21st century bestiary.
Cheryl Gledhill showed us how to move our organisations to web standards and also took us beyond SEO.
Kelly Goto taught us about the iterative app, and how to design for lifestyle, and then showed us workFLOW.
Lisa Herrod showed us that usability is more than skin deep, that usability is for the rest of us, and then how to recategorise WCAG 2 using a role-based approach.
Rachel Hinman talked us through some mobile prototyping essentials.
Molly Holzschlag showed us some crimes against web standards.
Tara Hunt - told us all about Government 2.0.
Suze Ingram asked us if we would like service design with that?
Leslie Jensen-Inman introduced us to the Open Web Education Alliance.
Lynne D Johnson showed us how new media means new business.
Tania Lang taught us how to use AJAX to enhance user experience.
Divya Manian got creative with CSS3 and also showed us how to be active web developers.
Hilary Mason introduced us to machine learning for web data.
Juliette Melton has taught us how to do mobile UX research and how to conduct effective remote studies.
Zoe Mickley Gillenwater showed us to create effective and efficient designs with CSS3.
Diana Mounter talked us through deciding between custom and CMS.
Jackie Moyes showed us how to convert research findings into business speak.
George Oates showed us how web apps need developers and designers to work together and also told us all about human traffic.
Laurel Papworth has shown us the business of online communities, the business of being social and taught us all about social networks and mobiles.
Silvia Pfeiffer showed us how to take HTML5 video a step further.
Veerle Pieters showed us how to find creativity in the design process.
Deborah Schultz told us that it's the people stupid.
Teale Shapcott told us all about usability in agile environments.
Kaitlin Sherwood showed us the business and technology of mashups.
Rashmi Sinha talked us through the perils of popularity.
Kay Smoljak showed us how to start and run a successful web business.
Donna Spencer has given us an IA how-to, showed us how to get content right, how to keep it alive from cradle to grave and taught us about information seeking behaviours.
Nicole Sullivan showed us her CSS power tools.
Stephanie Sullivan-Rewis taught us all about CSS3, the web's Swiss Army Knife.
Kerry Taylor told us about semantics and sensors.
Jenny Telford showed us how the Australian Bureau of Statistics is opening up government data.
Gina Trapani showed us how Google opened up Gmail's web interface to any developer.
Lea Verou showed us how to master CSS gradients.
Sandi Wassmer taught us that inclusive design is for everyone.
Gian Wild showed us how she managed accessibility compliance for the Melbourne Commonwealth Games, introduced us to WCAG 2 and then showed us its hidden nuggets.
Alex Young taught us how E is for everywhere and then showed us multi-device, multi-role.
Indi Young told us all about innovation with mental models.
" ["post_title"]=> string(38) "In honour of International Women's Day" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(37) "in-honour-of-international-womens-day" ["to_ping"]=> string(0) "" ["pinged"]=> string(893) "http://www.webdirections.org/resources/rachel-andrew-core-css3/ http://www.webdirections.org/resources/hannah-donovan-telling-stories-through-design/ http://www.webdirections.org/resources/kelly-goto-designing-for-lifestyle/ http://south09.webdirections.org/program/w3ctrack#the-open-web-education-alliance http://www.webdirections.org/resources/lynne-d-johnson-opening-keynote-new-media-new-business/ http://www.webdirections.org/resources/george-oates/ http://south08.webdirections.org/?page_id=7#post-56 http://www.webdirections.org/resources/rashmi-sinha/ http://www.webdirections.org/resources/jenny-telford/ http://www.webdirections.org/resources/esther-derby-agile-meets-ui/ http://www.webdirections.org/resources/kelly-goto-workflow/ http://www.webdirections.org/resources/divya-manian-creative-css3/ http://www.webdirections.org/resources/laurel-papworth-the-business-of-being-social/" ["post_modified"]=> string(19) "2012-03-12 12:01:04" ["post_modified_gmt"]=> string(19) "2012-03-12 02:01:04" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=4016" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(1) "1" ["filter"]=> string(3) "raw" } [4]=> object(stdClass)#120 (25) { ["ID"]=> int(4010) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2012-03-07 14:24:54" ["post_date_gmt"]=> string(19) "2012-03-07 04:24:54" ["post_content"]=> string(2708) "If you just read our blog here, you might think it's been a little quiet since Web Dirctions South last year. But we have in fact just finished a 4 city roadshow with workshops by Andy Clarke and me (John Allsopp) as well as our second round of What do you know, this time visiting Sydney, Melbourne, Brisbane and Perth.We've always loved the coding side of the web here at Web Directions, and our conferences have always had a strong developer track. But with so much going on and so much on the horizon, we've decided that in 2012 developers need their very own event as well. So we're very excited to announce a brand new show, Web Directions Code, this year in Melbourne on May 23 and 24.Read on for more details, but take note, there will of course be Web Directions South in Sydney in October, as there has been since 2006!Why Web Directions Code?
As the web becomes increasingly application like, and the capabilities of HTML5 and other web technologies become ever more sophisticated, we thought it was time to put together an event that focusses entirely on code. Web Directions Code features a fantastic international and local lineup, including- Paul Irish (HTML5 Rocks, ModernizR, HTML5 Boilerplate and much more)
- Divya Manian (HTML5 Please, CSS3 Please, HTML5 Boilerplate)
- Rob Hawkes (Game developer and Mozilla technical lead on gaming)
- Dave Johnson (originator of the phoneGap project)
The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspectLately, there has been a lot of concern expressed, by intelligent, experienced people I have a lot of respect for, about the future of the web, about its very viability.I've engaged folks like Joe Hewitt in strenuous, but I believe healthy and important debate about these issues.As I considered in my recent Web Directions presentation, A Dao of Web Design Revisited (article, slides and audio recording coming soon), I feel that many of these concerns strangely, uncannily, echo those which prompted my original Dao of Web Design article back in 2000.A recent tweet by Aral Balkan (once again, a passionate, intelligent contributor to the web, who has spoken at our events, and hopefully will do again), retweeted by Lea Verou (another generous contributor to the web, and again, one of our past, and I hope future speakers) really captured for me the essence of the issue
#oneversion #manifesto My websites will only support the latest versions of browsers. It's the browser makers' duty to get users to upgrade.Aral Balkan, Twitter, October 20 2011So, what does this tweet, and predictions of world population have to do with one another?I love the shiny stuff of the web - the gradients and animations, the transforms. As someone who has developed for the web for nearly 18 years, the ever increasing sophistication of the DOM, of JavaScript, the increase in the speed of JS engines, of rendering, the arrival of mobile platforms, and micro-mobile platforms for the web excite me as a user and a developer. But, they aren't what gets me up in the morning. They aren't what fires me up like no other platform before (I built my first Mac apps in 1986, and have had commercially available Mac OS apps continuously since 1994, and Windows apps since 1996).What really, at the not so tender age of 45, keeps me as passionate and excited about building stuff as I was when about 16 and got my first TRS80 clone is the potential for the web to transform our world for the better. And overarching all this is the question, the challenge, how do we get the next 2 billion online, and ultimately, the next 6 billion people online?This might not float your boat. And that's fine. You might consider it an ideological position. And that's your prerogative. But I know I'm not alone in believing that the potential, the promise, and in the face of overwhelming planet-wide challenges - anthropogenic climate change, global pandemics to name just two which our generation, and particularly my children's generation will have to increasingly confront - the necessity of bringing our planet together, and enabling all of us to collaborate, share, communicate, without the friction of borders, is something only the web can hope to achieve.Universality is a founding principle of the web. It is the manifesto the web has been built on, and I believe one of the key drivers of the almost unimaginable success of the web over these last two decades. We ignore that at the web's peril.The web alone, not iOS, or Android, or Windows Phone, or any other platform can possibly connect the next 6 billion. Yes, some, many of those 6 billion will be accessing the web via iOS, some via Android devices, some Windows Phone.But, this next six billion is children in rural India, Africa, China where access to power, and networks, may be intermittent. It's someone in Sumatra at a decade old Wintel box. It's people who speak hundreds of different languages, with dozens of different writing systems. It's people who are the first in their family to be able to read and write. It's the 20% of people worldwide who can't read or write. Yet.So, to say "My websites will only support the latest versions of browsers", you are in a sense saying, "I'm going to make the fact that developing for the web is harder than it would be if I concern myself with browsers other than the latest is not my problem, and not even the browser makers problem, it's the problem of the next 6 billion. It's not my problem, it's the problem of the child in rural India, Africa, China."The truth is, the challenge of universality is daunting. It is hard work. But to me at least, paying this forward is the quid pro quo of the enormous privilege I've been granted to work on the web, which has given me fascinating well paid work, connections with thousands of intelligent, passionate, generous people around the world, and the opportunity to participate, in however insignificant a way, in something genuinely extraordinary, something unique. I can pay this forward by including rather than excluding people. By, in my own small way, helping ensure that the next 6 billion will be able to share in the privilege that I, you and the first 2 billion share in.Which is not to say we shouldn't continue to develop the capabilities of web technologies. It is not to say we shouldn't continually explore what these technologies enable us to do.But to me at least, we owe it to the web to do this in a way that is generous to the web in the way the web has been generous to us.To reformulate the now famous question Steve Jobs asked of John Sculley:Do you want to make shiny products for the privileged for the rest of your life, or do you want to come with me and change the world?" ["post_title"]=> string(18) "The Next 6 Billion" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(18) "the-next-6-billion" ["to_ping"]=> string(0) "" ["pinged"]=> string(122) "http://www.webdirections.org/blog/the-web-is-a-different-problem/ http://www.webdirections.org/blog/css3-radial-gradients/" ["post_modified"]=> string(19) "2011-10-20 14:43:34" ["post_modified_gmt"]=> string(19) "2011-10-20 04:43:34" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=3734" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(2) "40" ["filter"]=> string(3) "raw" } [7]=> object(stdClass)#123 (25) { ["ID"]=> int(3710) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2011-10-17 13:16:40" ["post_date_gmt"]=> string(19) "2011-10-17 03:16:40" ["post_content"]=> string(3964) "When you work on something for an extended period of time but which itself lasts itself only a brief moment, such as Maxine and I do with Web Directions, there's an intensity to the event itself, and the strange mixture of relief (and exhaustion) coupled with nostalgia when it has come and gone.Luckily, through photos, tweets, blog and Facebook posts and even the odd video, increasingly an event like Web Directions is captured in more than just our memory.
Web Directions South, held last week, was the sixth annual Web Directions event we've run in Sydney, and though likely we feel this every year, it was in our estimation, and indeed of so many we spoke to, and based on many tweets we've read the best we've done by far.The atmosphere was incredibly positive, (reflected in everything from the twitter stream, to the crowds who refused to go home, staying long into the night at the closing night party).A new generation of partner companies, almost all from Australia and New Zealand, and most of which did not exist when Web Directions started back in 2006 companies shouted great parties, competitions, and most importantly showcased the sophistication and breadth (as well as spirit of fun) of the Australian web industry.Similarly, while many of the speakers, local and international, may have been unknown to our audience, they excelled–educating, inspiring and entertaining. From opening keynote to closing, never have we seen such uniformly positive responses to a program.Every presentation we had the privilege to see, as well as all those we didn't but which we heard amazing things about, was world class.Whether you were there, or missed out, as the podcasts and slides from presentations come online, we'll tweet about them (so if you don't already, you really should follow us on Twitter. Meanwhile, you can catch up on hundreds of past presentations from our conferences around the world from the last several years.And, as a special treat, coming soon is a slickly produced video version of James Bridle's amazing closing keynote, thanks to Hunting with Pixels.In the meantime, Ben Buchanan has already made available his traditional big stonking post™ with detailed live notes, links, and photos from a dozen presentations. So, if you missed it, and can bare to see what you missed out on, it's a great place to start.Thanks!
- A huge thanks to our wonderful sponsors, partners and exhibitors
- To our incredibly generous speakers
- To Cameron Adams for the opening sequence, setting a new standard for creativity with Web Technologies
- To the Design Computing program at the University of Sydney for their incredible exhibition of interactive technology
- To the W3C Australia Office for once again hosting the W3C South Track
- And not least to the fantastic, positive, open minded, engaged audience.
One of the most persistent criticisms of web technologies is that they evolve slowly, indeed, too slowly. Often the argument is raised that the process of standards is antithetical to "innovation" (for innovation read "making cool stuff up").
To contrast with this glacial change, we're typically pointed toward the wonders of platforms like iOS and Android, where change takes place at breakneck speed. Or we're pointed toward any number of open source projects, for example SASS, to demonstrate that we can evolve web standards faster outside the W3C (afterall, CSS hasn't kept place with SASS/LESS... therefor the W3C is clearly useless)
Pretty sure that already happened. SCSS, Coffeescript, etc. The W3C doesn't have influence anymore AFAIK
And occasionally, this erupts in an orgy of revolutionary fervour, where we need to man the barricades, and
dissolve the W3C, and run the web like an open source project. No more specs, just commits. Does Linux need a standards body?
I guess Joe hasn't taken a look at the WHATWG's "HTML"lately. An unversioned. constantly, continuously evolving potage, wherein creeps all kinds of at the every least questionable "innovations", whose trajectory is unburdened by such trifling challenges as the need for intellectual property policies, or getting anyone to actually implement their stuff.
But let's step back a pace. Is there really actually such a problem here? And if so, exactly what is that problem? And, are the proposals, to the extent they do exist, likely to help, or make matters worse?
Firstly, it is fair to say, that for the best part of the first decade of this century, we saw considerable stagnation in the development of web standards. The W3C went down the rabbit hole of XHTML2, and CSS 2 almost entirely stagnated (I'l have more to say on that in a second). During that time, innovation came almost entirely from developers discovering techniques which worked with existing browser capabilities, and through JavaScript libraries.
The last three or four years have however seen an explosion of innovation at the browser level. This has included:
- Huge improvements in the performance of JavaScript engines
- Fantastic new CSS features, that have within a couple of years landed in all modern browsers
- Sophisticated new APIs across a broad range of functionality, again now widespread across most if not all modern browsers (Selectors API, Geolocation, offline, localStorage, 2D and 3D canvas to name just a few)
Before we throw the baby out with the bathwater, we should ask, is there a problem at all? Are we really seeing innovation - from standards proposal to widespread browser adoption in the order of 10 years?
Let's take a look at an actual example. Dave Hyatt announced CSS Gradients in WebKit nightlies April 14th 2008. More or less simultaneously, Apple formally proposed these as part of CSS3. Gradients have been supported in Firefox since 3.6 (released Jan 2010), Opera since 11.1 (final release mid 2011), Internet Explorer 10 (developer releases early 2011). So, from first released and proposed as a standard to very widespread adoption in around 3 years.
We've seen similar timeframes for other CSS features, as well as "HTML5" features like the Selectors API, appcache, geolocation, localStorage.
So, to put it bluntly, I think the problem is overstated. We seem to have arrived at an approach that both enables the exploration and implementation of novel features in browsers, which are also widely adopted across browsers. It's that second part that is most important, and I'll return to it in a moment.
But I also want to quickly address why I think we seem to have thrown off the stagnation of previous years. I'd argue it comes down to stepping back from the monolithic approach of earlier W3C recommendations (CSS 1 and 2, XHTML2, SVG), toward a much more modular approach, as exemplified by CSS3, but as also seen with geolocation and the selectors API. A corollary to this is, I'd argue, that HTML5 is too monolithic and needs to be fragmented (which in some ways is happening).
But back to the real issue. The web is a different problem. It makes little if any sense to compare innovation of the web ecosystem with that of iOS, Android or other platforms. The web faces challenges far far greater (and has goals far more important). A platform such as iOS can abandon legacy applications, content and hardware, (along with their users) with little compunction. It can (and does) make developers and content creators wishing to participate jump through any number of hoops. It has a single dictatorial decision maker, beholden to no one, and nothing other than itself. And it generates extraordinary revenues, which can be reinvested into the ongoing development of the platform.
The web is different. It values interoperablity, backwards compatibility. It's goal is to bring access to the same information to billions across the world, on all manner of devices. Its custodians are, in my opinion, scandalously under-resourced, given just how much wealth the web has created for so many, perhaps above all Google and Apple.
Without the web, Google would not exist, and Apple's core engine of growth, iOS devices would essentially not either.
So, rather than generally criticising the W3C, or going so far as calling for its dissolution, we should focus on how well in many ways it has done an almost impossible task - getting companies which are fierce commercial rivals to sit down, work together and agree on core technologies they will each, and all, implement, even while at the same time, these same competitors are involved in significant legal conflicts with one another.
Can the W3C be improved? Certainly. But before suggesting solutions, let's identify, and demonstrate with evidence, genuine problems. Then, when devising solutions, it pays to ask what evidence there is that those solutions will not only solve the problems identified, but also ensure the ongoing cooperation that has given rise to the web.
" ["post_title"]=> string(30) "The web is a different problem" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(30) "the-web-is-a-different-problem" ["to_ping"]=> string(0) "" ["pinged"]=> string(0) "" ["post_modified"]=> string(19) "2011-09-21 15:33:11" ["post_modified_gmt"]=> string(19) "2011-09-21 05:33:11" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=3689" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(2) "18" ["filter"]=> string(3) "raw" } [10]=> object(stdClass)#126 (25) { ["ID"]=> int(3664) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2011-09-21 10:59:43" ["post_date_gmt"]=> string(19) "2011-09-21 00:59:43" ["post_content"]=> string(37539) "One of the most powerful features of CSS3 are transforms, which allow us to take any element in an HTML document, and while not changing its effect on the page layout, rotate it, translate it (move it left, right, up and down), skew it and scale it. CSS3 provides both 2D and 3D transforms, but while 2D transforms are supported in all modern browsers, including IE9 and up, 3D transforms are currently only supported in Safari, Chrome and IE10.
In this article, we'll take a look at how to use 2D transforms and the various transform functions (such as scale and rotate). We'll also, as always, look at some gotchas when first working with transforms, and once again, there's a tool to help you play with transforms (I developed it some time ago, but I've updated it significantly for this article).
What transforms do
As in many cases, an example is worth a thousand words, so let's take a look at a couple, to illustrate how transforms work, as well as some of their most important aspects.
When you click or tap the button below, this paragraph is rotated by 45 degrees. It's also scaled to 75% of the size it would otherwise be. However note how the rest of the page layout is not affected. That's because transformations don't change the box model of an element, and so leave the page layout unchanged.
This element has been skewed horizontally and vertically. You can still select the text, and otherwise interact with the element, as you would if it weren't transformed.
Transforming elements
The syntax for CSS transforms is in many ways straightforward. There are just two new properties to contend with - transform, and transform-origin. The first specifies the transforms we want applied (scaling, rotating and so on), while the second, which is optional, specifies the origin for this transformation (for example, do we rotate around the middle of the element, its top left hand corner, and so on).
The transform property
The transform property takes as its value one or more transform functions, which each specify a transformation. Let's take a look at each of these different functions in detail. Functions all take the form of a function name, with a value inside round brackets (as indeed do all CSS functions). So for example, we translate horizontally with the function translateX(200px).
OK, enough preliminaries, let's start in with some actual transforming. We'll start with the translate function.
translate
The translate function moves the contents of an element to the left (negative values) or right (positive values), and/or upwards (negative values) and downwards (positive values).
translate takes one or two comma separated values. The first is the horizontal translation value. If there is a second, this is the vertical translation value, while if there is only one value, then the vertical translation is zero (that is, the element will only be translated horizontally).
In addition to the translate function, there are the related translateX and translateY functions, which only translate an element horizontally, or vertically, respectively. Translate functions take length or percentage values, and like CSS properties, require units for values other than zero.
But enough theory, why not have a play with them?

transform: translate(0, 0)
We mentioned a moment ago that transforms don't impact the layout of a page. There's one area where this is not exactly true. If you translate the above element completely to the right, you'll notice a horizontal scrollbar appears (or the page can now be scrolled to the right). While the page layout has not been changed, the overflow property of the transformed elements containing element is affected by transforms (Safari on Mac OS X Lion is an exception to this, and perhaps indicates where this sort of UX is headed, but for now, in desktop/laptop browsers other than this, the addition of a scrollbar will impact page layout).
This element is scaled by 200% when you click the button below. Its containing div has an overflow: auto. So, scrollbars appear when the element is transformed. In many browsers this will cause the page to reflow.
scale
The scale function lets us zoom an element up or down in size (all the while maintaining the dimensions of its box for the purposes of the layout of the page). The function name is scale, and it takes a number value-with a value of .5 meaning "scale to half the current size", and of 2 meaning "scale to twice the current size", and so on. So, if we want to make an element 75% of its current size, we use the property transform: scale(.75). And that's really all there is to it. Why not have a play with it?

transform: scale(1)
It's also possible to scale horizontally and vertically independently from one another, by using two comma separated numerical values for the scale property. The first value scales an element horizontally, and the second, vertically. Let's take a look.

transform: scale(1, 1)
As with translate there are two related scale functions, scaleX, and scaleY. scaleX scales only horizontally (scaleX(2) is the equivalent of scale(2, 1)), and scaleY, which scales only vertically (scaleY(2) is the equivalent of scale(1, 2)).
rotate
Rotating text and images is a common page design technique, until now difficult to achieve on the web without considerable hackery, if at all. Transforms make rotating an element easy.
The rotate function takes an angle value. If you've worked with CSS linear gradients, in particular the newer syntax, then you'll have seen angle units before. There are several ways you can specify an angle in CSS.
degrees
As you most likely remember from school math, there are 360 degrees in a circle. So, when specifying a rotation of an element, 90 degrees is a quarter turn clockwise, 180 degrees is a half turn, 270 degrees is three quarters turn clockwise, and 360 degrees is a full revolution. Here these are below.
rotate(90deg) rotate(180deg) rotate(270deg) rotate(360deg)
Of course, it is possible to rotate an element by an arbitrary angle, for example 34.6 degrees, like so
rotate(34.6deg)
But, there are other ways we can specify rotations.
- turns
- perhaps the simplest way to specify a rotation is with the
turnvalue. We can rotate an element a quarter turn clockwise with the functionrotate(.25turn), half a turn with .5turn, three quarters of a turn with .75 turn, and a whole turn with the functionrotate(1turn). WebKit and Opera support theturnvalue, while Firefox (version 6) does not.
rotate(.25turn) rotate(.5turn) rotate(.75turn) rotate(1turn)
For completeness, it's worth noting that there are two other possible angle units-radians (rad), and gradians (grad). Briefly, there are 400 gradians in a full rotation (so one grad is slightly larger than one degree), while there are 2π radians in a full rotation (radians are widely used in mathematics).
Now, if you think about rotating an element, then you might wonder what point of the element the rotation takes place around. For example - is it the center? Or one of the corners?
This element rotates around the top left hand corner when you click or tap and hold it. (transform: rotate(360deg))
We can in fact specify where the rotation (and as we'll soon see, any transformation) takes place, using the transform-origin property. transform-origin takes two length or percentage values, which specify the horizontal and vertical "origin" of the transformation. In the above example, we have transform-origin: 0 0. If we want to rotate around the center of the element, we use transform-origin: 50% 50%, while to rotate around the bottom right of the element, we use transform-origin: 100% 100%.
This element rotates around the center of the element (transform-origin: 50% 50%; transform: rotate(360deg)).
This element rotates around the bottom right hand corner of the element (transform-origin: 100% 100%; transform: rotate(360deg)).
(You might be able to guess that we can animate transformation changes, like we can most other CSS property changes using CSS Transitions. For more on this, see my article on CSS transitions and animations.)
So, let's now put rotations and transform origin together so we can play around with them.

transform: rotate(0); transform-origin: 0 0
transform-origin can also be specified with keywords, in place of percentage (or length) values. As with background-position we can use left, center or right for the horizontal origin position and top, center or bottom for vertical. Where only one value is used, this applies to the horizontal origin, and the vertical is 50% (or center). Where no transform-origin is specified, its default value is 50% 50%.
skew
the last of the 2 dimensional transform functions is skew. Skewing is typically explained in mathematical terms, but if you recall a little school geometry, it isn't really that complicated. Horizontal skew (the skewX function) takes the box of an element, and while the top and bottom edges remain horizontal, tilts the left and right edges by the specified number of degrees to create a parallelogram. Similarly, a vertical skew (skewY), leaves the left and right edges vertical, and creates a parallelogram with the top and bottom edges rotated by the specified number of degrees. And when you skew both horizontally and vertically, you combine both of these (where it can get really quite tricky to work out what's going on). Does that help? You can play with the values below to get a sense of how skewing works in practice, including how it can create apparent 3D effects.
The skew function, like most other functions, takes one or two values. As with rotate, they are angle values, and where there are two values, they are comma separated.

transform: skew(0, 0)
Once again, as with a number of the other transform functions we've seen, there are two related functions - skewX and skewY, which each skew only in one dimension, and which take only a single, angle value.
Complex transformations
While you'll likely never need to use it, underneath all these functions is the core of CSS transformations, based on the mathematical concept of a transformation matrix. Each of the functions we've seen can be represented by a matrix value. While it's firmly in the domain of mathematics, we can for example represent the function scale(1.5,1.2) by the matrix
1.5 | 0 | 0 0 | 1.2 | 0 0 | 0 | 1
and directly apply this matrix value using the matrix function, like so
transform: matrix(1.5, 0, 0, 1.2, 0, 0)
this element has been scaled using a matrix function on the transform property.
The transform matrices for various functions are described in the SVG specification.
Multiple transforms
It's possible to apply multiple transform functions at once to an element. For example we can rotate, translate and skew an element with the single transform property:
transform: scale(0.75) rotate(45deg) translate(132px, -149px) skew(32deg, -32deg);
Click the button below to apply several transformation functions simultaneously, with the property transform: scale(0.75) rotate(45deg) translate(132px, -149px) skew(32deg, -32deg);
There is however a complicating factor, which we look at in a moment.
The Transformer Tool
Hand coding transforms, as with much to do with CSS3, is an increasingly complex process. Not only is there a good deal of syntax to remember often for quite straightforward effects, but the outcome of at least some transforms isn't necessarily obvious to most of us simply by looking at the CSS. So, to help you learn, and explore, CSS transforms, I've developed a 2D transform tool (there's a 3D one as well, but we'll cover 3D in a later article).
If you're keen to explore transformations in more detail, and how they can be made to work together, head over and take a look, and as always, let me know what you think via twitter.
Gotchas, tips and tricks
Its fair to say that while now widely supported in modern browsers, 2D CSS transforms are still experimental, and quite quirky. Here are some of the difficulties you might encounter, and some tips and ideas for working with 2D transforms.
vendor specific prefixes
Reflecting the experimental nature of transforms, all browsers require vendor specific prefixes for the transform and transform-origin properties (note that function names are not prefixed). As all modern browsers including IE9 and up support 2D CSS transforms, using these transformation properties can get more than a little unwieldy. For example, the CSS for a simple rotation around the center of an element, if we include all vendor specific properties, would be something like:
img{
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-o-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg); //always include a standard for last
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
-o-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
}
You might find the use of a CSS preprocessor like LESS or SASS worth exploring, as they can make stylesheets far more manageable when you use a lot of vendor prefixing.
Transforming inline elements in webkit
According to the current version of the specification, CSS Transforms should be applied to inline, as well as block elements, but while Opera and Firefox both correctly apply transforms to inline elements, WebKit browsers (Safari 5.1, Chrome 15) currently don't.
A workaround for this is to give inline elements which are to be transformed display: inline-block, which won't affect how they are laid out in the page, but will enable these browsers to transform them.
Translating rotated content
One subtle aspect of multiple transformations is that functions are performed in sequence - from first to last in the list. The order that you specify functions can make a difference. Take a look at the following paragraphs. Both have the same scale, rotate and translate functions applied. But, in the first, the element is translated to the right, while in the second, it is translated to the left. What's going on?
Clicking the button below applies several transformation functions simultaneously, with the property transform: scale(0.75) rotate(180deg) translate(-100px,0)
Clicking the button below applies several transformation functions simultaneously, with the property transform: scale(0.75) translate(-100px, 0) rotate(180deg)
Transformations don't take place in an absolute coordinate space. Rather, "up", "down", "left" and "right" are relative to the current rotation (but not skew) of the element. So, if an element is rotated half a turn, and so is "upside down" then translate(0, -100px) moves it down the page 100px. If it's rotated a quarter turn to the right, it's moved to the left. Similarly, translating "horizontally" is always relative to the element. So, if it's rotated by 180 degrees, then translate(100px, 0) moves the element to the left. In short, the X and Y axes of a transformation are relative not to the page, but the element's current rotation.
Interacting with transformed content
While the box of an element isn't changed by a transformation, elements that are transformed display various quirks when it comes to mouse events in various browsers, most likely due to the still experimental nature of transformations. Take the element below. It rotates a quarter turn clockwise when the mouse is over it, and then back to its original rotation when the mouse is out of it.

Now, if the box of the element isn't changed, then when rotated, hovering over any of the image which is outside that original box should not trigger a mouseover event, and so the element should rotate back to its original position. However, as makes intuitive sense, hovering over those parts of the rotated element that are outside the original box does cause mouseover events to be fired. But in addition, mouseover events are also fired when you hover over that part of the element's original box which no longer has content because it has been rotated away. And if you move your mouse around the element, you'll find in all browsers various locations where the rotation abruptly, though unintuitively, changes. Similar behavior can be observed for translation and other transformations.
In light of this, I'd suggest being extremely wary of transforming elements which users will interact with given the current state of browser support for transformations.
Overflowing and transformations
We mentioned earlier that while transformations don't effect the box model of an element, and so leave the layout of a page untouched, they do effect the overflow, and if we have overflow: auto, they can in fact impact the page flow.
In the element below, we have an image inside a div. The div has an overflow:auto. When we hover over the element (apologies to touch device users), the contained image scales up in size.

Now, in most browsers on most platforms (Safari on Mac OS X 10.7 is an exception) the browser adds a horizontal scrollbar to the div when you hover over the image, which adds to the height of the element, reflowing the page below it. Just something to be aware of.
CSS Positioning and Transforms
While I was writing this article, CSS legend Eric Meyer posted a detailed critique of one interesting aspect of transforms.
It has to do with the positioning of elements. When we absolutely or fixed position an element, and give it say a top and left, these positions are offset from their containing box - which is not necessarily their parent element. Rather, the containing box is the first ancestor which itself has either relative, absolute or fixed position. However, adding a transform to an element also makes it a containing block for its descendent elements! As Eric observes, this can particularly cause difficulties with fixed positioning. Rather than rehash Eric's detailed thoughts, I'll let you head over and read them first hand.
General Rendering Problems
On different browsers you'll find various rendering problems with transformed content. For example, with Opera, rotated text appears to be rendered in a lighter font than the same text when it isn't rotated. I've also seen redrawing problems when rotating text in WebKit browsers. In Safari on iOS 4, rotated text doesn't necessarily align smoothly along its baseline. None of these are necessarily deal breakers, but it's worth keeping in mind. Transforms are still experimental, so don't necessarily expect them to be perfectly supported in all circumstances just yet.
Hardware Acceleration
There's a widely held belief that at least some browsers hardware accelerate the rendering of CSS transformations (hardware acceleration involves the CPU handing off execution of certain types of calculation to the GPU, which can increase rendering performance significantly, particularly on mobile devices).
At present the current state of hardware acceleration for CSS transforms across all browsers and devices is difficult to pin down. A webkit engineer I tracked down confirmed that current versions of Safari (5.1 on the desktop, iOS 4), but not necessarily other WebKit browsers:
- animated 2D transforms in Safari are always hardware accelerated
- using 3D for static 2D transforms may improve performance, but may also increase memory use - a real issue for memory limited mobile devices in particular
Now, haven't we just spent however much effort covering 2D transforms? How could this possibly help? Well, as we'll see in an upcoming article on 3D transforms, we can use 3D transforms to do 2D transforms (for example, there's rotate3D). If squeezing maximum performance for iOS devices is a need, then it may be that using a 3D version of a transform will help.
It's also worth noting that issues around what aspects of CSS transforms are hardware accelerated and how that acceleration works are implementation details in specific browsers, and not specified as part of CSS Transforms. As such, across browsers there's likely to be little uniformity of approach to hardware acceleration, and even from one version of a browser to the next, approaches may change.
Backwards Compatibility
One of the strongest aspects of CSS3 features like gradients, border-radius, shadows and the like is that they have been typically designed so as to be easily used in a way that is backwards compatible almost automatically, or provided we keep a small number of potential challenges in mind. For example, with gradients we need to ensure we have a fallback background color or image for when gradients aren't supported. For text-shadow, we need to ensure that the contrast between the text and the element background is sufficient when the shadow is not drawn.
Of all new CSS3 properties, transform is the one where backwards compatibility is the most difficult to ensure. Ironically, it's precisely because transforms don't affect the page layout that they cause such difficulty. For example, in order to accommodate say a heading rotated 90 degree to run horizontally along the left hand side of the text which follows it, we need to create whitespace to ensure the heading does not overlap the text. We might do that with margin, or padding. We're also likely to want to remove the whitespace where the heading has been rotated away from, by, for example, using negative margin on the paragraph following the heading. But, what happens if transforms aren't supported? The heading text will be overlapped by the paragraph below it.
In order to use transforms for more sophisticated page layout along these lines, a solution like Modernizr, which enables different CSS to be applied based on the support, or absence of support for various CSS3 features like transforms is indispensable.
Transformations are typically most easily used in a way that is backwards compatible where animated transforms create a transition between states in an application. We're all most likely used to sliding or flipping transitions between states in iOS and other mobile apps. CSS transforms can be used for these transitions, in conjunction with CSS Transitions, and where transforms aren't supported (in which case it's unlikely transitions will be as well), your users simply see an abrupt change in state.
However you plan to use transforms, as with every other aspect of web development, keep in mind those browsers which don't support them, and ensure your user's experience isn't diminished to the point where information or functionality is denied them because their browser doesn't support it.
Browser Support
As we mentioned, despite the still rather experimental nature of support for CSS 2D Transforms, they are now widely supported in modern browsers including:
- Internet Explorer 9 and up
- Firefox 3.5 up
- Safari 3.2 up
- Chrome 10 and up
- Opera 10.6 and up
- iOS 3.2 and up
- Opera Mobile 11 and higher
- Android 2.1 and up
So, support is widespread, and constantly improving. While there are definitely challenges associated with using 2D Transforms, they're a powerful, and worthwhile addition to the repertoire of developers, and will only gain in value. What are you going to do with them?
More Reading
- The 2D Transforms Specification
- MSDN on Transforms - great to see the IE love for them!
- Opera's developer center on, you guessed it, Transforms
- On Transformations in Internet Explorer (before and after IE9)
- Bezier curves
- Accessing the gyroscope and accelerometer in mobile browsers (watch a live web based take battle built specially for the night!)
- WCAG ARIA
- Mobile HTML emails (our Sydney winner!)
- CSS3 Media Queries (our Melbourne winner, and great fun)
- Parallax effects in CSS
Principle 2.2. Degrade Gracefully
So, what happens if we use this feature in current browsers? Well, firstly none of these support the feature whatsoever. But here's an example I tried in a number of current browsers<section>
<style scoped>
p { color: red; }
</style>
<h2>How does it work?</h2>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
</section>
<p>other stuff</p>
And here's what happened.Safari 5.1, Opera 11, Chrome, Firefox 6, and IE7 up all recognise the style element, and then style both p elements red - so scoping is not honoured - let's call this not graceful degradation.Principle 2.3. Do not Reinvent the Wheel
We currently can scope style in an HTML document. We use these new fangled things call CSS selectors. Even without touching the HTML above we could style the paragraphs in the given section like sosection:first-of-type>p{
color: red
}
Or we could add class or id as appropriate to our section. I'd need to see a pretty compelling use case here, that demonstrates a need that can't otherwise be solved using existing CSS/HTML practices and technologies, before beginning to even think there's a problem to be solved, let alone that this might be a good solution to that problem.Principle 3.1. Solve Real Problems
In over 15 years of web development, I've not seen a problem this is a solution to. If you are going to violate widely held long standing best practice, you need to do a bit better than invent a rare problem, then solve it in a way that opens the floodgates to terrible practice.Truthfully, and I have paid a fair bit of attention for close to 15 years in the CSS space, I have never ever once heard anyone articulate this need. Sure, it's probably happened, but it's far from a pressing concern.Principle 3.2. Priority of Constituencies
In case of conflict, consider users over authors over implementors over specifiers over theoretical puritySo, users get broken pages in older browsers with style scope. Authors who want to use it would need to hack around the fact that this not only doesn't work in older browsers, it causes rendering challenges. Seems we are focussing at the level of theoretical "purity", or maybe, the whims of specifiers here.Is this what versionless HTML development is about? Design principles violated left right and center, and an unwanted, unneeded and harmful addition to HTML, seemingly at the whim of one of the authors of HTML5?And the first place we hear about it is in a draft of the future HTML specification. I wonder what other hidden gems await discovery in that document?You know, it's not like there aren't some pressing real world problems to there for HTML to address.If you feel remotely strongly as I do, you can submit a comment on the draft HTML specification at the WHATWG here." ["post_title"]=> string(72) "On the (abominable) proposed HTML5 "scoped" attribute for style elements" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(68) "on-the-abominable-proposed-html5-scoped-attribute-for-style-elements" ["to_ping"]=> string(0) "" ["pinged"]=> string(93) " http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#attr-style-scoped" ["post_modified"]=> string(19) "2011-09-15 13:13:10" ["post_modified_gmt"]=> string(19) "2011-09-15 03:13:10" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=3646" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(1) "7" ["filter"]=> string(3) "raw" } [13]=> object(stdClass)#129 (25) { ["ID"]=> int(3642) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2011-09-06 09:55:59" ["post_date_gmt"]=> string(19) "2011-09-05 23:55:59" ["post_content"]=> string(687) "There's not necessarily a lot of whimsy in the world of web standards. A great deal of value, a lot of hard work by really smart people, but not whimsy.Well, recently there's been a little bit of whimsy in the otherwise dry, but very useful CSS3 Text module, with the "bikeshedding" property. But sadly, no more. bikeshedding is now more prosaically named "white-space-collapsing", and I for one think this is wrong. So wrong, that I'm campaigning for the reinstatement of the bike shedding name.If like me, you want to bring a little bit of whimsy back to web standards, then join the campaign by commenting below, and bring back bikeshedding!" ["post_title"]=> string(42) "Bring back the CSS bike shedding property!" ["post_category"]=> string(1) "0" ["post_excerpt"]=> string(0) "" ["post_status"]=> string(7) "publish" ["comment_status"]=> string(4) "open" ["ping_status"]=> string(4) "open" ["post_password"]=> string(0) "" ["post_name"]=> string(41) "bring-back-the-css-bike-shedding-property" ["to_ping"]=> string(0) "" ["pinged"]=> string(0) "" ["post_modified"]=> string(19) "2011-09-06 09:55:59" ["post_modified_gmt"]=> string(19) "2011-09-05 23:55:59" ["post_content_filtered"]=> string(0) "" ["post_parent"]=> int(0) ["guid"]=> string(36) "http://www.webdirections.org/?p=3642" ["menu_order"]=> int(0) ["post_type"]=> string(4) "post" ["post_mime_type"]=> string(0) "" ["comment_count"]=> string(1) "5" ["filter"]=> string(3) "raw" } [14]=> object(stdClass)#130 (25) { ["ID"]=> int(3638) ["post_author"]=> string(1) "3" ["post_date"]=> string(19) "2011-09-02 11:11:32" ["post_date_gmt"]=> string(19) "2011-09-02 01:11:32" ["post_content"]=> string(13129) "
In the infancy of JavaScript, there was little if any concept of an HTML document object model (DOM). Even though JavaScript was invented to enable web developers to manipulate parts of a web page, and in the original implementation, in Netscape 2.0, developers could only access the form elements, links, and images in a page. Useful for form validation, and first widely used for image rollover techniques (think :hover, before CSS), but far from the general purpose tool to create modern web applications we now know (and love/hate).
Newer iterations of the DOM provided developers with access to far more than just that original limited range of elements, as well as the ability to insert, modify and delete elements in an HTML document. But, cross-browser implementations very often differed, and full support for the W3C's DOM standards have arguably been treated as far more optional than CSS or HTML support.
One of the many reasons for the success of JavaScript libraries like jQuery and Prototype, on top of their easing the pain of cross-browser development was how they made working with the DOM far less painful than it had previously been, and indeed how it was with the standard DOM. Being able to use arbitrary CSS selector notation to get matching elements from a document made the standard DOM methods seem antiquated, or at the every least, far too much like hard work.
Luckily, the standards and browser developers took notice. The W3C developed the Selectors API, a way of easily accessing elements in the DOM using standard CSS selector concepts, and browser developers have baked these into all modern browsers, way back to IE8.
In this short (by my standards) article, we'll look at the Selectors API, how you use it, browser support, and some little things you might like to keep in mind while using it. Rest assured, it's now widely supported, so in many cases, you can safely use it, potentially with a fallback for older browsers (IE7 and older specifically) via libraries like jQuery (or more lightweight selector engines like Sizzle, which provides this functionality for jQuery, and other libraries).
The Selectors API
The Selectors API, which many would consider to be part of HTML5, is in fact a separate, small specification from the W3C. It provides only two new methods, querySelector, and querySelectorAll, for the Document, Element, and DocumentFragment objects (typically, you'll use these methods on the document or element objects.) But do these methods make life easier for developers?
Before the Selectors API, to access an object in the DOM we could use these methods:
getElementById(from DOM Level 2 Core) - available for thedocumentelementgetElementsByClassName, standardized in HTML5, after long non standard browser support, which is supported ondocuments andelementsgetElementsByTagName, from DOM Level 2 Core, available on thedocumentandelementobjects
And there are some legacy ways of accessing elements on a page, which date from the earliest days of JavaScript:
linksis a property of the document object which contains all anchor (a) andareaelements with anhrefattributeanchorsis a property of the document object which contains allaelementsformsis a property of the document object which contains all form elements
We can also "traverse" the DOM, using:
childNodes, a property of thedocumentandnodeobjectsnextSibling, a property of anode, which contains the element directly following it in the same parent elementparentElement, a property of a node, which contains its parent element.
and related DOM traversal properties and methods.
But, what developers really often want to be able to do (as the success of jQuery and other libraries has shown) is simply say "give me all the elements which match this selector", or "give me the first element which matches this selector". And that's precisely what the simple, powerful Selectors API does. It doesn't completely do away with the need for DOM traversal, and legacy methods and properties, but it goes a long, long way.
querySelector
querySelector is a method of the document or any element, which returns the first descendent element which would be selected by its one argument, a CSS selector string. We can use this in place of the document.getElementById('content') like so: document.querySelector('#content') (like me, you'll probably find yourself forgetting to add the # from time to time in querySelector, something which doesn't throw an error, so can be frustrating to track down).
And we can do things like find the first header element in an HTML5 document, with querySelector('header'). So far so good. But where querySelector really shines is we can use any selector (attribute, structural, dynamic, UI, and even selector groups) with it. In most cases, this makes traversing the DOM, and locating a specific element far simpler, and most likely far quicker, as we won't be looping in JavaScript and accessing all kinds of DOM properties, rather, the query is taking place inside the browser's far faster native DOM engine.
querySelectorAll
Often, when working with the DOM, we want to manipulate several elements at once, For example, we might want to unobtrusively attach an event listener to all the links with a given class value. Here, querySelectorAll is your friend. Just like querySelector, it takes a single string as an argument, which is a CSS selector. Instead of returning a single element, it returns a NodeList (a kind of JavaScript array) of matching elements. We can then iterate through this array, and manipulate these objects.
For example, we could use it to replace document.links like so:
document.querySelectorAll('area[href], a[href]')This finds all area elements with the href attribute set, as well as all a elements with this attribute set as well (notice how we've used a selector group, which is quite acceptable with the Selectors API).
Matching elements are returned in the order they appear in the DOM parse tree.
Document or Element?
I mentioned that both the document, and element objects implement these two methods - what's the difference? Well, as you might have guessed, these methods find elements that are descendants of the object you query on. So, if you use the method on a paragraph element, it will only find the descendant elements of that paragraph which match the selector. Other elements in the document which might match it won't be returned. But, if you use the methods on the document, then any matching element in the document can be found.
Gotchas
If you've really got your hands dirty with the DOM, you'll know that when DOM methods return a NodeList, it is live—that is, the members of the list change, depending on the state of the document.
Let's say we get all the elements with a class of "nav" using document.getElementsByClassName('nav'), and it returns 5 elements, which we keep in a variable.
Now, if we add a new element with class nav, or remove one of the existing elements with a class of nav, the NodeList in our variable will be updated to reflect these changes (that's why it is called a live NodeList).
But querySelector and querySelectorAll are different. While they return a NodeList, it is static. So, if we similarly get all elements with a class of nav using document.querySelectorAll('.nav'), then regardless of what we subsequently do to the DOM, the length and contents of the NodeList won't change. Which means, it's always best to query the DOM just before you need the elements, rather than holding on to elements if your DOM is going to change.
There's also a performance consideration. Tests of various browsers indicate that querySelectorAll is slower than getElementByTagName (though not it would appear in Opera). But, it's also possible that once available, manipulating the static NodeList may be higher performance than manipulating a dynamic NodeList. And this issue will likely only have an impact in extreme cases. I'd certainly not recommend prematurely optimising by using getElementsByTagName, getElementsByClass, getElementById and so on in place of querySelectorAll, but it is worth noting you might be able to squeeze a little more performance out by doing so if you really need to.
And it is worth noting too that querySelector and querySelectorAll don't work with every kind of selector. While pseudo-class selectors (like :visited) work with these methods, pseudo-element selectors, like :first-letter, :first-line, :before and :after although permissible as arguments, will return null in the case of querySelector, and an array of length zero for querySelectorAll.
A little gotcha this aging developer has found I'm so used to getElementById and getElementsByClassName that I find myself forgetting the # or . required in the selector string in querySeletor and querySelectorAll. As I mentioned a moment ago, it can be frustrating, as this won't throw an error, but simply return null or an empty NodeList.
Support
All modern browsers, including IE8 and up support both querySelector and querySelectorAll. It is however worth noting that the results returned are dependent on what selectors the browser supports. IE8 supports CSS2.1 selectors, though not CSS3 selectors. IE9 supports many CSS3 selectors, but not a number of the UI related pseudo-classes, such as :required and :invalid. IE CSS support for versions 5 through 9 is extensively covered here by Microsoft.
So, keep in mind, even where these methods are supported, what they return is only as good as the CSS selector support in that browser.
The Wrap-up
If you're more of a web designer who's always been a little intimidated by the thought of wading through the DOM in search of elements (trust me, even seasoned developers often wince at the thought), then querySelector and querySelectorAll are a boon for you. Or, if you're a developer who finds yourself going straight for jQuery or another library as soon as the DOM is involved, the Selectors API might just save you a few KBs, additional files and fussing around. And, if you're one of those seasoned DOM experts, I'm sure there's plenty of times you'll breathe a sigh of relief that rather than wading through an entire document with childNodes, nextElementSibling and the like, a single call to querySelectorAll will return all the elements you want. I reckon these two are my favourite, and now most used DOM methods, and I'm only surprised they aren't more widely covered, as they really are like a Swiss Army Knife for the DOM. Perhaps we need a "top 2 Selectors API methods" article. Then again...
Reading
Here's a few articles and other resources which delve into the Selectors API.
- The Selectors API specification from the W3C
- Selectors API at Opera Developers Center, by Lachlan Hunt, one of the authors of the spec.
- Selectors API at Mozilla Hacks
- Thoughts on performance from JS performance guru Nicholas Zakas (the comments are well worth reading)
Until recently, the only ways to maintain a user's data between visits to your site have been to store it on the server, or use cookies in the browser. Both present significant security challenges and quite a good deal of effort for us as developers.
Cookies are designed for communication between the browser and a server that persists between sessions. They're typically used for identifying a user on return visits and storing details about that user. Cookies are sent between the browser and server in plain text, unencrypted, each time the user opens a page. So, unless an application encrypts cookie contents, these can be quite trivially be read particularly on public wifi networks, when used over standard HTTP (though less easily over encrypted HTTPS).
Storing all client data on the server creates usability issues as well, as users need to log in each time they use that site. And of course the heavy lifting of ensuring data is secure during transmission, and on the server is left to you as the developer. And it's rather tricky to build apps which work when the user is offline if the user's data is all stored on the server.
As web applications become increasingly sophisticated, developers need ways to keep data around in the browser (particularly if we want our applications to work when the user is offline).
Two closely related but slightly different W3C technologies exist to help keep track of information solely in the browser. They enable far more structured data than cookies, are much easier for us to use as developers, and the information stored can only be transmitted to a server explicitly by the application.
sessionStorage stores data during a session and is removed once a session is finished. localStorage is almost identical, but the data stored persists indefinitely, until removed by the application. Let's start with sessionStorage, keeping in mind that we use localStorage almost identically.
sessionStorage
What is a session?
The key feature of sessionStorage is that data only persists for a session. But just what is a session? HTML5 has the concept of a "top-level browsing context". This is, in essence, a window or tab. A session lasts for that top-level browsing context while it is open, and while that top-level browsing context is pointed at the same domain (or strictly speaking, the same origin).
During the session, a user could visit other pages of the domain, or other sites entirely, then return to the original domain. Any data saved in sessionStorage during that session will remain available, but only to pages in the original domain, until the tab or window is closed.
If the user opens a link to your site in another tab or window, then there is no access to this sessionStorage, since this new tab or window is a new session. It's worth noting that sessionStorage is also shared with pages inside subframes in the same domain as the top level document in the window.
So, if we
- visit
http://webdirections.orgin a tab and save data tosessionStorage - then follow a link to
http://westciv.comin this same tab - and then return to
http://webdirections.orgin the same tab - we return to the same session for
http://webdirections.org - the data in the original
sessionStorageis still available
If however we
- visit
http://webdirections.orgin a tab and save data tosessionStorage - then follow a link to
http://webdirections.orgin a new tab - the data in the original
sessionStorageis not available to this new tab (but is in the original tab)
The one exception to this is when a browser crashes, and is restarted. Typically, browsers will in this case reopen all the windows that were open when the browser crashed. The specification allows in this situation for sessionStorage to persist for reopened windows from before the crash (WebKit, Mozilla and Opera browsers support this, IE8 does not, though IE9 and up do).
Which may sound like a great boon for the user, but, as an application developer, you may wish to consider whether you in fact want to persist session data after a crash. A user may consider that when their browser crashes using a service like web mail or online banking at an internet café or other shared computer that their login details have been purged, but if these were stored in sessionStorage then the next user to launch the browser will resume the session that were current when the user crashed. Ruh-roh.
What good is sessionStorage?
So, what good is sessionStorage? Well, one very useful application would be to maintain sensitive information during a transaction, signup, sign in and so on, which will be purged as soon as the user closes a window or tab. It can be used to create a multi-page form or application, where the information in each page can persist, and then be sent to the server all at once when the transaction is complete. It also moves some of the heavy lifting for protecting sensitive data away from application developers to the browser developer.
Using sessionStorage
sessionStorage is a property of the window object in the DOM. Because it is as yet not universally supported, we'll want to check that this property exists before we use it:
if (window.sessionStorage) {
//we use sessionStorage
}
else {
//we do something else, perhaps use cookies, or another fallback
}Right, so now we have our sessionStorage object, how do we use it?
Key-Value Pairs
sessionStorage stores "key-value pairs". Each pair is a piece of information (the value), identified by a unique identifier (the key). Both the key and the value are strings (more on the implications of this in a moment).
We use the setItem method of the sessionStorage object to store data like so:
//get the value of the input with id="name"
var name = document.querySelector('#name').value;
//store this value with the key "name"
window.sessionStorage.setItem('name', name);
Now we've stored the value of the input "name" in an item of the sessionStorage object called 'name'. It will remain there until this window or tab is closed and it will then automatically be purged by the browser when the user closes the window or tab.
reading from sessionStorage
There's not much point in storing these details if we can't get them back at some point. We do this by using the function getItem of the sessionStorage object, using a single parameter, the key we used to set the item.
So, to get the value of the name, we'd use:
var name = window.sessionStorage.getItem('name');Non-existent items
Now, what happens if for some reason there's no item in sessionStorage with the key we are trying to access? In place of a string value, it returns null, not the empty string. So, it's worthwhile testing whether the result returned is not null before using it
var email = window.sessionStorage.getItem('email');
if(email!==null){
document.querySelector('#email').innerHTML = email;
}Saving Data Between Sessions
When information is less sensitive, it may make sense to store it between sessions. Particularly as web sites become more application-like, and can increasingly work offline, saving preferences or the state of a document can make for much better usability.
My HTML5 and CSS developer tools do just this, using localStorage. That way, when someone returns to the tools, the last gradient or layout or transformation they built is waiting for them.
Best of all, using localStorage, for persistence between sessions, is almost identical to using sessionStorage.
window.localStorage
Instead of the sessionStorage object of the window, we use the localStorage object. All the methods of localStorage are the same as sessionStorage.
- we set items with
setItem - we get items with
getItem
Because items on the localStorage will persist forever, we may want to delete them. We can do this with localStorage.removeItem(key), using the key for the item we want to remove.
If we want to delete the entire localStorage, we can use localStorage.clear(). But, be warned, anything your app has saved to localStorage for this user is gone for good.
sessionStorage in fact also has these methods, though we're less likely to need them, as all of a sessionStorage is discarded by the browser when a session finishes.
Gotchas, Tips and Tricks
sessionStorage and localStorage store all data as strings
As mentioned earlier, the values stored in local and session storage are strings, which has a number of implications for developers.
In particular, when we store boolean values, integers, floating point numbers, dates, objects and other non-string values, we need to convert to and from a string when writing to and reading from storage.
There's also a more subtle side effect of storing values as strings. JavaScript strings are UTF-16 encoded, which means each character is 2 bytes (in UTF-8 characters are one byte). This effectively halves the available storage space.
Private Browsing
Many browsers now have private (or 'incognito') browsing modes, where no history or other details are stored between sessions. In this situation, what happens with sessionStorage and localStorage varies widely by browser.
- Safari returns
nullfor any item set usinglocalStorage.setItemeither before or during the private browsing session. In essence, neithersessionStoragenorlocalStorageare available in private browsing mode. - Chrome and Opera return items set previous to private ("incognito") browsing commencing, but once private browsing commences, treat
localStoragelikesessionStorage(only items set on thelocalStorageby that session will be returned) but likelocalStoragefor other private windows and tabs - Firefox, like Chrome will not retrieve items set on
localStorageprior to a private session starting, but in private browsing treatslocalStoragelikesessionStoragefor non-private windows and tabs, but likelocalStoragefor other private windows and tabs
Getters and Setters
In addition to using getItem and setItem we can use a key directly to get and set an item in sessionStorage and localStorage, like so (where the key is "keyName"):
var itemValue = window.localStorage.keyName;
localStorage and sessionStorage Limits
The webStorage specification recommends browsers implement a 5MB limit on the amount of data localStorage or sessionStorage can save for a given domain. If you try to exceed the limit that various browsers have in place (for some browsers users can change this allowance) setItem throws an error. There's no way of asking localStorage for the amount of space remaining, so it's best to set item values with a try and catch for any error:
try {
window.localStorage.setItem(key, value);
}
catch (exception) {
//test if this is a QUOTA_EXCEEDED_ERR
}If the available space for this localStorage is exceeded, the exception object will have the name "QUOTA_EXCEEDED_ERR" and a code of 22.
As mentioned, in JavaScript strings are UTF-16 encoded, which means they are 2-byte. So, when saving the string "John", we are actually using 8 bytes, not 4. Which means instead of 5MB of storage space per storage area, we effectively have 2.5MB.
If the storage needs of your application are likely to exceed 5MB, then web databases are likely to be a better solution. However, the situation with web databases is complicated, with two different standards, one, webSQL widely supported but deprecated, and the other IndexedDB, currently supported only in Firefox, Chrome and IE10.
Storage Events
We can add an event listener to the window for storage events so that when a storage object has been changed (there's a reason for the emphasis) then we can be notified and respond to those changes.
window.addEventListener('storage', storageChanged, false);Here, when localStorage is changed (by setting a new item, deleting an item or changing an existing item) our function storageChanged(event) will be called. The event passed as a parameter to this function has a property storageArea, which is the window's localStorage object.
There are two things to be aware of with storage events.
- The event only fires if the storage is changed (not if it is simply accessed and not if we set an item to the same value that it currently has!)
- In the specification, the event is not received in the window or tab where the change occurred, only in other open windows and tabs that have access to this localStorage. Some browsers have implemented storage events in such a way that the event is also received by the window or tab which causes the change, but don't rely on this.
webStorage Performance
Of late, a number of high profile, widely read articles critical of localStorage have been published, centring on its asserted performance shortcomings. The key criticism relates to the fact that webStorage is synchronous. This means a script using sessionStorage or localStorage waits while getItem, setItem and other storage methods are invoked. In theory, this can impact both the browser's response to user input and execution of JavaScript in a page. In practice, I'd argue that this is not likely to be a significant problem for most cases.
I recently conducted some testing across a number of devices and browsers which demonstrates that even for poorly implemented code that does a very significant number of getItem and setItem operations, the performance of webStorage is unlikely to have significant impact.
Origin restrictions
We said earlier that that sessionStorage and localStorage are restricted to windows or tabs in the same domain, but in fact, the restriction is tighter than simply the top-level domain (such as webdirections.org)
To have access to each other's webStorage, tabs or windows must have the same top-level domain (for example webdirections.org), subdomains (for example test.webdirections.org), and protocol (https://webdirections.org has a different localStorage from http://webdirections.org).
At first glance this might seem overly restrictive but imagine john.geocities.com having access to the localStorage of fred.geocities.org?
Browser Support and Backwards Compatibility
localStorage and sessionStorage are widely supported, from IE8 upwards and in all modern browsers, including mobile devices.
There are also several polyfills that allow for webStorage in browsers which don't support it natively.
The Wrap
webStorage solves a long standing challenge for web developers - reliably and more securely storing data between sessions entirely on the client side. While there are assertions that performance limitations make localStorage "harmful", in the real world, services like Google and Bing are using localStorage and performance experts like Steve Souders and Nicholas Zakas defend and advocate their use. That's not to say webStorage is perfect or ideal in all situations. The synchronous nature of the API and 5MB limit per origin do mean that in certain circumstances an alternative may be required. webStorage is however eminently usable for a great many client side data storage needs.
Further Reading
- The ever reliable HTML5 Doctors on webStorage
- Opera Developer Network, another always useful place to visit, on webStorage
- All about local storage, from Mark Pilgrim's excellent "Dive into HTML5"
- Remy Sharp on the state of support for offline events, and some workarounds
- A couple of articles from the Mozilla Hacks blog on web databases, the Road to IndexedDB and an introduction to IndexedDB
- MSDN's introduction to IndexedDB (supported in IE10)
- An introduction to webStorage, also from MSDN
Blog
Morning coffee for web workers News Feed Podcast Follow Us
webStorage: Persistent client side data storage
- In: Blog, developer
- By: John
- March 30, 2012
- 7 Comments
Until recently, the only ways to maintain a user’s data between visits to your site have been to store it on the server, or use cookies in the browser. Both present significant security challenges and quite a good deal of effort for us as developers.
Cookies are designed for communication between … Read more »
Web Directions South 2012 — got an idea for a session?
- In: Blog
- By: Maxine
- March 12, 2012
- No Comments
It’s that time of the year again when John and I start writing ideas and names on post it notes, shuffling them around, and coming up with the program that will be Web Directions South, this October 18 and 19. The only thing that scares us is that 2011 was … Read more »
localStorage, perhaps not so harmful
- In: Blog
- By: John
- March 8, 2012
- 17 Comments
Recently, Christian Heilmann and Taras Glek, both at Mozilla, posted articles critical of localStorage. The arguments in each of these really didn’t gel with my experience, and both felt unduly alarmist (“considered harmful” as argued elsewhere really should be retired as a post heading). So, I wanted to … Read more »
In honour of International Women’s Day
The 8th of March is International Women’s Day. In honour of the occasion I decided to create a listing of all the presentations by people who happen to be women that we’ve had at Web Directions conferences over the years.
Many hours later I had come up with the list below.
I … Read more »
Introducing Web Directions Code, Melbourne in May
- In: Blog
- By: John
- March 7, 2012
- No Comments
If you just read our blog here, you might think it’s been a little quiet since Web Dirctions South last year. But we have in fact just finished a 4 city roadshow with workshops by Andy Clarke and me (John Allsopp) as well as our second round of … Read more »
Standards, innovation, Flash, ownership and all that
- In: Blog
- By: John
- November 10, 2011
- 4 Comments
It’s often argued (well, asserted might be a better way of putting it) that standards are an anathema to innovation, or at the very least a significant impediment to it.
At its most extreme, this is used as an argument for disbanding the W3C, and even for core web technologies … Read more »
The Next 6 Billion
- In: Blog
- By: John
- October 20, 2011
- 40 Comments
Some time this month, for the first time, there will be 7 Billion people alive on earth. In around 14 years, the United Nations predicts our population will reach 8 Billion. These are numbers the human mind has not evolved to intuitively understand.
According to most estimates just over 2 … Read more »
Web Directions South 2011
- In: Blog
- By: John
- October 17, 2011
- 4 Comments
When you work on something for an extended period of time but which itself lasts itself only a brief moment, such as Maxine and I do with Web Directions, there’s an intensity to the event itself, and the strange mixture of relief (and exhaustion) coupled with nostalgia when it … Read more »
The challenge of WYSIWYG development for the web
- In: Blog
- By: John
- September 27, 2011
- 7 Comments
This is the first in what I hope will be a number of articles I’m writing to clarify my thinking in the lead up to my Dao of Web Design Revisited presentation at this years Web Directions South.
In the middle 1990s, by an accident of fate and coincidence … Read more »
The web is a different problem
- In: Blog
- By: John
- September 21, 2011
- 18 Comments
One of the most persistent criticisms of web technologies is that they evolve slowly, indeed, too slowly. Often the argument is raised that the process of standards is antithetical to “innovation” (for innovation read “making cool stuff up”).
To contrast with this glacial change, we’re typically pointed toward the wonders of … Read more »
2D Transforms in CSS3
- In: Blog, developer
- By: John
- September 21, 2011
- 5 Comments
One of the most powerful features of CSS3 are transforms, which allow us to take any element in an HTML document, and while not changing its effect on the page layout, rotate it, translate it (move it left, right, up and down), skew it and scale it. CSS3 provides … Read more »
What do you know? Video now available
What do you know? That’s the question we posed 20 Australian designers, developers, UX folks and others for our first ever “What do you know” events in Sydney and Melbroune.
The format was simple — each of the speakers had 5 minutes to tell the audience something they know — … Read more »
On the (abominable) proposed HTML5 “scoped” attribute for style elements
- In: Blog, developer
- By: John
- September 15, 2011
- 7 Comments
Over the last couple of years, I’ve had my fair share to say about the direction HTML5 has been taking, in particular being quite critical of the entire approach taken to adding richer semantics to HTML, as well as specific language choices.
It must be said though, that nothing has … Read more »
Bring back the CSS bike shedding property!
- In: Blog
- By: John
- September 6, 2011
- 5 Comments
There’s not necessarily a lot of whimsy in the world of web standards. A great deal of value, a lot of hard work by really smart people, but not whimsy.
Well, recently there’s been a little bit of whimsy in the otherwise dry, but very useful CSS3 Text module, with the … Read more »
HTML5 selectors API — It’s like a Swiss Army Knife for the DOM
- In: Blog, developer
- By: John
- September 2, 2011
- 4 Comments
In the infancy of JavaScript, there was little if any concept of an HTML document object model (DOM). Even though JavaScript was invented to enable web developers to manipulate parts of a web page, and in the original implementation, in Netscape 2.0, developers could only access the form elements, links, … Read more »
Stay in touch
- Our awesome weekly newsletter:
- Twitter: @webdirections
- Keep up with the latest in HTML5, app development, platforms, devices and more at our blog, the web, unplugged
What do you know?
Live dev and design screencasts
State of Web Development Reports
Flip through our detailed reports focussing on the technologies and techniques used by web professionals around the world.
- State of Mobile Web Development (2011)
- State of Web Development (2010)
- State of Web Development (2008)
Slides and podcasts from previous events
- accessibility
- coding
- css
- data
- design
- development
- html
- innovation
- interaction design
- javascript
- mobile
- strategy
- usability
- user experience
- visual design
- wdn08
- wds07
- wds08
- wds09
- web standards
Jobs from jobs.webdirections.org
Post contract, part-time or full-time job offerings for web professionals of all types for free, or find properly qualified job openings as a web professional.
