The Windows Library for JavaScript (WinJS) and Visual Studio templates for Windows Store apps give developers great flexibility when using third-party JavaScript libraries, including the popular jQuery. Many modern web developers use jQuery for quick, easy, and powerful DOM access and manipulation.
As we announced in April, you can use jQuery release 2.0 in a Windows Store app without getting errors related to the JavaScript runtime or dynamic content. In this post, I’ll show how you can apply jQuery to your Windows Store app. In addition, I’ll also look at some areas where WinJS provides compelling alternatives to specific jQuery APIs.
(If you want a refresher on the jQuery APIs, take a look at the jQuery documentation. You might also take a minute to remind yourself about how to use CSS selectors.)
Using jQuery in Windows Store apps
You need a local version of jQuery in your Window Store app, so you’ll need to download a copy of jQuery release 2.0 (or higher). After you have a copy of the jQuery library, add it to your JavaScript project in Visual Studio 2012 (right-click the ‘js’ folder in the project, click Add > Existing Item, and then select the jQuery library from where you saved it.)
(You can also get jQuery directly into your Visual Studio solution using NuGet.)
Next, you’ll need to reference the jQuery library in the HTML files for your app. Note that you’ll need to include a reference to jQuery on any HTML page that references a JavaScript file that uses jQuery. I recommend placing the reference in the starting page for the app (usually ‘default.html’), which ensures that you can use jQuery from any page loaded into (navigated from) that page.
<script src="/js/jquery-2.0.0.min.js"></script>
Basic DOM manipulation
The jQuery selector function (‘$’, also known simply as ‘the jQuery function’) comes in really handy when you need to access the DOM in an HTML document – including the markup that makes up the content of a Windows Store app. jQuery also provides a substantial library of APIs with various functions for manipulating HTML content and JavaScript data. From applying CSS styles to storing hidden data as JSON objects, jQuery has a wealth of tools you can use within your Windows Store app.
As an aside, if you just want the CSS selector function, you don’t need all of jQuery. Instead, you might try the Sizzle library: it’s free, open-source, and small. (In fact, the jQuery function was built upon Sizzle.) Sizzle also uses CSS selectors to return collections of HTML elements from the DOM. To use Sizzle, download a local copy of the library, add it to your project, and include a reference to the library in your HTML page (just like jQuery).
Now we can get down to business: using jQuery in a Windows Store app! The jQuery functions and techniques that you’ve become familiar with are available to your Windows Store app:
- You can get access to one or many elements in the app, with a fine degree of precision:
var appHeadings = $('h1');
var appContent = $('#app-content')[0];
var formFields = $('form input[type="text"]');
- You can add event handlers to one (or many) elements in the app:
$('.data').mouseover(function (event) {
// Some handler code.
});
$('#my-button').click(function (event) {
// Some handler code.
}); - You can get the value of a select element in the app:
var name = $("select option:selected").val();
- You can dynamically add HTML content to your app:
$("#my-list").append("<li>A new list item.</li>");
$("h1").html("My App Name"); - You can even dynamically style the HTML content in your app:
$(".legal").css("font-size", "12pt");
With the jQuery functions, you can write terser, yet more powerful code for your Windows Store app. You can spend more time building features rather than trying to manipulate the UI.
Best yet, you can use the APIs provided by WinJS along with jQuery to create more elegant solutions. WinJS provides access to specific Windows features that jQuery can’t access on its own. Together, the two libraries can enable fluid, responsive, and beautiful apps. We’ll take a look at two specific scenarios for using jQuery in a Windows Store app: building an AppBar for navigation and selecting/storing data from a ListView control.
Creating an app navigation bar
To build a WinJS.UI.AppBar in a Windows Store app, we would declare the AppBar in the app’s HTML like so, specifying top placement as is used for navigation:
<div id="appbar" data-win-control="WinJS.UI.AppBar"
data-win-options="{layout: 'commands', placement: 'top'}">
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id: 'back', icon: 'back'}"
value="/html/pageone.html">
</button>
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id: 'favorite', icon: 'favorite'}"
value="/html/pagetwo.html">
</button>
<button data-win-control="WinJS.UI.AppBarCommand"
data-win-options="{id: 'forward', icon: 'forward'}"
value="/html/pagethree.html">
</button>
</div>
Let’s assume that we want to define the behavior for the app bar as such: when a button is clicked, the app navigates to a specific page and the button that raised the event is disabled. All other buttons are enabled. Without jQuery, we might create this behavior with the following code, where we’re attaching the same handler to each command in the navigation bar:
// Add event handlers to all the buttons in the appbar.
// This function is called when the page loads.
function addAppbarNavigation() {
var appBar = document.getElementById('appbar');
WinJS.UI.process(appBar);
var appBarControl = appBar.winControl,
commandIds = ["back", "favorite", "forward"];
for (var i = 0; i < commandIds.length; i++) {
var command = appBarControl.getCommandById(commandIds[i]);
command.onclick = navigateFromAppBar;
}
}
// Navigate to the page specified by the button.
function navigateFromAppBar(event) {
var element = event.target;
for (var i = 0; i < element.parentNode.childNodes.length; i++) {
element.parentNode.childNodes[i].disabled = "";
}
element.parentNode.winControl.hide();
WinJS.Navigation.navigate(element.value);
element.disabled = 'disabled';
}
Now, with jQuery, we can create the same behavior, but with significantly less code.
// Add event handlers to all the buttons in the appbar.
// This function is called when the page loads.
function addAppbarNavigation() {
$('#appbar button').click(navigateFromAppBar);
}
// Navigate to the page specified by the button.
function navigateFromAppBar(event) {
var element = event.target;
$('#' + element.parentNode.id + ' button').removeAttr('disabled');
element.parentNode.winControl.hide();
WinJS.Navigation.navigate(element.value);
element.disabled = 'disabled';
}
Building on top of a ListView control
jQuery can also be a big help when used along with a WinJS.UI.ListView control. Imagine that your app displays data about a group of items for sale, where each item has an image and some info associated with it.
var items = [
{
name: 'item1',
description: 'item1 description',
itemPrice: 1.99,
quantity: 20,
pictureSRC: '/images/item1.jpg'
},
{
/* Next item details. */
}
];
Given the format of our data, we can define a template to display the array of items.
<div id="smallListTextTemplate"
data-win-control="WinJS.Binding.Template" >
<div>
<h2 data-win-bind="innerText: name"></h2>
<img src="#" data-win-bind="src: pictureSRC" />
</div>
</div>
<div id="item-view">
Loading items …
</div>
We can then create a WinJS.UI.ListView control that is bound to our data source when the page loads using the following code:
// Populate the 'item-view' as a ListView control with data from
// the 'items' array. This function is called when the page loads.
function createItemList(page) {
var currentItems = new WinJS.Binding.List(items),
listDiv = $('#item-view')[0];
var listView = new WinJS.UI.ListView(listDiv, {
itemDataSource: currentItems.dataSource,
itemTemplate: smallListTextTemplate,
oniteminvoked: getItem,
selectionMode: 'single',
tapBehavior: 'directSelect',
swipeBehavior: 'none',
layout: {type: WinJS.UI.GridLayout }
});
WinJS.UI.processAll(page);
}
So far, our app populates the ListView with the name and picture of each item in the items list when the page loads. But what if we want to see the cost or available quantity of an item? We might create a “details view” for our app to display more info about an item. First, we define an ‘item-details’ div of our app to show additional info about the item when it is selected in the ListView.
<div id="item-details">
<h2>Item details</h2>
<div id="item-picture"></div>
<div id="item-name"></div>
<div id="item-info">
<p>Cost per unit: $<span id="item-cost"> USD</span></p>
<p>Available quantity: <span id="item-quantity"></span></p>
</div>
</div>
When the user selects an item in the ListView, we want the app to display details about the item in the Item details section. We need to get the selected item from the ListView with the following code:
// Get the data for the selected item in the list view.
function getItem(evt) {
var index = evt.detail.itemIndex,
data = $("#item-view")[0].winControl.itemDataSource;
data.itemFromIndex(index).done(
function (result) {
showItemDetails(result.data);
});
}
Here’s where jQuery comes in really handy. To display the data from the item in the Item details section, we only need to use the following small function:
// Display info about the item in the 'item-details' div.
function showItemDetails(item) {
$("#item-name").html(item.name);
$("#item-cost").html(item.itemPrice);
$("#item-quantity").html(item.quantity);
$("#item-details").data(item);
var img = new Image();
img.src = item.pictureSRC;
$("#item-picture").html(img);
}
The item info, taken from the data source, is then displayed in the Item details section along with its picture.
In the previous code snippet, you’ll notice that the raw data about the item is stored in the ‘item-details’ div using the jQuery data function. The data function gets or sets arbitrary data in the element(s) returned by the selector function. The data is essentially hidden from the layout of the page, meaning that you can store hidden metadata without it affecting the design of the page. This can be really helpful in scenarios where you want to store a small amount of hidden data but don’t want to store it across sessions or to instantiate a ListView control to display a set of data.
Using the previous example, if we wanted our app to add the item displayed in the Item details section to a checkout cart, we wouldn’t need to access the original data source again – we would only need to access the data associated with the ‘item-details’ element.
// Add the item shown in the Item details to the checkout cart.
function addToCart() {
var itemToAdd = $("#item-details").data();
$("#checkout-cart").append(
"<div><h2>" + itemToAdd.name + "</h2>" +
"<p>Price: $" + itemToAdd.itemPrice + "</p>" +
"</div>"
);
}
Promises
Both jQuery and WinJS provide their own implementation of the Promises pattern. Some jQuery functions return a promise object by default (functions like animate and get – more on those later). Other functions can be wrapped in a promise by calling the promise function on the results of the function. When you do this, the promise returns immediately with a resolved promise.
// The code in the done method that follows executes immediately.
$("div").promise().done(function (result) { });
In some circumstances, you might want to chain WinJS and jQuery promises together. Luckily, the two implementations are interoperable. jQuery promises implement the same then and done methods that WinJS uses.
In this next (somewhat silly) example, I’ve mixed jQuery and WinJS promises together using both promise chaining and the WinJS.Promise.join method. This code gets data stored in the app using the jQuery get function, creates a temporary file, writes the data from the app to the file, reads the file, and then displays the data from the file on the screen.
var tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder,
promiseArray = [],
filename = "tempfile.txt";
promiseArray.push(tempFolder.createFileAsync(filename,
Windows.Storage.CreationCollisionOption.replaceExisting));
promiseArray.push($.get("/data/data.txt"));
WinJS.Promise.join(promiseArray).
then(function (response) {
var file = response[0],
data = response[1],
memoryStream =
new Windows.Storage.Streams.InMemoryRandomAccessStream(),
dataWriter =
new Windows.Storage.Streams.DataWriter(memoryStream);
dataWriter.writeString(data);
var buffer = dataWriter.detachBuffer();
dataWriter.close();
return Windows.Storage.FileIO.writeBufferAsync(file, buffer);
}).
then(function () {
return tempFolder.getFileAsync(filename);
}).
then(function (file) {
return Windows.Storage.FileIO.readTextAsync(file);
}).
then(function (data) {
return $("#scenario").text(data).promise();
}).
done(function () {
$("#scenario").append("<br/><br/>Finished!");
});
Use the tools best suited for the job
Although jQuery adds a powerful complement to the APIs in Windows, it’s good to keep in mind that WinJS already provides a wealth of tools you can use. The WinJS APIs are optimized for use with Windows and are designed around app-specific scenarios (whereas jQuery is platform agnostic). If you find yourself considering whether to use jQuery or WinJS for a particular task, it’s a good idea to defer to the WinJS APIs.
Here are a handful of scenarios where WinJS already supplies the best ‘tools’ for a job.
Animating elements
jQuery includes a group of functions for animating DOM content, many of which functions are based on its animate function. However, the animations in WinJS are specially designed to make efficient use of machine resources by offloading the animation workload from the CPU to the GPU (for animations that affect transform and opacity). As a result, the WinJS animations perform well and don’t block the UI thread.
WinJS includes animations that are equivalent to the animations provided by jQuery, animations that match the design and look of Windows. Take the jQuery fadeIn and fadeOut functions, which make an HTML element fade into / out of view and change the display value of the element’s style. WinJS has the WinJS.UI.Animation.fadeIn and WinJS.UI.Animation.fadeOut functions, which also smoothly fade an HTML element into and out of view, but without changing the display value.
// Animating content into view with jQuery.
$("#content").fadeIn("slow");
// Animating content into view with WinJS and jQuery.
WinJS.UI.Animation.fadeIn($("#content")[0]);
// Animating content out of view with jQuery.
$("#content").fadeOut("fast");
// Animating content out of view with WinJS and jQuery.
WinJS.UI.Animation.fadeOut($("#content")[0]);
In the above sample, note that the WinJS effects only change the opacity of the affected elements. The jQuery animations affect the opacity and the display value, which lead to a re-layout of the page.
Compare some other jQuery animations with similar animations in WinJS:
jQuery animation |
WinJS |
slideUp |
|
slideDown |
|
show |
WinJS.UI.Animation.enterContent function, WinJS.UI.Animation.createExpandAnimation function |
hide |
WinJS.UI.Animation.exitContent function, WinJS.UI.Animation.createCollapseAnimation function |
The WinJS.UI.Animation namespace contains additional functions for animating app content, including dragging items, animating button clicks, adding and removing items from lists, and much more – all designed to integrate perfectly with Windows.
Plus, many animations built into Windows Store apps can be applied using CSS3. The following code combines jQuery with the new CSS3 animation styles to create a “flip” effect on the affected element.
// Flip the 'box' element along its y-axis.
$('#box').css({
"-ms-transition-property": "-ms-transform",
"-ms-transition-duration": "1s",
"-ms-transition-timing-function": "ease-in",
"-ms-transform": "rotateY(-180deg)",
"-ms-transform-origin": "50% 50%"
});
Checking that the DOM has loaded
When building a normal web application, you need to know when the DOM has completed loading before you can begin manipulating any elements that it contains. This means using the ready function in jQuery to add a handler to the event raised when the DOM is fully loaded.
// Check to see if the webpage has finished loading and is ready.
$(document).ready(function() {
// Add set up code here.
})
However, the jQuery ready function is largely unnecessary in a Windows Store app given the WinJS.Application.onready event and IPageControl.ready method. The IPageControl.ready method is raised when the DOM for a page / element has been fully loaded into the app. You then add your initialization code for the page to the handler function for the IPageControl.ready method.
// When the page is ready to be used, run setup code.
WinJS.UI.Pages.define("pageone.html", {
ready: function (element, options) {
// Add set up code here.
}
});
Accessing web resources
You may be familiar with using the load, get, or ajax functions in jQuery to access remote resources on the web. However, you’ll find that the best way to get web resources from a Windows Store app is the WinJS.xhr function. The WinJS.xhr wraps the standard XMLHttpRequest object in a Promise object.
(Note that in Windows 8.1 there’s a new Windows.Web.Http.HttpClient API that’s simple to use and yet more powerful than WinJS.xhr. It’s the recommended API for HTTP requests for apps targeting Windows 8.1.)
For example, the WinJS.xhr function can easily retrieve the RSS feed from this blog:
var url = "http://blogs.msdn.com/b/windowsappdev/rss.aspx";
// Request the RSS feed from the blog.
WinJS.xhr({ url: url }).done(
function (result) {
$("#output").text(result.responseText);
});
With the WinJS.xhr function, we can parse the response from the web resource just like a normal XMLHttpRequest.
Navigating between pages
Navigating from one page to another is a key design feature for most apps. With jQuery, one might create a multi-page navigation in using the load function. For instance, you can inject the HTML from the body element of one page into a host element contained in another page using the following code:
// Display the page navigated to in the content host.
$("#content-host").load("/html/pageone.html");
The WinJS library contains a Navigation namespace that, when combined with the APIs included in the WinJS.UI.Pages namespace, provides robust multi-page navigation. In the following example, a new page is displayed inside of an element with the ID value ‘content-host’.
// Navigate to a new page.
WinJS.Navigation.navigate("/html/pageone.html");
// Other code …
// After the app has navigated to a new page, display the contents
// of the new page in the 'content-host' div.
WinJS.Navigation.addEventListener("navigated", function (eventObject) {
var url = eventObject.detail.location,
host = $("#content-host")[0];
host.winControl && host.winControl.unload && host.winControl.unload();
WinJS.Utilities.empty(host);
eventObject.detail.setPromise(
WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {
WinJS.Application.sessionState.lastUrl = url;
WinJS.UI.Animation.enterPage(host);
WinJS.UI.processAll();
}));
});
The code sample calls WinJS.Navigation.navigate function to navigate to the new URL. It then adds a handler to the ‘navigated’ event to display the opened page within the content host. Before the new page is displayed, the IPageControl.unload function is called on the host and the element is emptied. Note how the sessionState for the app is updated after the navigation and a nice little animation is run when the new page enters.
(Some of the templates in Visual Studio also include the navigator.js file, which provides additional navigation utilities for multi-page apps.)
Selecting single elements
In Windows Store apps, you can reference an HTML element that has a unique ID attribute by its ID. Thus, you don’t particularly need the selector function in jQuery in situations when you need to select a single element with a known ID. (You see similar behavior in the code-behind files of Windows Store apps built with C# and XAML.)
For example, take a simple HTML span element with a valid ID attribute on a Windows Store app page:
<span id='mySpan'></span>
In a JavaScript file referenced by the app page, the span can be referenced directly by its ID, without needing to declare a new variable:
// We can reference the span directly using its ID as a variable name.
// The following statement evaluates to 'true'.
mySpan.tagName == 'SPAN';
You don’t need to capture a reference to an element with a unique ID because it’s already provided to you. This works only if the ID value of the element is allowed as a variable name in JavaScript: ‘mySpan’ works but ‘my-span’ doesn’t.
Moreover, you can use this trait of Windows Store apps in conjunction with jQuery. Because the variable references the element as an HTMLElement object, you can pass in the variable directly to the jQuery selector function without using a CSS selector expression.
$(mySpan).css('background-color', 'rgba(40, 200, 40, 1)');
Better together
As we’ve seen, jQuery complements the tools available in WinJS very well. With jQuery, you can select multiple HTML elements from your app’s markup with greater ease and finer control. The functions in jQuery can turbocharge the WinJS code you use to manipulate your app. With the two, you can build a dynamic and elegant Windows Store app with less code, meaning less time to develop (and less time to publish to the Store).
Thank you for reading and keep on developing apps!
Eric Schmidt, Content Developer 2, Windows Developer Content