Metro: Query Selectors

The goal of this blog entry is to explain how to perform queries using selectors when using the WinJS library. In particular, you learn how to use the WinJS.Utilities.query() method and the QueryCollection class to retrieve and modify the elements of an HTML document.

Introduction to Selectors

When you are building a Web application, you need some way of easily retrieving elements from an HTML document. For example, you might want to retrieve all of the input elements which have a certain class. Or, you might want to retrieve the one and only element with an id of favoriteColor.

The standard way of retrieving elements from an HTML document is by using a selector. Anyone who has ever created a Cascading Style Sheet has already used selectors. You use selectors in Cascading Style Sheets to apply formatting rules to elements in a document.

For example, the following Cascading Style Sheet rule changes the background color of every INPUT element with a class of .required in a document to the color red:

input.red { background-color: red }

The “input.red” part is the selector which matches all INPUT elements with a class of red.

The W3C standard for selectors (technically, their recommendation) is entitled “Selectors Level 3” and the standard is located here:

http://www.w3.org/TR/css3-selectors/

Selectors are not only useful for adding formatting to the elements of a document. Selectors are also useful when you need to apply behavior to the elements of a document. For example, you might want to select a particular BUTTON element with a selector and add a click handler to the element so that something happens whenever you click the button.

Selectors are not specific to Cascading Style Sheets. You can use selectors in your JavaScript code to retrieve elements from an HTML document.

jQuery is famous for its support for selectors. Using jQuery, you can use a selector to retrieve matching elements from a document and modify the elements. The WinJS library enables you to perform the same types of queries as jQuery using the W3C selector syntax.

Performing Queries with the WinJS.Utilities.query() Method

When using the WinJS library, you perform a query using a selector by using the WinJS.Utilities.query() method. 

The following HTML document contains a BUTTON and a DIV element:

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

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

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


    <button>Click Me!</button>

    <div style="display:none">

        <h1>Secret Message</h1>

    </div>


</body>
</html>

The document contains a reference to the following JavaScript file named jsdefault.js:

(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {

            WinJS.Utilities.query("button").listen("click", function () {
                WinJS.Utilities.query("div").clearStyle("display");
            });

        }
    };

    app.start();
})();




The default.js script uses the WinJS.Utilities.query() method to retrieve all of the BUTTON elements in the page. The listen() method is used to wire an event handler to the BUTTON click event. When you click the BUTTON, the secret message contained in the hidden DIV element is displayed. The clearStyle() method is used to remove the display:none style attribute from the DIV element.

clip_image001

Under the covers, the WinJS.Utilities.query() method uses the standard querySelectorAll() method. This means that you can use any selector which is compatible with the querySelectorAll() method when using the WinJS.Utilities.query() method. The querySelectorAll() method is defined in the W3C Selectors API Level 1 standard located here:

http://www.w3.org/TR/selectors-api/

Unlike the querySelectorAll() method, the WinJS.Utilities.query() method returns a QueryCollection. We talk about the methods of the QueryCollection class below.

Retrieving a Single Element with the WinJS.Utilities.id() Method

If you want to retrieve a single element from a document, instead of matching a set of elements, then you can use the WinJS.Utilities.id() method. For example, the following line of code changes the background color of an element to the color red:

WinJS.Utilities.id("message").setStyle("background-color", "red");

The statement above matches the one and only element with an Id of message. For example, the statement matches the following DIV element:

<div id="message">Hello!</div>

Notice that you do not use a hash when matching a single element with the WinJS.Utilities.id() method. You would need to use a hash when using the WinJS.Utilities.query() method to do the same thing like this:

WinJS.Utilities.query("#message").setStyle("background-color", "red");

Under the covers, the WinJS.Utilities.id() method calls the standard document.getElementById() method. The WinJS.Utilities.id() method returns the result as a QueryCollection.

If no element matches the identifier passed to WinJS.Utilities.id() then you do not get an error. Instead, you get a QueryCollection with no elements (length=0).

Using the WinJS.Utilities.children() method

The WinJS.Utilities.children() method enables you to retrieve a QueryCollection which contains all of the children of a DOM element. For example, imagine that you have a DIV element which contains children DIV elements like this:

<div id="discussContainer">

   <div>Message 1</div>

   <div>Message 2</div>

   <div>Message 3</div>

</div>


You can use the following code to add borders around all of the child DIV element and not the container DIV element:

var discussContainer = WinJS.Utilities.id("discussContainer").get(0);

WinJS.Utilities.children(discussContainer).setStyle("border", "2px dashed red");

 

clip_image002

It is important to understand that the WinJS.Utilities.children() method only works with a DOM element and not a QueryCollection. Notice that the get() method is used to retrieve the DOM element which represents the discussContainer.

Working with the QueryCollection Class

Both the WinJS.Utilities.query() method and the WinJS.Utilities.id() method return an instance of the QueryCollection class. The QueryCollection class derives from the base JavaScript Array class and adds several useful methods for working with HTML elements:

  • addClass(name) – Adds a class to every element in the QueryCollection.
  • clearStyle(name) – Removes a style from every element in the QueryCollection.
  • conrols(ctor, options) – Enables you to create controls.
  • get(index) – Retrieves the element from the QueryCollection at the specified index.
  • getAttribute(name) – Retrieves the value of an attribute for the first element in the QueryCollection.
  • hasClass(name) – Returns true if the first element in the QueryCollection has a certain class.
  • include(items) – Includes a collection of items in the QueryCollection.
  • listen(eventType, listener, capture) – Adds an event listener to every element in the QueryCollection.
  • query(query) – Performs an additional query on the QueryCollection and returns a new QueryCollection.
  • removeClass(name) – Removes a class from the every element in the QueryCollection.
  • removeEventListener(eventType, listener, capture) – Removes an event listener from every element in the QueryCollection.
  • setAttribute(name, value) – Adds an attribute to every element in the QueryCollection.
  • setStyle(name, value) – Adds a style attribute to every element in the QueryCollection.
  • template(templateElement, data, renderDonePromiseContract) – Renders a template using the supplied data. 
  • toggleClass(name) – Toggles the specified class for every element in the QueryCollection.

Because the QueryCollection class derives from the base Array class, it also contains all of the standard Array methods like forEach() and slice().

Summary

In this blog post, I’ve described how you can perform queries using selectors within a Windows Metro Style application written with JavaScript. You learned how to return an instance of the QueryCollection class by using the WinJS.Utilities.query(), WinJS.Utilities.id(), and WinJS.Utilities.children() methods. You also learned about the methods of the QueryCollection class.

Discussion

  1. Stilgar says:

    This begs the question… Why not use jQuery?

    • @Stilgar – Selectors were made famous by jQuery but, at this point, you should think of selectors as a standard part of JavaScript. In the WinJS library when you use the query() method, Microsoft is just wrapping a call to the standard querySelectorAll() method.

      You can use jQuery side-by-side with the WinJS library. So there is nothing wrong with using jQuery selectors when writing code for WinJS.

      However, why bother? If the only functionality that you want from jQuery is support for selectors then you might as well use WinJS selectors.

  2. jgauffin says:

    > However, why bother? If the only functionality that you want from jQuery is support for selectors then you might as well use WinJS selectors.

    I would argue the opposite. jQuery is de facto standard today. Introducing WinJS would just force the developers to learn something new again that doesn’t really add any value.

    Sure, the api is a lot nicer than jQuerys if you are used to windows programming. But still. jQuery got so much more than just the selectors.