Part 3: PageControl objects and navigation (HTML)

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

The "Hello, world" app that you created in the previous tutorials only contains a single page of content. Most real-world apps contain multiple pages.

There are various navigation patterns that can be used in a Windows Store app. Navigation patterns will help you choose the best navigation pattern for your app.

Note  

See the two primary navigation patterns (Flat navigation and Hierarchical navigation) in action as part of our App features, start to finish series.

 

In this tutorial, you copy the code from your "Hello, world" app into a new app that uses the Navigation App template, and then you add an additional page.

Learn how to:

  • Use the Navigation App project template to create an app that contains multiple pages of content.
  • Use PageControl objects to separate your code into modular units.
  • Use the single-page navigation model to navigate between pages.
  • Use an NavBar to provide navigation commands.

Tip  

If you want to skip the tutorial and go straight to the code, see Getting started with JavaScript: Complete code for the tutorial series.

 

Before you start...

About navigation in Windows Store apps

Nearly every website provides some form of navigation, usually in the form of hyperlinks that you click to go to a different page. Each page has its own set of JavaScript functions and data, a new set of HTML to display, style information, and so on. This navigation model is known as multi-page navigation. This design is fine for most websites, but it can pose problems for an app because it makes it difficult to maintain state across the different pages.

Another navigation model is single-page navigation, where you use a single page for your app and load additional data into that page as needed. You still split your application into multiple files, but instead of moving from page to page, your app loads other documents into the main page. Because your app's main page is never unloaded, your scripts are never unloaded, which makes it easier to manage state, transitions, or animations. We recommend that Windows Store apps use the single-page navigation model.

To help you create apps that use the single-page navigation model, the Windows Library for JavaScript provides the WinJS.UI.Pages.PageControl object. There's also the Navigation App project template, which provides some additional navigation infrastructure. In the next step, you use this template to create a new project.

Step 1: Create a new Navigation App in Visual Studio

Let's create a new app named HelloWorldWithPages that uses the Navigation App template. Here's how:

  1. Launch Microsoft Visual Studio Express 2013 for Windows.

  2. From the File menu select New Project.

    The New Project dialog appears. The left pane of the dialog lets you pick the type of templates to display.

  3. In the left pane, expand Installed, then expand Templates, then expand JavaScript and select the Windows Store template type. The dialog's center pane displays a list of project templates for JavaScript.

    For this tutorial, we use the Navigation App template.

  4. In the center pane, select the Navigation App template.

  5. In the Name text box, enter "HelloWorldWithPages".

  6. Uncheck the Create directory for solution checkbox.

  7. Click OK to create the project.

    Visual Studio creates your project and displays it in the Solution Explorer.

Notice that your new Navigation App contains a few more files than your "Hello, world" app does. Let's take a look at these new files:

  • /pages/home/home.css, /pages/home/home.html, and /pages/home/home.js

    These three pages define a PageControl for the app's home page. A PageControl is made up of an HTML file, a JavaScript file, and a CSS file. A PageControl is a modular unit of HTML, CSS, and JavaScript that can be navigated to (like an HTML page) or used as a custom control. You can use PageControl objects to split a large app into smaller, more manageable portions.

    PageControl objects support several methods that makes using them in your app easier than using a collection of loose HTML, CSS, and JavaScript pages. You'll learn more about these methods in a later step.

  • /js/navigator.js

    This file provides the PageControlNavigator helper class that you can use to display PageControl objects and navigate between them. You don't need it to display a PageControl, but it can make using them easier.

Let's take a look at the default.html page of your new app:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>HelloWorldWithPages</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    <script src="//Microsoft.WinJS.2.0/js/base.js"></script>
    <script src="//Microsoft.WinJS.2.0/js/ui.js"></script>

    <!-- HelloWorldWithPages references -->
    <link href="/css/default.css" rel="stylesheet" />
    <script src="/js/default.js"></script>
    <script src="/js/navigator.js"></script>
</head>
<body>

    <div id="contenthost" data-win-control="Application.PageControlNavigator" 
        data-win-options="{home: '/pages/home/home.html'}"></div>
    <!-- <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand" 
            data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}" 
            type="button"></button>
    </div> -->
