Elisabeth Robson

Tutorial: How to use document fragments to increase browser performance

Posted in Blog, Featured, Technology, Tutorial by Elisabeth on October 28, 2011

In my new book, Head First HTML5 Programming, we have several examples where we create and add new elements to a web page. For example, in Chapter 9, which is about Web Storage, we build a sticky notes web app you can use to create and store sticky notes in the browser.

When you store data in the browser using local storage, it stays there even after you close your browser window. If you run the sticky notes app, add some notes, and then close the window, when you run the app again, you want to see the notes you added previously. That means when the app first starts up (ie, you load the web page), you need to load all the sticky notes already in local storage.

We show how to use a simple for loop to loop through all the notes in local storage, and then call a helper function, addStickyToDOM, like this:

for (var i = 0; i < localStorage.length; i++) {
   var key = localStorage.key(i);
   if (key.substring(0, 6) == "sticky") {
       var value = localStorage.getItem(key);
       addStickyToDOM(value);
   }
}

In addStickyToDOM, we create a new list item (<li>) and add the value of the sticky note to the stickies list (<ul>) in the DOM:

function addStickyToDOM(value) {
    var stickies = document.getElementById("stickies");
    var sticky = document.createElement("li");
    var span = document.createElement("span");
    span.setAttribute("class", "sticky");
    span.innerHTML = value;
    sticky.appendChild(span);
    stickies.appendChild(sticky);
}

That means, if you have a lot of sticky notes in local storage, you are updating the DOM many times in the loop: each time you call addStickyToDOM. This isn't very efficient, because the DOM could be a large structure if you have a big web page, and the browser has to look up the element and update the DOM any time you call appendChild.

A more efficient way to update the DOM is to use something called a document fragment. Essentially this is just a group of elements that are structured just like the DOM, but aren't actually in the DOM until you add the fragment all in one piece. Let's see how this might work in the sticky notes app.

First, you need to create a document fragment. Then you need to pass this document fragment to addStickyToDOM so that the function can add the sticky note to the fragment instead of the main DOM.

function init() {
    ...

    var stickies = document.getElementById("stickies");
    var fragment = document.createDocumentFragment();
    for (var i = 0; i < stickiesArray.length; i++) {
        var key = stickiesArray[i];
        var value = JSON.parse(localStorage[key]);
        addStickyToDOM(value, fragment);
    }
    stickies.appendChild(fragment);
}

First, notice that, unlike before, you need to get the stickies element in the function init(), because you'll be using it here (instead of in addStickyToDOM). Next, create a document fragment using the document.createDocumentFragment() method. You need to pass this fragment to addStickyToDOM since we'll be updating that function to add the sticky note to the fragment instead of the DOM. After all the notes in local storage have been read and added to the fragment, you finally need to add the fragment to the DOM with appendChild. This works just like appending an element, except you're appending a group of elements.

Think of this group just like a mini-DOM. It is a tree structure just like the DOM is; in this case, the tree structure is just a bunch of list item elements ready to add to the stickies <ul>.

Now it's time to update addStickyToDOM. First, make sure you add the parameter fragment. Next, remove the line that gets the stickies list from the DOM; you won't need that any more, since you're adding the list items to the fragment instead.

Last, change the line where you were adding the sticky note, the <li> you've just created with the value of the sticky note, to the stickies list and instead add it to the fragment.

function addStickyToDOM(value, fragment) {
    var stickies = document.getElementById("stickies");
    var sticky = document.createElement("li");
    var span = document.createElement("span");
    span.setAttribute("class", "sticky");
    span.innerHTML = value;
    sticky.appendChild(span);
    stickies.appendChild(sticky);
    fragment.appendChild(sticky);
}

Remember that even though JavaScript is "pass by value", because fragment is an object, when you modify fragment in addStickyToDOM, you're changing the same object that was passed in from the init function. So any changes you make to fragment in addStickyToDOM stick around (yeah, bad pun, I know!).

Give this a try. You probably won't notice any difference in performance in this app because you probably don't have too many sticky notes (unless you're incredibly addicted to lists), but if you create a web page with many many elements on it, you now know how to use this technique to increase performance.

When you update your code, don't forget you'll need to update the function createSticky too (because that function also calls addStickyToDOM).

I've put a completed version of the sticky notes app using a document fragment to increase performance in the code repo for the book. The new code is in the directory chapter9/performance.

Let me know if you try it!



Head First HTML5

Posted in Blog, Featured, Technology by Elisabeth on May 10, 2011

I am writing a new book, Head First HTML5, with my co-author, Eric Freeman. HTML5 is a major new version of HTML that includes new elements, as well as JavaScript APIs, which allow you to program many of the new elements, such as video and canvas, that are now part of the language. This is new for HTML; in previous versions, JavaScript APIs have not been part of the HTML specification. I see this as a positive step: it will provide a standard for browser-makers to implement and hopefully keep the differences among browsers to a minimum (fewer browser headaches for developers!).

Head First HTML5 is written for people who already know HTML and CSS and want to learn the new technologies that have been added to HTML. We’re focusing almost entirely on the JavaScript APIs and the new elements that require JavaScript: canvas, video and audio, localStorage, geoLocation, and so on. You’ll need to get your feet wet with JavaScript if you don’t already know it, and we’ll help get you there in Chapter 2 with an introduction to JavaScript.

You’ll find lots more information about Head First and my books at WickedlySmart.com (to be launched in about a month). And look for this book later this year, in fall of 2011.



Tagged with: , , , ,

Tab bars and Navigation bars together

Posted in Blog, Featured, Technology, Tutorial, Video by Elisabeth on June 21, 2009

The TableView is a common way to display data on an iPhone. For example, news apps like Wall Street Journal, New York Times and Huffington Post all use a table view to display a list of stories. When you click on one of those stories, you see the full news story in a detail view. At the top of these apps, you see a navigation bar that allows you to navigate back to the list of stories, and at the bottom, a tab bar that lets you select among different app functions or topics, for example.

Combining a tab bar with a table view and navigation bar isn’t very difficult, but it took me forever to figure out how to do it properly. I found the explanation of how to combine tab bars and navigation bars on Apple’s website woefully inadequate. I spent some time studying one of the examples that comes with the Apple documentation for Tab Bar Controllers, called TheElements, which builds this kind of app programmatically. After studying that for a while, it started to make more sense, but I really wanted to figure out how to do it using Interface Builder. I posted a couple of questions to the cocoadev mailing list, and got some really helpful answers and was eventually able to figure it out.

I’ve created an example to demonstrate how to build this kind of app and recorded a screencast partly so I’d never forget again, and also to help anyone else out there who might be struggling with this same challenge. There are lots of little steps that you have to do in just the right order to get it all working. A screencast seemed like the perfect way to demonstrate these steps since a lot of the work happens in Interface Builder and it’s much easier to show what actions to take than it is to describe them in text or even with pictures.

If you try to build this app yourself by following the screencast, I’d love to hear how it goes and if the screencast was helpful for you. And if you have any feedback on the process I’m using to build this app, I’d love to hear that too.

Or, to follow along more closely with the code, click here to view or download the original high resolution .mov file. [254 Mb]

Tagged with: