Quickstart: Using single-page 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 ]

Learn about the single-page navigation model and how you can implement it in your own app by using PageControl objects and WinJS.Navigation features.

For help choosing the best navigation pattern for your app, see Navigation patterns.

Also, you can see the Flat navigation and Hierarchical navigation patterns in action as part of our App features, start to finish series.

Prerequisites

One of the simplest and most commonly used forms of navigation is the hyperlink. Here's HTML code that creates a hyperlink that navigates to page2.html.

<p><a href="page2.html">Go to page 2</a></p>

Because it's a relative link, the system assumes that page2.html is a local page that's part of your app, relative to current document's base URL. For example, if the link appears in /folder/default.html, clicking the link takes you to /folder/page2.html.

Note  If you have optimized your app for localization (which is always a good idea), this behavior varies based on your localized content folders under /folder/. See Globalizing your app.

 

If you want to explicitly specify the full URI for a local file in your application, use the new packaged-content URL scheme (ms-appx) like this:

  • ms-appx://package name/file path

You can omit the package name if you want.

  • ms-appx:///file path

Here's an example.

<p><a href="ms-appx:///page2.html">Go to page 2</a></p>

You can use the ms-appx URL scheme to refer to any file that is included in the app’s package. However, we recommend using relative URLs because they're resolved automatically based on the document's base URL.

The previous example showed you how to create a link that performs a top-level navigation. That's fine for a webpage, but you shouldn't perform top-level navigation in an app for several reasons, including these:

  • The screen goes blank while the app loads the next page.
  • The script context is destroyed and must be initialized again. The app might receive system events but not handle them because the script context is being destroyed and reinitialized.

Using single-page navigation provides better performance and provides a more app-like experience than top-level navigation. Navigation controls are not automatically provided in new Microsoft Visual Studio projects, so a top-level navigation to a new page means there's no way to get back to the first page unless you manually create a link or other navigation mechanism to go back. Also, your start page is the place to define how your app manages its life cycle—how it behaves when it starts up, shuts down, or is suspended. If you have multiple top-level pages, each one must contain logic for managing the app's life cycle and state.

Which control should you use to bring content into your main page?

  • You can use the Document Object Model (DOM) to load documents from another source.
  • For simple HTML content (content that isn't interactive and doesn't contain script references), use an HtmlControl object.
  • For external web content, use the x-ms-webview control whenever possible. Compared with an iframe, this control provides better isolation, navigation, SmartScreen Filter availability, and support for sites that can’t be hosted in an iframe.
  • For all other content, use a Page control.

Visual Studio provides several JavaScript project templates for Windows Runtime apps that can make managing navigation easier. These project templates provide a navigation control, called PageControlNavigator, that you can use to navigate between PageControl objects. The PageControlNavigator class demonstrates one way that you can use the PageControl to facilitate single-page navigation.

The next steps show you how to create an app that contains multiple PageControl objects and how to use the single-page navigation model to navigate between those pages.