</body>
</html>

The body of the file contains two elements: A div element for the PageControlNavigator and a commented-out div for an AppBar. Let's ignore the app bar for now and take a closer look at the first div element.

    <div id="contenthost" data-win-control="Application.PageControlNavigator" 
        data-win-options="{home: '/pages/home/home.html'}"></div>
 

This div element creates a PageControlNavigator control. The PageControlNavigator loads and displays our home page for us. You use the data-win-options attribute to tell it which page to load (/pages/home/home.html).

Go ahead and run the app.

Although it's not obvious, the app is actually showing both default.html and home.html. It's similar to using an iframe to display a HTML page inside another HTML page.

Step 2: Copy your HTML and CSS content from your "Hello, world" app

Our new app has two HTML pages: default.html and home.html. Where do you put your content?

  • Use default.html for UI that should always be present, no matter which page the app is displaying. For example, you can use default.html to host a navigation bar.

  • Use pages, such as home.html, for content that makes up an individual screen in the app.

Let's open home.html and take a look at some of the markup it contains.

  • It has a head element that contains references to the WinJS code and style sheets. It also contains references to the app's default style sheet (default.css) and to the other files that make up the home page (home.css and home.js).

    <head>
        <meta charset="utf-8" />
        <title>homePage</title>
    
        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
        <script src="//Microsoft.WinJS.2.0/js/base.js"></script>
        <script src="//Microsoft.WinJS.2.0/js/ui.js"></script>
    
        <link href="/css/default.css" rel="stylesheet" />
        <link href="/pages/home/home.css" rel="stylesheet" />
        <script src="/pages/home/home.js"></script>
    </head>
    
  • It has a page header area that includes a BackButton for backwards navigation and a title area. The template contains code that automatically enables the back button when you can navigate backwards. The button won't be visible until we add a second page and navigate there.

            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
                </h1>
            </header>
    
  • It has a section for your main content.

            <section aria-label="Main content" role="main">
                <p>Content goes here.</p>
            </section>
    

Let's add the content from our "Hello, world" app to the home page (home.html) of our new HelloWorldWithPages project.

To add the HTML and CSS content from your "Hello, world" app

  1. Copy your final HTML content from the default.html file of your "Hello, world" app into the main content section of the /pages/home/home.html in your new project.

    <body>
        <!-- The content that will be loaded and displayed. -->
        <div class="fragment homepage">
            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
    
                <!-- Copied from "Hello, world" -->
                <h1 class="headerClass">Hello, world!</h1>
                <div class="mainContent">
                    <p>What's your name?</p>
                    <input id="nameInput" type="text" />
                    <button id="helloButton">Say "Hello"</button>
                    <div id="greetingOutput"></div>
                    <label for="ratingControlDiv">
                        Rate this greeting: 
                    </label>
                    <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
                    </div>
                    <div id="ratingOutput"></div>
                </div>
            </section>
        </div>
    </body>
    
  2. Move the heading content that you copied to the h1 element that home.html provides for you. Since home.html already contains a main content section, remove the "mainContent" div element that you copied (but leave its contents).

    <body>
        <!-- The content that will be loaded and displayed. -->
        <div class="fragment homepage">
            <header aria-label="Header content" role="banner">
                <button data-win-control="WinJS.UI.BackButton"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Welcome to HelloWorldWithPages!</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
    
                <p>What's your name?</p>
                <input id="nameInput" type="text" />
                <button id="helloButton">Say "Hello"</button>
                <div id="greetingOutput"></div>
                <label for="ratingControlDiv">
                    Rate this greeting: 
                </label>
                <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
                </div>
                <div id="ratingOutput"></div>
            </section>
        </div>
    </body>
    
  3. Switch to the light style sheet. Replace the reference to the dark style sheet:

        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    

    With this one:

        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.2.0/css/ui-light.css" rel="stylesheet" />
    
  4. Each PageControl has its own Cascading Style Sheets (CSS) file.

    Copy the greetingOutput style from the default.css file you created in Part 1: Create a "Hello, world!" app to home.css.

    .homepage section[role=main] {
        margin-left: 120px;
        margin-right: 120px;
    }
    
    #greetingOutput {
        height: 20px; 
        margin-bottom: 40px;
    }
    
  5. Run the app.

    You've recreated the content from your original "Hello, world" app. Next, we add interactivity by copying your "Hello, world" event handlers.

