Transform Your HTML/CSS/JavaScript Apps into Windows 8 Application

With HTMLand JavaScript becomes the first class citizen to develop windows 8 apps, you can transform your existing HTMLor web apps into windows 8 apps quite easily.  You may already have a web app on the Internet and want to reach customers with a new, targeted experience on Windows 8. In this article, I will provide some practical guidance and best practices on how to reuse your web assets, and how you can use your Web skills to build deeply-integrated Windows apps. 

First we should understand some difference between web and windows 8.

Difference between Windows 8 app vs Web App

A Windows 8 store style app built using HTML5, CSS3 and JavaScript, uses the same technology that a web site uses. However, a Windows 8 style app is not deployed page-by-page from a web server. Instead, it is installed locally on the user's computer. Like any native Windows application, a Windows 8 style app built using HTML has direct access to the underlying platform and is able to share information with other applications.

Local and web context

To understand some of the differences between how your markup and code behave in the browser and how they behave in a Windows Store app using JavaScript, you need to first understand the difference between the local context and the web context.

A Windows Store app using JavaScript contains at least one HTML page. That page, and any other pages you include in the app itself, generally run in the app's local context. When you use an iframe to navigate to a remote page, that page runs in the web context and has limited access to your system.

Although they have access to more of the system than other external pages, web context pages don't have as much access as local context pages. For example, a page in the web context can't access the Windows Runtime, but a page in the local context can. For more info about the differences between local and web contexts, see Features and restrictions by context.

How to Enable Some Access

You can find more information from HTML, CSS, and JavaScript features and differences (Windows Store apps).  Also, if you can find which content is considered safe, and which is not,  from article Making HTML safer: details for toStaticHTML.

  • ApplicationContentUriRules

You can use the ApplicationContentUriRules section of the app's package manifest to give a page in the web context access to your system's geolocation devices (if your app has permission to access this functionality), as well as access to the clipboard, indexed db, appcache and the ability to download files.

  • MSApp.addPublicLocalApplicationUri

Navigating from a web context page to a local context page is not allowed by default, but it can be enabled by using MSApp.addPublicLocalApplicationUri.  See the following sample code: 

//This must be called from the local context
MSApp.addPublicLocalApplicationUri("ms-appx:///localpage.html");

  • MSApp.execUnsafeLocalFunction

Automatic filtering prevents script injection into DOM elements. For example, setting innerHTML, outerHTML, document.write, DOMParser.parseFromString, etc are typical example of automatic filtering prevention.

If you really trust what you are bringing in, disables the safe HTML filtering for the specified function. You can create a function that inserts content that would normally be blocked and use MSApp.execUnsafeLocalFunction to execute that function.

var someElement = document.getElementById('someElementID');
MSApp.execUnsafeLocalFunction(
function() { someElement.innerHTML = '<div onclick="console.log(\"hi\");">hi</div>' }
);

  • setInnerHTMLUnsafe , setOuterHTMLUnsafe and insertAdjacentHTMLUnsafe

    Writes the specified HTML without using safe HTML filtering. (These functions are a part of the Windows Library for JavaScript.) If you really trust what you are bringing in, you can use WinJS.Utilities.setInnerHTMLUnsafe, setOuterHTMLUnsafe, and insertAdjacentHTMLUnsafe to serve as wrappers for calling DOM methods that would otherwise strip out risky content.

Tips and Best Practices

Organize Your Pages

  • If you have a lot of web assets files like image, audio, video, you can put them into a top level big ressources folder.  By putting the resources folder in the top level, one can access these assets easily from different page at different level.
  • For an application with multiple pages:
    • Youcan create a top level folder called pages and then inside this folder, each page can be another folder which contain it’s HTML, CSS and JavaScript files
    • It’s a good idea to have separate HTML, CSS and JavaScript files

Organizing Your code with WinJS.Namespace

By default, there is no namespace concept in JavaScript.  The WinJS.Namespace.define function creates a public namespace.  This function allows you to create your own namespace as a way of organizing your code. When you create a type or other element inside a namespace, you reference it from outside the namespace by using its qualified name: Namespace.Type. The following code shows how to create a Robotics namespace and define the Robot type inside it, and how to use the type outside the namespace:

WinJS.Namespace.define("Robotics", {
Robot: WinJS.Class.define( function(name) {
this.name = name;
},
{ modelName: "" },
{ harmsHumans: false, obeysOrders: true })
});
var myRobot = new Robotics.Robot("Mickey");