Step 1: Create a new Navigation App project

  1. Start Microsoft Visual Studio 2013 Update 2.

    Note  When you run Visual Studio for the first time, it prompts you to obtain a developer license.

     

  2. Choose File > New Project or click New Project from the Start Page tab. The New Project dialog box opens.

  3. In the Templates pane on the left, expand Installed => Templates => JavaScript => Store Apps.

  4. Select the Universal Apps template type. The available project templates for JavaScript are displayed in the center pane of the dialog box.

  5. In the center pane, select the Navigation App (Universal Apps) project template.

  6. In the Name text box, type a name for your project. The examples in this topic use "NavigationAppExample".

    The New Project dialog showing the JavaScript Universal Apps templates.

  7. Click OK to create the project.

    Your new Navigation App project contains a start page, a home page, and some additional supporting files. It looks like this in Solution Explorer.

    Files in the new Navigation Application project.

    Your new Navigation App project includes a number of files. Some are specific to a Windows app, some are specific to a Phone app, and some are shared.

    The project includes these HTML files:

    • default.html—the start page. It is loaded first. It contains an instance of the PageControlNavigator (which loads each page into the main window) and it's where you create your AppBar if your app uses one.Note  HTML pages are loaded into a single content host, a div element declared in default.html. In default.html, the content host div element is declared as a control of type PageControlNavigator using the data-win-control attribute that is provided by Windows Library for JavaScript (WinJS).  
    • home.html—the home page. It displays a "Welcome" title.

    The project includes these JavaScript files:

    • default.js—specifies how the app behaves when it's started.
    • home.js—specifies behavior that's associated with the home page.
    • navigator.js—implements the PageControlNavigator object that supports the navigation model for the Windows Store app JavaScript templates.

    The project includes these CSS files:

    • default.css—specifies Cascading Style Sheets (CSS) styles for the content host page and for the app as a whole.
    • home.css—specifies CSS styles for the home page.

    The project also includes the package.appxmanifest file, which describes the app package. Finally, the project also includes several image files, like splashscreen.png for the splash screen image, and storelogo.png, which is used for the Windows Store.

  8. Run the app. Choose Debug > Start Debugging, or choose F5 (choose SHIFT + F5 to stop debugging and return to Visual Studio).

    Here is a screen shot of the Windows app.

    The new Windows Navigation App.

    Here is a screen shot of the Phone app.

    The new Phone Navigation App.

    Notice that the content you're seeing isn't defined in the default.html file. It's defined in the home.html file, a separate page. It's the PageControlNavigator that retrieves the content of the home page and displays it inside default.html.

    The navigation control, PageControlNavigator is shown here. It defines several functions that are used for navigation. This control is implemented in navigator.js.

    
    WinJS.Namespace.define("Application", {
        PageControlNavigator: WinJS.Class.define(
            // Define the constructor function for the PageControlNavigator.
            function PageControlNavigator(element, options) {
                // Initialization code.
                }, {
                    // Members specified here.
                }
        ), // . . .
    });
    

    The constructor function performs initialization for the navigation control. A few important initialization tasks include setting handlers for WinJS events, such as the WinJS.Navigation.onnavigating event, and setting the home page of the app. (The home value is specified in the contenthost DIV element, in a data-win-options attribute.)

    
    // Initialization code.
    this.home = options.home;
    // . . .
    // The 'nav' variable is set to WinJS.Navigation.
    addRemovableEventListener(nav, 'navigating', this._navigating.bind(this), false);
    addRemovableEventListener(nav, 'navigated', this._navigated.bind(this), false);
    

    A DIV element that's declared as the app's navigation control (in default.html) provides the content host for all of the app's pages. The DIV element uses the WinJS data-win-control attribute to declare itself as the navigation control, and this control provides the navigation model for the app. All page content is loaded into this DIV.

    Here's the complete markup for the default.html page.

    
    <!-- default.html -->
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>NavigationAppExample</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>
    
        <!-- NavigationAppExample 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'}">
            </button>
        </div> -->
    </body>
    </html>
    

    This illustration shows the different units of content that appear in the app's window:

    Content breakdown for a Navigation Application.

In the next steps, you'll learn more about adding pages with the PageControlNavigator and the PageControl object.

Step 2: Create a new page

A PageControl is a modular unit of HTML, CSS, and JavaScript that functions as a logical page.

When you add a new page to a Visual Studio 2013 project, you need to:

  1. Add the new page using the Page Control item template in Visual Studio.
  2. Add code to navigate to the new page by using the WinJS.Navigation.navigate function. Tip  This function does not perform navigation directly, but invokes the WinJS.Navigation.onnavigated event, which is handled in navigator.js. Code in navigator.js calls the ready function in your new page. Typically, you don't have to modify navigator.js.  
  3. Add UI and event handlers appropriate to your app, if necessary, to invoke the page-navigation code.

A page has a set of predefined methods that the library calls automatically, in a predefined order. The WinJS.UI.Pages.define function exposes these methods for implementation.

