Using the Web Storage API
In this chapter, we will explore what you can do with HTML5 Web Storage—sometimes referred to as DOMStorage—an API that makes it easy to retain data across web requests. Before the Web Storage API, remote web servers needed to store any data that persisted by sending it back and forth from client to server. With the advent of the Web Storage API, developers can now store data directly on the client side in the browser for repeated access across requests or to be retrieved long after you completely close the browser, thus reducing network traffic.
We’ll first look at how Web Storage differs from cookies and then explore how you can store and retrieve data. Next, we will look at the differences between
sessionStorage, the attributes and functions that the storage interface provides, and how you can handle Web Storage events. We wrap up with a look at Web SQL Database API and a few practical extras.
Overview of Web Storage
To explain the Web Storage API, it is best to review its predecessor, the intriguingly named cookie. Browser cookies—named after an age-old programming technique for passing small data values between programs—are a built-in way of sending text values back and forth from server to browser. Servers can use the values they put into these cookies to track user information across web pages. Cookie values are then transmitted back and forth every time a user visits a domain. For example, cookies can store a session identifier that allows a web server to know which shopping cart belongs to a user by storing a unique ID in a browser cookie that matches the server’s own shopping cart database. Then, as a user moves from page to page, the shopping cart can be updated consistently. Another use for cookies is to store local values into an application so that these values can be used on subsequent page loads.
Cookie values can also be used for operations that are slightly less desirable to users, such as tracking which pages a user visits for the sake of targeted advertising. As such, some users have demanded that browsers include functionality to allow them to block or remove cookies either all of the time or for specific sites.
Love them or hate them, cookies have been supported by browsers since the earliest days of the Netscape browser, back in the mid-1990s. Cookies are also one of the few features that have been consistently supported across browser vendors since the early days of the Web. Cookies allow data to be tracked across multiple requests, as long as that data is carefully coordinated between the server and the browser code. Despite their ubiquity, cookies have some well-known drawbacks:
- Cookies are extremely limited in size. Generally, only about 4 KB of data can be set in a cookie, meaning they are unacceptable for large values such as documents or mail.
- Cookies are transmitted back and forth from server to browser on every request scoped to that cookie. Not only does this mean that cookie data is visible on the network, making them a security risk when not encrypted, but also that any data persisted as cookies will be consuming network bandwidth every time a URL is loaded. As such, the relatively small size of cookies makes more sense.
localStorage, developers can choose to let those values survive either across page loads in a single window or tab or across browser restarts, respectively. Stored data is not transmitted across the network, and is easily accessed on return visits to a page. Furthermore, larger values can be persisted using the Web Storage API values as high as a few megabytes. This makes Web Storage suitable for document and file data that would quickly blow out the size limit of a cookie.
Browser Support for Web Storage
Web Storage is one of the most widely adopted features of HTML5. In fact, since the arrival of Internet Explorer 8 in 2009 all currently shipping browser versions support Web Storage in some capacity. At the time of this publication, the market share of browsers that do not support storage is dwindling down into single digit percentages.
Web Storage is one of the safest new APIs to use in your web applications today because of its widespread support. As usual, though, it is a good idea to first test if Web Storage is supported before you use it. The subsequent section “Checking for Browser Support” will show you how you can programmatically check if Web Storage is supported.
Using the Web Storage API
The Web Storage API is surprisingly simple to use. We’ll start by covering basic storage and retrieval of values and then move on to the differences between
localStorage. Finally, we’ll look at the more advanced aspects of the API, such as event notification when values change.
Checking for Browser Support
The storage database for a given domain is accessed directly from the
window object. Therefore, determining if a user’s browser supports the Web Storage API is as easy as checking for the existence of
Figure 11-1 shows this check for storage support in action.
Some browsers do not support
sessionStorage for files accessed directly from the file system. Make sure you serve up the pages from a web server when you run the examples in this chapter! For example, you can start Python’s simple HTTP server in the
code/storage directory as follows:
python -m SimpleHTTPServer 9999
After that, you can access the files at
http://localhost:9999/. For example,
However, you are free to use any server or URL location to run the examples.
Setting and Retrieving Values
To begin, we’ll focus on the session storage capability as you learn to set and retrieve simple values in a page. Setting a value can easily be done in a single statement, which we’ll initially write using the long-hand notation:
There are a few important points to notice from this storage access statement:
- We can omit the reference to the
windowfor a shorthand notation, as the storage objects are made available in the default page context.
- The function we are calling is
setItem, which takes a key string and a value string. Although some browsers might support passing in nonstring values, the specification only allows strings as values.
- This particular call will set into the session storage the string
myFirstValue, which can later be retrieved by the key
To retrieve the value, the long-hand notation involves making a call to the
getItem function. For example, if we augmented our previous example with the following statement
myFirstValue. As you can see, setting and retrieving values from the Web Storage API is very straightforward.
However, there is an even simpler way to access the storage objects in your code. You are also able to use expando-properties to set values in storage. Using this approach, the
getItem calls can be avoided entirely by simply setting and retrieving values corresponding to the key-value pair directly on the
sessionStorage object. Using this approach, our value set call can be rewritten as follows:
sessionStorage.myFirstKey = ‘myFirstValue’;
sessionStorage[‘myFirstKey’] = ‘myFirstValue’;
Similarly, the value retrieval call can be rewritten as:
We’ll use these formats interchangeably in the chapter for the sake of readability.
That’s it for the basics. You now have all the knowledge you need to use session storage in your application. However, you might be wondering what’s so special about this
sessionStorage can be retrieved from other pages using the same keys. This also applies to subsequent loads of the same page. As a developer, you are probably used to the idea that changes made in script will disappear whenever a page is reloaded. That is no longer true for values that are set in the Web Storage API; they will continue to exist across page loads.
Plugging Data Leaks
How long do the values persist? For objects set into
sessionStorage, they will persist as long as the browser window (or tab) is not closed. As soon as a user closes the window—or browser, for that matter—the
sessionStorage values are cleared out. It is useful to consider a
sessionStorage value to be somewhat like a sticky note reminder. Values put into
sessionStorage won’t last long, and you should not put anything truly valuable into them, as the values are not guaranteed to be around whenever you are looking for them.
Why, then, would you choose to use the session storage area in your web application? Session storage is perfect for short-lived processes that would normally be represented in wizards or dialogs. If you have data to store over the course of a few pages, that you would not be keen to have resurface the next time a user visits your application, feel free to store them in the session storage area. In the past, these types of values might be submitted by forms and cookies and transmitted back and forth on every page load. Using storage eliminates that overhead.
sessionStorage API has another very specific use that solves a problem that has plagued many web-applications:scoping of values. Take, for example, a shopping application that lets you purchase airline tickets. In such an application, preference data such as the ideal departure date and return date could be sent back and forth from browser to server using cookies. This allows the server to remember previous choices as the user moves through the application, picking seats and a choice of meals.
However, it is very common for users to open multiple windows as they shop for travel deals, comparing flights from different vendors for the same departure time. This causes problems in a cookie system, because if a user switches back and forth between browser windows while comparing prices and availability, they are likely to set cookie values in one window that will be unexpectedly applied to another window served from the same URL on its next operation. This is sometimes referred to as leaking data and is caused by the fact that cookies are shared based on the origin where they are stored. Figure 11-2 shows how this can play out.
sessionStorage, on the other hand, allows temporary values like a departure date to be saved across pages that access the application but not leak into other windows where the user is also browsing for flights. Therefore, those preferences will be isolated to each window where the corresponding flights are booked.
Local Versus Session Storage
Sometimes, an application needs values that persist beyond the life of a single tab or window or need to be shared across multiple views. In these cases, it is more appropriate to use a different Web Storage implementation:
localStorage. The good news is that you already know how to use
localStorage. The only programmatic difference between
localStorage is the name by which each is accessed—through the
localStorage objects, respectively. The primary behavioral differences are how long the values persist and how they are shared. Table 11-1 shows the differences between the two types of storage.
|Values persist only as long as the window or tab in which they were stored.||Values persist beyond window and browser lifetimes.|
|Values are only visible within the window or tab that created them.||Values are shared across every window or tab running at the same origin.|
Keep in mind that browsers sometimes redefine the lifespan of a tab or window. For example, some browsers will save and restore the current session when a browser crashes, or when a user shuts down the display with many open tabs. In these cases, the browser may choose to keep the
sessionStorage around when the browser restarts or resumes. So, in effect,
sessionStorage may live longer than you think!
Other Web Storage API Attributes and Functions
The Web Storage API is one of the simplest in the HTML5 set. We have already looked at both explicit and implicit ways to set and retrieve data from the session and local storage areas. Let’s complete our survey of the API by looking at the full set of available attributes and function calls.
localStorage objects can be retrieved from the
window object of the document in which they are being used. Other than their names and the duration of their values, they are identical in functionality. Both implement the
Storage interface, which is shown in Listing 11-2.
Let’s look at the attributes and functions here in more detail.
lengthattribute specifies how many key-value pairs are currently stored in the storage object. Remember that storage objects are specific to their origin, so that implies that the items (and length) of the storage object only reflect the items stored for the current origin.
key(index)function allows retrieval of a given key. Generally, this is most useful when you wish to iterate across all the keys in a particular storage object. Keys are zero-based, meaning that the first key is at index (0) and the last key is at index (length – 1). Once a key is retrieved, it can be used to fetch its corresponding value. Keys will retain their indices over the life of a given storage object unless a key or one of its predecessors is removed.
- As you’ve already seen,
getItem(key)function is one way to retrieve the value based on a given key. The other is to reference the key as an array index to the storage object. In both cases, the value
nullwill be returned if the key does not exist in storage.
setItem(key, value)function will put a value into storage under the specified key name, or replace an existing value if one already exists under that key name. Note that it is possible to receive an error when setting an item value; if the user has storage turned off for that site, or if the storage is already filled to its maximum amount, a
QUOTA_EXCEEDED_ERRerror will be thrown during the attempt. Make sure to handle such an error should your application depend on proper storage behavior.
removeItem(key)function does exactly as you might expect. If a value is currently in storage under the specified key, this call will remove it. If no item was stored under that key, no action is taken.
- Finally, the
clear()function removes all values from the storage list. It is safe to call this on an empty storage object; as such, a call will simply do nothing.
Communicating Web Storage Updates
Sometimes, things get a little more complicated, and storage needs to be accessed by more than one page, browser tab, or worker. Perhaps your application needs to trigger many operations in succession whenever a storage value is changed. For just these cases, the Web Storage API includes an event mechanism to allow notifications of data updates to be communicated to interested listeners. Web Storage events are fired on the window object for every window of the same origin as the storage operation, regardless of whether or not the listening window is doing any storage operations itself.
To register to receive the storage events of a window’s origin, simply register an event listener, for example:
window.addEventListener("storage", displayStorageEvent, true);
As you can see, the name
storage is used to indicate interest in storage events. Any time a
localStorage —for that origin is raised any registered event listener will receive the storage event as the specified event handler. The storage event itself takes the form shown in Listing 11-3.
StorageEvent object will be the first object passed to the event handler, and it contains all the information necessary to understand the nature of the storage change.
keyattribute contains the key value that was updated or removed in the storage.
oldValuecontains the previous value corresponding to the key before it was updated, and the
newValuecontains the value after the change. If the value was newly added, the
oldValuewill be null, and if the value has been removed, the
newValuewill be null.
urlwill point to the origin where the
- Finally, the
storageAreaprovides a convenient reference to the
localStoragewhere the value was changed. This gives the handler an easy way to query the storage for current values or make changes based on other storage changes.
Listing 11-4 shows a simple event handler, which will raise an alert dialog with the contents of any storage event fired on the page’s origin.
Exploring Web Storage
Since Web Storage is very similar in function to cookies, it is not too surprising that the most advanced browsers are treating them in a very similar manner. Values that are stored into
sessionStorage can be browsed similar to cookies in the latest browsers, as shown in Figure 11-5.
This interface also grants users the ability to remove storage values as desired and easily see what values a given web site is recording while they visit the pages. Not surprisingly, the Safari browser from Apple has a similar, unified display for cookies and storage, as it is based on the same underlying WebKit rendering engine as Chrome is. Figure 11-6 shows the Safari Storage panel.
Like the other browsers, the Opera Dragonfly storage display allows users to not only browse and delete storage values but also create them as shown in Figure 11-7.
As Web Storage becomes more widely implemented by the various browser vendors, expect both the capacity and tooling available to users and developers to expand rapidly.
Building an Application with Web Storage
Now, let’s put together what you’ve learned by integrating storage into a web application. As applications grow more complex, it becomes increasingly important to manage as much data as possible without server interaction. Keeping data local to the client reduces network traffic and increases responsiveness by fetching data from a local machine instead of a remote location.
One common problem developers grapple with is how to manage data as users move from page to page within an application. Traditionally, web applications achieve this by storing data on a server and moving it back and forth while the user navigates pages. Alternatively, the application may attempt to keep the user in a single page and update everything dynamically. However, users are prone to wander, and getting data back into the display quickly when a user returns to your application’s page is a great way to enhance the user experience.
In our sample application, we’ll show how to store temporary application data locally while the user moves from page to page on a web site and quickly load it from storage on each page. To accomplish this, we’ll build on the examples of previous chapters. In Chapter 5, we showed how easy it is to gather a user’s current location. Then, in Chapter 7, we demonstrated how to take location data and send it to a remote server so that it can be viewed by any number of interested users. Here, we will go one step further: we will listen for broadcasted location data delivered via a WebSocket and store it in local storage so that it is immediately available as users move from page to page.
Imagine that our running club has live location information from its race participants being broadcast from their mobile devices and shared via a WebSocket server. It would be simple for a web application to display the current position of every racer live and in real time, as the racers upload new position information during the race. And a smart web site would cache those race positions to display them quickly as a user navigated among the pages of the site. That’s exactly what we’re going to build.
In order to achieve this, we’ll need to introduce a demonstration web site that can save and restore our racer data. We’ve created a three-page example running race site and placed it in our online resources in the folder
code/storage, but you can use any site of your choosing for your own demonstration. The key here is merely that you have multiple web pages that are easily traversed by a user. We will insert a bit of dynamic content into those pages to represent a live leader board, or a list of race participants and their current distance from the finish line. Figure 11-8 shows the three pages that make up the race site.
Each of our web pages will contain a common section to display the leader board data. Each entry in the leader board will show the name of one of our racers and his or her current distance from the finish line. When any of our pages is loaded, it will make a WebSocket connection to a race broadcast server and listen for messages indicating the position of a racer. The racers, in turn, will be sending their current position to the same broadcast server, causing the position data to stream down to the page in real time.
All of this has been covered in previous chapters related to Geolocation and WebSockets. In fact, much of the demonstration code here is shared with the examples from earlier in this book. However, there is one key difference in this example: when the data arrives in the page, we will store it in the session storage area for later retrieval. Then, whenever a user navigates to a new page, the stored data will be retrieved and displayed before making a new WebSocket connection. In this way, the temporary data is transferred from page to page without using any cookies or web server communication.
To keep our data feed small, we’ll send our racer location messages across the web in a simple format that is easy to read and parse. This format is a
String that uses the semicolon character (
;) as a delimiter separating the chunks of data: name, latitude, and longitude. For example, a racer named Racer X who is at latitude 37.20 and longitude –121.53 would be identified with the following string:
First, we’ll establish a few utility methods that you’ve seen before. To calculate the distance of any particular racer from the finish line, we need routines to calculate distance between two geolocation positions as shown in Listing 11-5.
In this familiar set of functions—used earlier in Chapter 5—we calculate the distance between two points with a
distance function. The details are not of particular importance, nor are they the most accurate representation of distance along a racetrack, but they’ll do for our example.
In the final lines, we establish a latitude and longitude for the finish line location of the race. As you’ll see, we will compare these coordinates with incoming racer positions to determine the racers’ distance from the finish line, and thus, their ranks in the race.
Now, let’s look at a tiny snippet of the HTML markup used to display the page.
<h2> Live T216 Leaderboard</h2>
<p id="leaderboardStatus"> Leaderboard: Connecting…</p>
Although most of the page HTML is irrelevant to our demonstration, in these few lines, we declare some named elements with the IDs
leaderboardStatus is where we will display the connection information for our WebSocket. And the leaderboard itself is where we will insert
div elements to indicate the position information we are receiving from our WebSocket messages, using the utility function shown in Listing 11-6.
This utility is a simple display routine, which takes the racer’s name and distance from the finish line. Figure 11-9 shows what the leader board section looks like on the
The name is used for two purposes; not only is it placed into the status message for that racer but it is also used to reference the unique
div element where that racer’s status is stored. If a
div for our racer already exists, we will find it when we look it up using the standard
document.getElementById() routine. If a
div does not already exist in the page for that racer, we will create one and insert it into the
leaderboard area. Either way, we update the
div element corresponding to that racer with the latest distance from the finish line, which will immediately update it in the display of the page. If you have already read Chapter 7, this will be familiar to you from the example application we created there.
Our next function is the message processor that will be called whenever data is returned from the broadcasting race WebSocket server, as shown in Listing 11-7.
split() routine to produce the
Next, it passes the racer’s latitude and longitude, as well as the latitude and longitude of the finish line, to the
distance utility method we defined earlier, storing the resulting distance in the
Now that we actually have some data worth storing, we can look at the call which exercises Web Storage.
// store the incoming user name and distance in storage
window.sessionStorage[incomingId] = currentDistance;
In this line, we use the
sessionStorage object on the window to store the current distance of the racer from the finish line as a value under the name and ID of the racer. In other words, we will set a value on the session storage with the key being the racer’s name and the value being that racer’s distance from the finish. As you will see momentarily, this data will be retrieved from storage as the user navigates from page to page on the web site. At the end of the function, we call the
displayLocation() routine we previously defined to make sure that this most recent location update is displayed visually in the current page.
Now, on to our final function in our storage example—the load routine shown in Listing 11-8 that fires whenever visitors access the web page.
This is a longer function than the others, and there is a lot going on. Let’s take it step by step. First, as shown in Listing 11-9, we do a basic error check to make sure that the browser viewing the page supports
sessionStorage by checking for its presence on the window object. If
sessionStorage is not accessible, we simply update the
leaderboardStatus area to indicate as much, and then return out of the loading routine. We won’t be attempting to work around lack of browser storage in this example.
The next thing we do on page load is to use the storage to retrieve any racer distance results that have already been served to this or other pages of our website. Recall that we are running an identical block of script code on every one of our site pages, so that the leader board follows the users as they browse around various locations. As such, the leader board may already have stored values into storage from other pages that will be retrieved and displayed here directly on load as shown in Listing 11-10. The previously saved values will follow the user during navigation, as long as the user does not close the window, tab, or browser, thus clearing out the session storage.
This is an important section of code. Here, we query the session for its length—in other words, the number of keys the storage contains. Then, we grab each key using
storage.key() and store it into the
currRacer variable, later using that variable to reference the key’s corresponding value with
storage[currRacer]. Together, the key and its value represent a racer and that racer’s distance, which were stored on a visit to a previous page.
Once we have a previously stored racer name and distance, we display them using the
displayRacerLocation() function. This all happens very quickly on page load, causing the page to instantaneously fill its leader board with previously transmitted values.
Our last piece of load behavior is to hook up the page to the racer broadcast server using a simple WebSocket, as shown in Listing 11-11.
As we did before in our WebSocket chapter, we first check to make sure that the browser supports WebSocket by checking for the existence of the
window.WebSocket object. Once we have verified that it exists, we connect to the URL where our WebSocket server is running. This server broadcasts racer location messages of the semicolon-separated format listed previously, and whenever we receive one of those messages via the
socket.onmessage callback, we call our previously discussed
dataReturned() function to process and display it. We also use the
socket.onopen callback to update our
leaderboardStatus area with a simple diagnostic message to indicate that the socket opened successfully.
That’s it for our
load routine. The final block of code we declare in our script block is the registration function, which requests that the
loadDemo() function is called whenever page load is complete:
// add listeners on page load and unload
window.addEventListener("load", loadDemo, true);
As you have seen many times before, this event listener requests that our
loadDemo() function will be called when the window has completed loading.
But how do we get racer data transmitted from the trails to the broadcast WebSocket server and into our pages? Well, we could actually use the tracker example previously declared in the WebSocket chapter by simply pointing its connect URL to the broadcast server listed previously. However, we have also created a very simple racer broadcast source page, shown in Listing 11-12, which serves a similar purpose. This page would theoretically be run on the mobile devices of the race participants. Although it does not include any Web Storage code itself, it is a convenient way to transmit the properly formatted data when run in a browser with both WebSocket and Geolocation support. The file
racerBroadcast.html is available from the web site sample area provided for this book.
We won’t spend too much space covering this file in detail, as it is nearly identical to the tracker example in Chapter 7. The primary difference is that this file contains a text field for entering the racer’s name:
Racer name: <input type="text" id="racerName" value="Racer X"/>
The racer’s name is now sent to the broadcast server as part of the data string:
var toSend = ";" + document.getElementById("racerName").value
+ ";" + latitude + ";" + longitude;
To try it out for yourself, open two windows in a browser that supports Web Storage, Geolocation, and WebSocket, such as Google Chrome. In the first, load the running club’s
index.html page. You will see it connect to the race broadcast site using WebSocket and then await any racer data notifications. In the second window, open the
racerBroadcast.html file. After this page, too, has connected to the WebSocket broadcast site, enter a name for your racer, and click the Start button. You’ll see that the racer broadcast has transmitted the location of your favorite racer, and it should show up in the leader board in your other browser window. Figure 11-10 shows what this looks like.
Now, navigate to other racing club pages using the Signup and About the Race links on the left side of the page. Because all of these pages have been configured to load our script, they will immediately load and populate the leader board with the previous racer data, which was delivered while browsing other pages. Send more racer status notifications (from the broadcast page), and you’ll see them propagate through the club site pages as you navigate, as well.
sessionStorage. When the page is loaded, it checks for any previously stored racer position values, thus maintaining the state as the user navigates the site. What are some of the benefits we gain from this approach?
- Reduced network traffic: Race information is stored locally in the browser. Once it arrives, it sticks around for every page load, rather than using cookies or server requests to fetch it again.
- Immediate display of values: The browser pages themselves can be cached rather than loaded from the network, because the dynamic parts of the page—the current leaderboard status—are local data. This data is rapidly displayed without any network load time.
- Transient storage: The race data isn’t very useful after the race has completed. Consequently, we store it in session storage area, meaning it is discarded when the window or tab is shut down, and it no longer consumes any space.
This same technique we demonstrated in this example can be applied to any number of data types: chat, e-mail, and sports scores are other examples that can be cached and displayed from page to page using local or session storage just as we’ve shown here. If your application sends user-specific data back and forth from browser to server at regular intervals, consider using Web Storage to streamline your flow.
The Future of Browser Database Storage
The key-value Storage API is great for persisting data, but what about indexed storage that can be queried? HTML5 applications will eventually have access to indexed databases as well. The exact details of the database APIs are still solidifying, and there are two primary proposals.
The Web SQL Database
One of the proposals, Web SQL Database, has been implemented in Safari, Chrome, and Opera. Table 11-2 shows the browser support for Web SQL Database.
|Chrome||Supported in version 3.0 and greater|
|Firefox||The image is repeated only in the X dimension|
|Internet Explorer||Not supported|
|Opera||Supported in version 10.5 and greater|
|Safari||Supported in version 3.2 and greater|
Because Web SQL Database is already implemented in the wild, we are including a basic example but omiting the complete details of the API. This example demonstrates the basic use of the Web SQL Database API. It opens a database called
mydb, creates a
racers table if a table by that name does not already exist, and populates the table with a list of predefined names. Figure 11-11 shows this database with racers table in Safari’s Web Inspector.
To begin, we open a database by name. The
window.openDatabase() function returns a
Database object through which database interaction takes place. The
openDatabase() function takes a name as well as an optional version and description. With an open database, application code can now start transactions. SQL statements are executed in the context of a transaction using the
transaction.executeSql() function. This simple example uses
executeSql() to create a table, insert racer names into the table, and later query the database to create an HTML table. Figure 11-12 shows the output HTML file with the list of names retrieved from the table.
Database operations can take some time to complete. Instead of blocking script execution until a result set is available, queries run in the background. When the results are available, a function given as the third argument to
executeSQL() is called back with the transaction and the result set as arguments.
Listing 11-13 shows the complete code for the file
sql.html; the sample code shown is also located in the
The Indexed Database API
A second proposal for browser database storage gained prominence in 2010. The Indexed Database API is supported by Microsoft and Mozilla and is seen as a counter to the Web SQL Database. Where the Web SQL Database looks to bring the established SQL language into browsers, the Indexed Database aims to bring low-level indexed storage capabilities, with the hope that more developer-friendly libraries will be built on top of the indexed core.
While the Web SQL API supports using query languages to issue SQL statements against tables of data, the Indexed DB API issues synchronous or asynchronous function calls directly against a tree-like object storage engine. Unlike Web SQL, the Indexed DB does not work with tables and columns.
The support for the Indexed Database API is growing (see Table 11-3).
|Chrome||Supported in current versions|
|Firefox||Supported in current versions|
|Internet Explorer||Supported in version 10+|
|Opera||Not currently supported|
|Safari||Not currently supported|
Microsoft and Mozilla have announced that they will not support the Web SQL Database and have thrown their weight behind the Indexed Database instead. Google’s Chrome has joined in with support, and as such, it is likely that the Indexed Database is the future of standardized structured storage in the browser. Among their reasons are the fact that SQL is not a true standard and also that the only implementation of Web SQL was the SQLite project. With only one implementation and a loose standard, they could not support WebSQL in the HTML5 specification.
Creation or modification of Indexed Database storage is done under the context of transactions, which can be classified as either READ_ONLY, READ_WRITE, or VERSION_CHANGE. While the first two may be self-explanatory, the VERSION_CHANGE transaction type is used whenever an operation will modify the structure of the database.
Retrieving records from an Indexed Database is done via a cursor object. A cursor object iterates over a range of records in either increasing or decreasing order. At any time a cursor either has a value or does not, due to the fact that it is either in the process of loading or has reached the end of its iteration.
A detailed description of the Indexed Database API is beyond the scope of this book. If you are intending to implement a query engine on top of the built-in API, you should consult the official specification at http://www.w3.org/TR/IndexedDB/. Otherwise, you would be wise to wait for one of the proposed engines layered on top of the standard to be made available to use a more developer-friendly database API. At this point, no third-party libraries have gained prominence or significant backing.
Sometimes, there are techniques that don’t fit into our regular examples but nonetheless apply to many types of HTML5 applications. We present to you some short, but common, practical extras here.
JSON Object Storage
JSON is a standard for data-interchange that can represent objects as strings and vice-versa. JSON has been used for over a decade to transmit objects from browser clients to servers over HTTP. Now, we can use it to serialize complex objects in and out of Web Storage in order to persist complex data types. Consider the script block in Listing 11-14.
As you can see, the script contains event listeners to register handlers for load and unload events in the browser window. In this case, the handlers call the
saveData() functions, respectively.
loadData() function, the session storage area is queried for the value of a storage key, and that key is passed to the
JSON.parse() function. The
JSON.parse() routine will take a previously saved string representation of an object and reconstitute it into a copy of the original. This routine is called every time the page loads.
saveData() function takes a data value and calls
JSON.stringify() on it to turn it into a string representation of the object. That string is, in turn, stored back into storage. By registering the
saveData() function on the
unload browser event, we ensure that it is called every time the user navigates away or shuts down the browser or window.
The practical result of these two functions is that any object we wish to track in storage, no matter if it is a complex object type, can be stored and reloaded as users navigate in and out of the application. This allows developers to extend the techniques we have already shown to nontext data.
A Window into Sharing
As alluded to in an earlier section, the ability for Web Storage events to fire in any window browsing the same origin has some powerful implications. It means that storage can be used to send messages from window to window, even if they are not all using the storage object itself. This, in turn implies that we can now share data across windows that have the same origin.
Let’s see how this works using some code samples. To listen to cross-window messages, a simple script needs only to register a handler for storage events. Let’s assume that a page running at
http://www.example.com/storageLog.html contains the code shown in Listing 11-15 (the sample file
storageLog.html for this example is also located in the
After registering an event listener for the
storage event type, this window will receive notification of storage changes in any pages. For example, if a browser window viewing
http://www.example.com/browser-test.html that is currently browsing the same origin sets or changes a new storage value, the
storageLog.html page will receive a notification. Therefore, to send a message to a receiving window, the sending window need only modify a storage object, and its old and new values will be sent as part of the notification. For example, if a storage value is updated using
localStorage.setItem(), then the
displayStorageEvent() handler in the
storageLog.html page hosted at the same origin will receive an event. By carefully coordinating event names and values, the two pages can now communicate, a feat which has been difficult to accomplish before. Figure 11-13 shows the
storageLog.html page in action, simply logging storage events it receives.
In this chapter, we showed how Web Storage can be used as an alternative to browser cookies for keeping local copies of data across windows, tabs, and (with
localStorage) even across browser restarts. You’ve seen that data can be appropriately segregated between windows by using
sessionStorage, and shared—even across windows—by using storage events. In our full-fledged example, we showed a practical way to use storage to track data from page to page as users navigate a website, which could just as easily be applied to other data types. We even demonstrated how nontext data types can be stored when a page loads or unloads to save and restore the state of a page across visits.
In the next chapter, we’ll show you how HTML5 lets you create offline applications.