Step 3: Copy your event handlers

Each PageControl has its own JavaScript file. Let's take a look at the JavaScript file that Visual Studio created for our "home" PageControl, home.js:

(function () {
    "use strict";

    WinJS.UI.Pages.define("/pages/home/home.html", {
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            // TODO: Initialize the page here.
        }
    });
})();

This file looks quite a bit different than your default.js file. For one thing, it's much shorter. That's because default.js already handles activation and core app logic. Each PageControl only needs to contain logic for the page itself.

One of the first lines of code, a call to the WinJS.UI.Page.define function, creates the PageControl object. This function takes two parameters: the URI of the page ("/pages/home/home.html" in this example), and an object that defines the members of the PageControl. You can add any type of member you want. You can also implement a set of special members, described by the IPageControlMembers interface, that automatically get called by the app when you use the PageControl.

The home.js file created by the template defines one of these special members, the ready function. The ready function is called after your page is initialized and rendered. This is a good place to attach event handlers.

You might notice that the code doesn't include a call to WinJS.UI.processAll. That's because the PageControl calls it for you, automatically. By the time the ready function is called, WinJS.UI.processAll has already been called and has completed its processing.

To add your event handlers

  1. In Part 1: Create a "Hello, world!" app and Part 2: Manage app lifecycle and state, you defined three event handlers: buttonClickHandler, ratingChanged, and nameInputChanged. Copy those event handlers to your home.js file and make them members of your PageControl. Add them after the ready function that the template created for you.

        WinJS.UI.Pages.define("/pages/home/home.html", {
    
            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                // TODO: Initialize the page here.
    
            },
    
            buttonClickHandler: function (eventInfo) {
    
                var userName = document.getElementById("nameInput").value;
                var greetingString = "Hello, " + userName + "!";
                document.getElementById("greetingOutput").innerText = greetingString;
    
                // Save the session data. 
                WinJS.Application.sessionState.greetingOutput = greetingString;
            },
    
            ratingChanged: function (eventInfo) {
    
                var ratingOutput = document.getElementById("ratingOutput");
                ratingOutput.innerText = eventInfo.detail.tentativeRating;
    
                // Store the rating for multiple sessions.
                var appData = Windows.Storage.ApplicationData.current;
                var roamingSettings = appData.roamingSettings;
                roamingSettings.values["greetingRating"] = eventInfo.detail.tentativeRating;
            },
    
            nameInputChanged: function (eventInfo) {
                var nameInput = eventInfo.srcElement;
    
                // Store the user's name for multiple sessions.
                var appData = Windows.Storage.ApplicationData.current;
                var roamingSettings = appData.roamingSettings;
                roamingSettings.values["userName"] = nameInput.value;
            }
    
    
        });
    
  2. Now we need to attach our event handlers. In Parts 1 and 2, we created a then function for the Promise returned by WinJS.UI.processAll. Things are a bit simpler now, because we can use the ready function to attach our event handlers. The ready function is called after the PageControl has automatically called WinJS.UI.processAll for us.

    Copy the code that attaches your event handlers to the ready function in home.js.

            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                // TODO: Initialize the page here.
    
                // Retrieve the div that hosts the Rating control.
                var ratingControlDiv = document.getElementById("ratingControlDiv");
    
                // Retrieve the actual Rating control.
                var ratingControl = ratingControlDiv.winControl;
    
                // Register the event handler. 
                ratingControl.addEventListener("change", this.ratingChanged, false);
    
                // Retrieve the button and register our event handler. 
                var helloButton = document.getElementById("helloButton");
                helloButton.addEventListener("click", this.buttonClickHandler, false);
    
                // Retrieve the input element and register our
                // event handler.
                var nameInput = document.getElementById("nameInput");
                nameInput.addEventListener("change", this.nameInputChanged);
    
            },
    
  3. Run the app. When you enter a name and click the button, it displays a greeting. When you rate the greeting, it displays the numeric rating value.