Hh452768.wedge(en-us,WIN.10).gifAdd a page

  1. In Solution Explorer, right-click the pages folder and choose Add > New Folder.Note  For this example, we add a Shared page. You can add unique pages to the Windows or the Phone projects, as required.

     

  2. Name the new folder page2.

  3. Right click the page2 folder and select Add > New Item.

  4. In the Add New Item dialog box, choose Page Control in the middle pane.

  5. Name the page page2.html and choose Add.

    The page2.html file is created in the page2 folder, along with two other project files: page2.css and page2.js. Together, these files represent a logical page.

    Tip  If you add the item template elsewhere in the project, you may need to update script and CSS references in page2.html.

     

  6. Open page2.js and verify that the URI is correct in the define function. Here's what it should look like.

    WinJS.UI.Pages.define("/pages/page2/page2.html", {
        // . . .
        ready: function (element, options) {
            // ready function implementation.
        },
        // . . .
    });
    

Step 3: Customize your page

Now, modify your new page so that it displays a different message and the day of the week.

  1. Open page2.html. The Page Control item template creates an HTML page that contains a header section (which contains a back button) and a section for the page's main content.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>page2</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="page2.css" rel="stylesheet" />
        <script src="page2.js"></script>
    </head>
    <body>
        <div class="page2 fragment">
            <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 page2</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
                <p>Content goes here.</p>
            </section>
        </div>
    </body>
    </html>
    
  2. Replace the main content section with this one.

    
            <section aria-label="Main content" role="main">
                <p>Page controls make it easy to divide your app 
                    into modular portions.</p>
                <p>Today is <span id="dayPlaceholder"></span>.</p>
            </section>
    

    Your page2.html file should now look like this.

    
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>page2</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="page2.css" rel="stylesheet" />
        <script src="page2.js"></script>
    </head>
    <body>
        <div class="page2 fragment">
            <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 page2</span>
                </h1>
            </header>
            <section aria-label="Main content" role="main">
                <p>Page controls make it easy to divide your app 
                    into modular portions.</p>
                <p>Today is <span id="dayPlaceholder"></span>.</p>
            </section>
        </div>
    </body>
    </html>
    
  3. Open page2.js. The PageControl includes a set of predefined functions that get called automatically, in a predefined order. The Page Control item templates include the ready function and updateLayout and unload functions for you.

    The PageControlNavigator calls the updateLayout function when the user switches between portrait and landscape or changes the app windows size. This topic doesn't show you how to define the updateLayout, but it's something that every app should do. See Guidelines for resizing windows to tall and narrow layouts and Responsive design 101 for Universal Windows Platform (UWP) apps.

    The ready function is called when the page's DOM has been loaded, controls are activated, and the page has been loaded into the main DOM.

    (The PageControl supports additional functions for the page life cycle. For more info, see Adding Page controls.)

    Here's the page2.js file that the Page Control item template created.

    
    // page2.js
    (function () {
        "use strict";
    
        WinJS.UI.Pages.define("/pages/page2/page2.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.
            },
    
            unload: function () {
                // TODO: Respond to navigations away from this page.
            },
    
            updateLayout: function (element) {
                /// <param name="element" domElement="true" />
    
                // TODO: Respond to changes in layout.
            }
        });
    })();
    
  4. Modify the ready function so that it retrieves the span you created in step 2 ("dayPlaceholder") and sets its innerText to the current day.

    
    // page2.js
    (function () {
        "use strict";
    
        WinJS.UI.Pages.define("/pages/page2/page2.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.
    
                var dayPlaceholder = document.querySelector("#dayPlaceholder");
                var calendar = new Windows.Globalization.Calendar();
                dayPlaceholder.innerText =
                    calendar.dayOfWeekAsString();
            },
    
            unload: function () {
                // TODO: Respond to navigations away from this page.
            },
    
            updateLayout: function (element) {
                /// <param name="element" domElement="true" />
    
                // TODO: Respond to changes in layout.
            }
        });
    })();
    