myRobot.modelName = "4500";
var harm = Robotics.Robot.harmsHumans;

You can add elements that are defined elsewhere to a namespace by defining a field and setting the field value to it. In the following code, the getAllRobots function that is defined outside the WinJS.Namespace.define function is added to the Robotics namespace:

function getAllRobots() {
// Get the total number of robots.
return _allRobots;
}
...
WinJS.Namespace.define("Robotics", {
getAllRobots: getAllRobots });

var _allRobots = [
new Robotics.Robot("mike"),
new Robotics.Robot("ellen")
];

Important If you define an element outside a namespace, and that element depends on the existence of the namespace (like the _allRobots array), you must place the declaration of the dependent element after the namespace definition. If you try to add the definition before the namespace definition, you get the error "namespace name is undefined."

You can add properties to a namespace multiple times if you need to. You might want to define a function in a different file from the one in which the rest of the namespace elements are defined. Or you might want to reuse a function in several namespaces.

Unique ID

In general, it’s always good practice to always use unique ID for each element across all HTML/CSS pages.  It is especially important to make sure each element has an unique ID across all HTML/CSS pages in Windows 8 platform due to the nature of Windows 8. 

Where to Attaching Event Handler

The recommended way to attach an event handler is to use JavaScript to retrieve the control, then use the addEventListener method to register the event. The question is, when should you retrieve the control? You could just add it anywhere to your JavaScript code, but then there's a chance it might get called before the control exists.

The answer for most HTML files is to provide a then or done function for the Promise returned by the WinJS.UI.processAll method.  If it’s for Page controls, you can use the ready function instead.

What's a Promise? To provide responsive user experience, many Windows Library for JavaScript and Windows Runtime functions execute asynchronously. That way your app can continue to respond to user interactions while performing work in the background. Instead of directly returning a value, an asynchronous function returns a Promise for a value. For more info about asynchronous programming, see Asynchronous programming in JavaScript.

In JavaScript

(function () {
"use strict";

    WinJS.Binding.optimizeBindingReferences = true;

    var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
WinJS.strictProcessing();

    app.onactivated = 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.
}
args.setPromise(WinJS.UI.processAll().done(function () {
var button1 = document.getElementById("button1");
button1.addEventListener("click", button1Click, false);
})
);
}
};

    app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// args.setPromise().
};

    // The click event handler for button1
function button1Click(mouseEvent) {
var button1Output = document.getElementById("button1Output");
button1Output.innerText =
mouseEvent.type
+ ": (" + mouseEvent.clientX + "," + mouseEvent.clientY + ")";

    }

    var namespacePublicMembers = { clickEventHandler: button1Click };
WinJS.Namespace.define("startPage", namespacePublicMembers);

    app.start();
})();

In HTML

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

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

    <!-- BasicAppExample references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
</head>
<body>
<button id="button1">An HTML button</button>
<p id="button1Output"></p>
</body>
</html>

Pages and Navigation

The Blank Application template works well for a very simple app, but when you write a more complex app, you'll probably want to divide your content into multiple files.

A traditional website might have a series of pages that you navigate between using hyperlinks. Each page has its own set of JavaScript functions and data, a new set of HTML to display, style info, and so on. This navigation model is known as multi-page navigation.

Unlike a traditional website, a Windows Store app using JavaScript works best when it uses the single-page navigation model. In this model, you use a single page for our app and load additional data into that page as needed.

That means that your app never navigates away from its default.html page. Always make the default.html and default.js files your app's start up page. They define the outmost UI for your app (such as the AppBar) and handle the application lifecyle.

If you never navigate away from default.html, how do you bring in content from other pages? There are a few different ways.

  • You can use HtmlControl to display HTML from another page that doesn't contain interactivity (HtmlControl doesn't support the loading of JavaScript).
  • You can create a PageControl and display it on your main page. A PageControl is set of HTML, JavaScript, and CSS that you can display inside another HTML page, much like you would another control. For more info about creating Page controls, see Adding Page controls.
  • You can use an iframe to display content from another page. This is the least efficient way to incorporate content form another page, but it's your only option if you want to display an external web page.
  • You can also use DOM methods to incorporate content from other pages.

For more info about navigation, see Supporting navigation.

In my next blog, I will show some examples on how to transform HTML5/CSS3 apps into Windows 8 app.