Step 4: Restore the app's state

We've almost recreated the functionality we had in our "Hello, world" app. The only thing we need to do now is restore the app's state when the user launches it.

You might remember that we had two types of app state to restore:

  • The user name and rating. We restore this state regardless of how the app was shut down.
  • The personalized greeting. We only restore this state if the app was successfully terminated the last time it ran.

To restore the app's state

  1. Copy the code from the "Hello, world" app that restores the user name and rating. Add the code to the ready function in home.js.

            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                // TODO: Initialize the page here.
    
                // Retrieve the div that hosts the Rating control.
                var ratingControlDiv = document.getElementById("ratingControlDiv");
    
                // Retrieve the actual Rating control.
                var ratingControl = ratingControlDiv.winControl;
    
                // Register the event handler. 
                ratingControl.addEventListener("change", this.ratingChanged, false);
    
                // Retrieve the button and register our event handler. 
                var helloButton = document.getElementById("helloButton");
                helloButton.addEventListener("click", this.buttonClickHandler, false);
    
                // Retrieve the input element and register our
                // event handler.
                var nameInput = document.getElementById("nameInput");
                nameInput.addEventListener("change", this.nameInputChanged);
    
                // Restore app data. 
                var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
    
                // Restore the user name.
                var userName =
                    Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
                if (userName) {
                    nameInput.value = userName;
                }
    
                // Restore the rating. 
                var greetingRating = roamingSettings.values["greetingRating"];
                if (greetingRating) {
                    ratingControl.userRating = greetingRating;
                    var ratingOutput = document.getElementById("ratingOutput");
                    ratingOutput.innerText = greetingRating;
                }
    
            },
    
  2. We only want to restore the personalized greeting if the app was successfully terminated the last time it ran. Unfortunately, our PageControl doesn't provide a built-in way for us to check the app's previous execution state: that info is provided to the onactivated event handler in our default.js file. But there's an easy solution to this problem: we just need to save the app's previous execution state in the sessionState object so that our PageControl can access it.

    1. In your default.js file, add code to your onactivated handler to save the previous execution state. Save the state by adding a property to the sessionState object named previousExecutionState.

          app.addEventListener("activated", function (args) {
              if (args.detail.kind === activation.ActivationKind.launch) {
                  if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                      // TODO: This application has been newly launched. Initialize
                      // your application here.
                  } else {
                      // TODO: This application has been reactivated from suspension.
                      // Restore application state here.
                  }
      
                  // Save the previous execution state. 
                  WinJS.Application.sessionState.previousExecutionState = 
                      args.detail.previousExecutionState;
      
                  if (app.sessionState.history) {
                      nav.history = app.sessionState.history;
                  }
                  args.setPromise(WinJS.UI.processAll().then(function () {
                      if (nav.location) {
                          nav.history.current.initialPlaceholder = true;
                          return nav.navigate(nav.location, nav.state);
                      } else {
                          return nav.navigate(Application.navigator.home);
                      }
                  }));
              }
          });
      
    2. In your home.js file, add code to your ready method that checks the previousExecutionState data. If the previous execution state is terminated, restore the personalized greeting (you can copy your code that does this from the default.js file in your "Hello, world" app.)

                  // If the app was terminated last time it ran, restore the personalized
                  // greeting. 
                  if (
                      WinJS.Application.sessionState.previousExecutionState
                      === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                      var outputValue = WinJS.Application.sessionState.greetingOutput;
                      if (outputValue) {
                          var greetingOutput = document.getElementById("greetingOutput");
                          greetingOutput.innerText = outputValue;
                      }
      
                  }
      

      Here's the complete ready method.

              // This function is called whenever a user navigates to this page. It
              // populates the page elements with the app's data.
              ready: function (element, options) {
                  // TODO: Initialize the page here.
      
                  // Retrieve the div that hosts the Rating control.
                  var ratingControlDiv = document.getElementById("ratingControlDiv");
      
                  // Retrieve the actual Rating control.
                  var ratingControl = ratingControlDiv.winControl;
      
                  // Register the event handler. 
                  ratingControl.addEventListener("change", this.ratingChanged, false);
      
                  // Retrieve the button and register our event handler. 
                  var helloButton = document.getElementById("helloButton");
                  helloButton.addEventListener("click", this.buttonClickHandler, false);
      
                  // Retrieve the input element and register our
                  // event handler.
                  var nameInput = document.getElementById("nameInput");
                  nameInput.addEventListener("change", this.nameInputChanged);
      
                  // Restore app data. 
                  var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
      
                  // Restore the user name.
                  var userName =
                      Windows.Storage.ApplicationData.current.roamingSettings.values["userName"];
                  if (userName) {
                      nameInput.value = userName;
                  }
      
                  // Restore the rating. 
                  var greetingRating = roamingSettings.values["greetingRating"];
                  if (greetingRating) {
                      ratingControl.userRating = greetingRating;
                      var ratingOutput = document.getElementById("ratingOutput");
                      ratingOutput.innerText = greetingRating;
                  }
      
                  // If the app was terminated last time it ran, restore the personalized
                  // greeting. 
                  if (
                      WinJS.Application.sessionState.previousExecutionState
                      === Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                      var outputValue = WinJS.Application.sessionState.greetingOutput;
                      if (outputValue) {
                          var greetingOutput = document.getElementById("greetingOutput");
                          greetingOutput.innerText = outputValue;
                      }
      
                  }
      
              },
      
  3. Run the app. We've now duplicated the functionality we had in our original "Hello, world" app.