You've created and customized a page. In the next step, you'll enable the user who's running the app to navigate to your page.

Step 4: Use the navigate function to move between pages

When you run the app right now, it displays home.html; there's no way for the user to navigate to page2.html. A simple way to help the user get to page2.html is to link to it from home.html.


<!-- home.html -->
<!DOCTYPE html>
<html>
<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>
<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 NavigationAppExample!</span>
            </h1>
        </header>
        <section aria-label="Main content" role="main">
            <p>Content goes here.</p>

            <!-- A hyperlink to page2.html. -->
            <p><a href="/pages/page2/page2.html">Go to page 2.</a></p>
        </section>
    </div>
</body>
</html>

If you run the app and click the link, it seems to work: the app displays page2.html. The problem is that the app performs a top-level navigation. Instead of going from home.html to page2.html, it goes from default.html to page2.html.

Performing a top-level navigation.

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

Navigating to page2.html the recommended way.

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.onnavigated 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 instead of 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 it, the app uses WinJS.Navigation.navigate to go 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; then call the WinJS.Navigation.navigate function and pass it the link target. Here's how to do that:

  1. In your home.js file, define a click event handler for your hyperlinks.

    
    function linkClickEventHandler(eventInfo) {
    
    }
    
  2. Call the preventDefault method to prevent the default link behavior (which would navigate directly to the specified page).

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

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

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

        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.
                WinJS.Utilities.query("a").listen("click", linkClickEventHandler, false); 
    
            }
        });
    

That's the last step. Here's the complete code for your home.js file.


// 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.
            WinJS.Utilities.query("a").listen("click", linkClickEventHandler, false); 
        
        }
    });

    function linkClickEventHandler(eventInfo) {
        eventInfo.preventDefault();
        var link = eventInfo.target;
        WinJS.Navigation.navigate(link.href);
    }


})();

Run the app and click the link for page2.html. Here's what you see.

The page2.html screen with a back button

This time the page is displayed using the proper navigation pattern.

Content breakdown after navigating to page2.html.

The Page control template includes a back button that is enabled when you use the WinJS.Navigation functions to navigate. When you use the WinJS.Navigation functions, the app stores navigation history for you. You can use that history to navigate backward by calling WinJS.Navigation.back or to navigate forward by calling WinJS.Navigation.forward.

Note  Windows Store apps typically use one of two navigation patterns (flat and hierarchical). We recommend that you do not use the BackButton object in flat navigation apps. For details, see Navigation patterns.

 

Saving navigation history when your app is suspended

Navigation history is not automatically stored when your app is suspended or shut down, but it's easy to store this info yourself. Use the WinJS.Navigation.history property to retrieve the navigation history and use the WinJS.Application.sessionState object to store it. To ensure a smooth suspend/resume experience, it's best to store this info each time the user navigates.

When your app resumes, retrieve the history info from the WinJS.Application.sessionState object and use it to set the WinJS.Navigation.history property.

For an example of how to use the WinJS.Application.sessionState object to store and restore session data, see Part 2: Manage app lifecycle and state. For more info about suspending and resuming, see Launching, resuming, and multitasking.

Summary

You learned how to use objects and methods of the WinJS.UI.Pages namespace to support the single-page navigation model.

You learned how to build apps using the single-page navigation model. You used the PageControlNavigator class, provided by the templates, to implement this model in your own app using PageControl objects and WinJS.Navigation features.

For developers

Your first app - Part 3: PageControl objects and navigation

Adding app bars

Quickstart: Adding a nav bar (NavBar)

Quickstart: Using a hub control for layout and navigation

WinJS.Navigation Namespace

WinJS.UI.Hub object

WinJS.UI.AppBar object

WinJS.UI.NavBar object

WinJS.UI.BackButton object

HTML Hub control sample

HTML AppBar control sample

HTML NavBar control sample

Navigation and navigation history sample

For designers

Navigation patterns

Command patterns

Layout

Back button

Guidelines for the hub control

Guidelines for app bars (Windows Store apps)

Making the app bar accessible