Step 5: Add another page

Most apps contain several pages. Let's add another page to our app. Because we're using the Navigation App template, it's easy to add additional pages.

To add another page

  1. In the Solution Explorer, right-click the pages folder and select Add > New Folder. A new folder appears in the project.

  2. Rename the folder to "page2".

  3. Right-click the page2 folder and select add Add > New Item.... The Add New Item dialog appears.

  4. Select Page Control from the list. In the Name text box, enter "page2.html".

  5. Click Add to add the PageControl. The new PageControl appears in the Solution Explorer.

    The new PageControl has three files: page2.css, page2.html, and page2.js.

  6. In your page2.html file, replace the reference to the dark style sheet:

        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.2.0/css/ui-dark.css" rel="stylesheet" />
    

    With this one:

        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.2.0/css/ui-light.css" rel="stylesheet" />
    

You've created a new page. In the next step, you learn how to navigate to it.

Step 6: Use the navigate function to move between pages

Right now, we have a second page but no way for the user to get to it. Let's update our home.html page by adding a link to page2.html.

To navigate between pages

  1. Open your home.html and add a link to page2.html.

    <body>
        <!-- The content that will be loaded and displayed. -->
        <div class="fragment homepage">
            <header aria-label="Header content" role="banner">
                <button class="win-backbutton" aria-label="Back" disabled type="button"></button>
                <h1 class="titlearea win-type-ellipsis">
                    <span class="pagetitle">Hello, world!</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
    
                <p>What's your name?</p>
                <input id="nameInput" type="text" />
                <button id="helloButton">Say "Hello"</button>
                <div id="greetingOutput"></div>
                <label for="ratingControlDiv">
                    Rate this greeting: 
                </label>
                <div id="ratingControlDiv" data-win-control="WinJS.UI.Rating">
                </div>
                <div id="ratingOutput"></div>
    
                <!-- A hyperlink to page2.html. -->
                <p><a href="/pages/page2/page2.html">Go to page 2.</a></p>
            </section>
        </div>
    </body>
    
  2. Run the app and click the link. It seems to work: the app displays page2.html.

However, there's a problem: the app performed a top-level navigation. Instead of navigating from home.html to page2.html, it navigates from default.html to page2.html.

What you want instead is to replace the content of home.html with page2.html.

Fortunately, the PageControlNavigator control makes performing this type of navigation fairly easy. The PageControlNavigator code (in your app's navigator.js file) handles the WinJS.Navigation.navigated event for you. When the event occurs, the PageControlNavigator loads the page specified by the event.

The WinJS.Navigation.navigated event occurs when you use the WinJS.Navigation.navigate, WinJS.Navigation.back, or WinJS.Navigation.forward functions to navigate.

You need to call WinJS.Navigation.navigate yourself rather than using the hyperlink's default behavior. You could replace the link with a button and use the button's click event handler to call WinJS.Navigation.navigate. Or you could change the default behavior of the hyperlink so that when the user clicks on a link, the app uses WinJS.Navigation.navigate to navigate to the link target. To do this, handle the hyperlink's click event and use the event to stop the hyperlink's default navigation behavior, and then call the WinJS.Navigation.navigate function and pass it the link target.

To override the default hyperlink behavior

  1. In your home.js file, define a click event handler for your hyperlinks and make it a member of your PageControl. Name it linkClickEventHandler and add it after the nameInputChanged handler.

            nameInputChanged: function (eventInfo) {
                var nameInput = eventInfo.srcElement;
    
                // Store the user's name for multiple sessions.
                var appData = Windows.Storage.ApplicationData.current;
                var roamingSettings = appData.roamingSettings;
                roamingSettings.values["userName"] = nameInput.value;
            },        
    
    
            linkClickEventHandler: function (eventInfo) {
    
            }
    
  2. Call the preventDefault method to prevent the default link behavior (navigating directly to the specified page).

            linkClickEventHandler: function (eventInfo) {
                eventInfo.preventDefault();
    
            }
    
  3. Retrieve the hyperlink that triggered the event.

            linkClickEventHandler: function (eventInfo) {
                eventInfo.preventDefault();
                var link = eventInfo.target;
    
            }
    
  4. Call the WinJS.Navigation.navigate function and pass it the link target. (Optionally, you could also pass a state object that describes the state for that page. For more information, see the WinJS.Navigation.navigate page.)

            linkClickEventHandler: function (eventInfo) {
                eventInfo.preventDefault();
                var link = eventInfo.target;
                WinJS.Navigation.navigate(link.href);
            }
    
  5. In the home.js file's ready function, attach the event handler to your hyperlinks.

    The WinJS provides a WinJS.Utilities.query function that makes it easy to retrieve a number of elements on the page. The WinJS.Utilities.query function returns a QueryCollection, which provides additional methods for attaching and removing event handlers. Let's use the WinJS.Utilities.query collection and the listen method to attach our linkClickEventHandler.

            // This function is called whenever a user navigates to this page. It
            // populates the page elements with the app's data.
            ready: function (element, options) {
                // TODO: Initialize the page here.
    
                WinJS.Utilities.query("a").listen("click", 
                    this.linkClickEventHandler, false);
    

    The nice thing about this approach is that it will work for any number of links on the page. We only have one link right now, but, with this approach, we could add more links and we wouldn't have to change our code.

  6. Run the app and click the link for page2.html.

    Now the page displays using the proper navigation pattern.

Step 7: Add a NavBar for additional navigation

The NavBar is like an AppBar that's dedicated to navigation commands. (In fact, the NavBar is a subclass of the AppBar.) It can contain a simple list of links, and it can contain several levels of links organized into categories. You can populate the NavBar by hard-coding entries, programmatically updating it, or by using data binding.

The NavBar appears at the top of the app screen when the user needs it. The user invokes the NavBar by performing an edge swipe, pressing the Windows logo key+Z, or right-clicking.

The NavBar also supports vertical layouts and split-navigation items (navigation items that have child navigation options). The NavBar is highly customizable: you can use CSS to style almost any aspect of the NavBar and its contents, and you can also create custom navigation items.

The nav bar is a WinJS control. To declare one in your HTML, you use this syntax:

<div id="navbar" data-win-control="WinJS.UI.NavBar">

</div>

The NavBar has three components:

  • The NavBar itself.
  • A NavBarContainer object, which contains navigation items (NavBarCommand objects) and supports both pagination and panning and scrolling. You can have one or more NavBarContainer objects in a single NavBar. You use NavBarContainer objects to define groups of navigation options.
  • One or more NavBarCommand objects. These are what the user clicks to navigate.

Here's an example of a simple NavBar:

<div id="NavBar" data-win-control="WinJS.UI.NavBar">
    <div id="GlobalNav" data-win-control="WinJS.UI.NavBarContainer">
            <div data-win-control="WinJS.UI.NavBarCommand" data-win-options="{
                label: 'Home',
                icon: WinJS.UI.AppBarIcon.home,
                location: '/html/home.html',
                splitButton: false
                }">
            </div>
            <div data-win-control="WinJS.UI.NavBarCommand" data-win-options="{
                label: 'Your apps',
                icon: WinJS.UI.AppBarIcon.favorite,
                location: '/html/yourapps.html',
                splitButton: false
                }">
            </div>
    </div>
</div>

Our app contains several pages. Where should we add our nav bar?

  • If your nav bar contains commands that should be available to every page, add it to your default.html file.
  • If your nav bar is different from page to page, you can define a different app bar in each of your PageControl objects.
  • You can also define a central navr bar in default.html and then modify as you load different PageControl objects.

Let's create a simple nav bar that lets the user navigate between home.html and page2.html. We'll define our navr bar in our default.html file.

To add a nav bar

  1. Open your default.html file. Add a NavBar control as the first child of the body element.

        <div id="navbar" data-win-control="WinJS.UI.NavBar">
    
        </div>
    
    
  2. Each nav bar contains one or more NavBarContainer objects. You use NavBarContainer objects to define groups of navigation options. Add a NavBarContainer to your nav bar.

        <div id="navbar" data-win-control="WinJS.UI.NavBar">
            <div id="globalNav" data-win-control="WinJS.UI.NavBarContainer">
    
            </div>
        </div>
    
    
  3. Each NavBarContainer contains one or more NavBarCommand objects. These are what the user clicks to navigate. Add NavBarCommand objects to your NavBarContainer.

    Here are the properties to set for each NavBarCommand:

    • label: The label to display for the command.

    • icon: The icon to display for the command, or the path to a custom PNG file. (For a list of icon values, see AppBarIcon.)

    • location: The location to go to.

    • splitButton: Whether the command has a sub-menu. (The default is false.)

        <div id="navbar" data-win-control="WinJS.UI.NavBar">
            <div id="globalNav" data-win-control="WinJS.UI.NavBarContainer">
                    <div data-win-control="WinJS.UI.NavBarCommand" data-win-options="{
                        label: 'Home',
                        icon: WinJS.UI.AppBarIcon.home,
                        location: '/pages/home/home.html',
                        splitButton: false
                        }">
                    </div>
                    <div data-win-control="WinJS.UI.NavBarCommand" data-win-options="{
                        label: 'Page 2',
                        icon: WinJS.UI.AppBarIcon.page,
                        location: '/pages/page2/page2.html',
                        splitButton: false
                        }">
                    </div>
            </div>
        </div>
    
    

    (This example shows how to define NavBarCommand objects inline, but you can also generate them from a data source. For an example, see the HTML NavBar sample.)

  4. Run the app. To show the bar, make an edge swipe, press Win + Z, or click the right mouse button.

    When you click a button, the nav bar automatically calls navigate and takes you to the page.

    You've declared an nav bar. Next, let's style it.

  5. Open your default.css file and, at the beginning of the file, create a CSS style for the app bar that changes its background color.

    #navbar {
    
        background-color: #03abe2;
    }
    
  6. Run the app. Now you can use the nav bar to navigate between the two pages.

Summary

Congratulations, you're done with the third tutorial! You learned how to create a project that uses the Navigation App template and how to use PageControl objects. You also learned how to create an app bar.

Download the sample

Did you get stuck, or do you want to check your work? If so, download the Getting started with JavaScript sample.

Next steps

In the next part of this tutorial series, you learn how to create a more complex app. Go to Part 4: Layout and views.

Getting started with JavaScript: Complete code for the tutorial series

Programming Windows 8 Apps with HTML, CSS, and JavaScript (ebook)

For designers

Navigation patterns

Command patterns

Layout

Guidelines for back buttons

Guidelines for hub controls

Guidelines for app bars (Windows Store apps)

Making the app bar accessible

For developers (HTML)

Navigation and navigation history sample

Adding app bars

Adding nav bars

Navigating between pages (HTML)

HTML Hub control sample

HTML AppBar control sample

HTML NavBar control sample

WinJS.UI.Hub object

WinJS.UI.AppBar object

WinJS.UI.NavBar object

WinJS.UI.BackButton object