Continuing from part 1 of this series, the next few methods in WinJS to look at are WinJS.Utilities.data and WinJS.Utilities.convertToPixels.

WinJS.Utilities.data is documented as “gets a data value associated with the specific element.” The data value is always an object and is attached to the element using a property name of _msDataKey. So WinJS.Utilities.data(<element>) always just gives you back that object, or creates one if one doesn’t yet exist. You can then add properties to that object, or retrieve them. Basically this is a tidy way to attach extra data to an arbitrary element knowing that you won’t interfere with the element otherwise. WinJS uses this internally in various places.

WinJS.Utilities.convertToPixels sounds fancier than it is. It’s just a helper to convert a CSS positioning string for an element to a real number. That is, in CSS you often use values suffixes like “px” and “em” and so on that, of course, aren’t values that are meaningful in any computations. This function converts those values to a meaningful number of pixels. With “px” values, or something without suffixes, it’s easy–just pass it to parseInt which will strip “px” automatically if it’s there. For other CSS values–basically anything that starts with a numerical value, what this function does it just assign the value to the elements left property (saving the prior value so nothing gets altered), then reads back the elements pixelLeft property. In other words, it lets the DOM engine handle the conversion which will produce 0 if the value isn’t convertible.

Along these same lines are the following WinJS.Utilities methods:

  • getRelativeLeft: gets the left coordinate of an element relative to a specified parent. Note that the parent doesn’t have to be the immediate parent, but can be any other node in the element’s tree. This function then basically takes the offsetLeft property of the element and keeps subtracting off the offsetLeft of the next element up until the designated ancestor is reached.
  • getRelativeTop: does the same thing as getRelativeLeft except with offsetTop.
  • getContentWidth: returns the offsetWIdth of an element minus the values of borderLeftWidth, borderRightWidth, paddingLeft, and paddingRight, which results in the actual width of the area where content is shown.
  • getTotalWidth: returns the offsetWIdth of the element plus the marginLeft and marginRight values.
  • getContentHeight: returns the offsetHeight of an element minus the values of borderTopWidth, borderBottomWidth, paddingTop, and paddingBottom, which results in the actual height of the area where content is shown.
  • getTotalHeight: returns the offsetHeight of the element plus the marginTop and marginBottom values.
  • getPosition: returns an object with properties of left, top, width, and height properties of an element relative to the topmost element in the tree (up to document or body), taking scroll postions into account.

Again, it’s helpful to take a look in base.js for the implementation of these functions so you can see what they’re doing, and appreciate the work they’ll save you!


In this and the next few posts I want to bring up a few of the more obscure features of APIs of WinJS and give a little more explanation about them, or at least just make you aware that they exist!

The first ones in these posts are simple wrappers around a few common DOM operations.

WinJS.Utilities.QueryCollection is a class that wraps an element.querySelectorAll or document.querySelectorAll with a number of useful methods. An instance of this class is created by calling WinJS.Utilities.query(<query>[, ]) where <query> is a usual DOM query string and the optional <element> scopes the query. That is, if you provide <element>, the instance wraps element.querySelectorAll(<query>); if you omit <element>, the instance uses document.querySelectorAll(<query>). Similarly, WinJS.Utilities.id(<id>) does a document.getElementById(<id>), then passes the result to new WinJS.Utilities.QueryCollection. WinJS.Utilities.children creates a QueryCollection that contains children of a specified element.

Anyway, once you have a QueryCollection instance, it provides methods to work with its collection, that is, with the results of the DOM query that in and of itself is just an array. As such, you’d normally be writing plenty of loops and iterators to work with the items in the array, and that’s exactly what QueryCollection provides as a convenience. It follows along with other parts of WinJS, which are utilities that most developers end up writing anyway, so it might as well be in a library!

We can see this in what the individual methods do:

  • forEach: calls Array.prototype.forEach.apply on the collection using the given callback function and this argument.
  • get: returns [] from the array
  • setAttribute: iterates the collection and calls setAttribute for each item.
  • getAttribute: gets an attribute for the first item in the collection.
  • addClass, hasClass, removeClass, toggleClass: iterates the collection and calls WinJS.Utilities.addClass, hasClass, removeClass, or toggleClass for each item, respectively.
  • listen, removeEventListener: iterates the collection calling addEventListener or removeEventListener for each item
  • setStyle, clearStyle: iterates the collection setting a given style to a value or “”, respectively.
  • include: adds other items to this collection. The items can be in an array, a document fragment (DOM node), or a single item.
  • query: executes a querySelectorAll on each item in the collection and calls include for each set of results. This could be used, for instance, to extract specific children of the items in the collection and add them to the collection.
  • control: given the name of a control constructor (a function) and an options object (as WinJS controls typically use), creates an instance of that control and attaches it to each item in the collection. It’s allowable to call this method with just an options object as the first argument, in which case the method calls WinJS.UI.process on each item in the collection followed by WinJS.UI.setOptions for each control therein. This allows the collection to basically contain elements that have WinJS control declarations (data-win-control attributes) that have not yet been instantiated.
  • template: renders a template element that is bound to the given data and parented to the elements included in the collection. If the collection contains multiple elements, the template is rendered multiple times, once at each element per item of data passed.

As for WinJS.Utilities.addClass, WinJS.Utilities.removeClass, WinJS.Utilities.hasClass, and WinJS.Utilities.toggleClass, as used above, these are helper functions that simply add one or more classes (space delimited) to an element, removes a single class from an element, checks if an element has a class, and adds or removes a class from an element depending on whether it’s already applied. While these operations sound simple, the implementation is actually quite non-trivial. Take a look at the WinJS source code (in base.js) to see what I mean! Good code that you don’t have to write.

 


When using WinJS page controls to implement pages and navigation in an app, it's important to understand that any and all CSS that is loaded on behalf of those pages is cumulative. Because page controls are just performing DOM replacement within the context of the app's root page, you get the benefit of preserving the global JavaScript context but at the same time get the sometimes-frustrating behavior of preserving all the CSS styles as well.

For example, let's say the app's root page is default.html and its global styles are in css/default.css. It then has several page controls defined in pages/page1 (page1.html. page1.js, page1.css), pages/page2 (page2.html. page2.js, page2.css), and pages/page1 (page3.html. page3.js, page3.css). Let's say that page1 is the 'home' page and is loaded into default.html at startup. This means that the styles in default.css and page1.css have been loaded.

Now the user navigates to page2. This causes page1.html to be dumped from the DOM, but its styles remain in the stylesheet. So when page2 is loaded, page2.css gets added to the overall stylesheet as well. This means that any styles in page2.css that have identical selectors to page1.css will overwrite the latter's. The same thing happens also when the user navigates to page3–the styles in page3.css get loaded and overwrite those that already exist.

The tricky part is what then happens if the user navigates back to page1. Because the apphost's rendering engine has already loaded page1.css into the stylesheet, page1.css won't be loaded again. This means that any styles that were overwritten by other pages' stylesheets will not be reset to those in page1.css–basically you get whichever ones were loaded most recently.

The same thing happens with .js files, by the way, as those are not reloaded if they've been loaded already. The lesson here is to not duplicate variable names between those files.

On that thought, there are two ways to get around the CSS issue. One is to just use a global stylesheet (i.e. default.css) and not do much in the individual page .css files unless you're certain it's local. The second way is to make sure that each page has a top-level div with a page-name class, e.g. <div class="page1">, which allows you to then scope all style rules in page1.css with the page name, e.g.

.page1 p {
    font-weight: bold;
}

This will prevent collision between all the page<n>.css files.

Another place where this can arise is if you try to use the ui-light.css and ui-dark.css WinJS stylesheets in different pages of the same app. Because there are only two files here, whichever one is used first will be overwritten by the second, with the effect that subsequent pages that refer to ui-light.css will still appear with the dark styles.

The solution to this particular problem comes from the fact that where the styles differ, those in ui-light.css are scoped with a CSS class win-ui-light and those in ui-dark.css are scoped with win-ui-dark. This means you can just refer to whichever stylesheet you use most often in your .html files, then add either win-ui-light or win-ui-dark to individual elements that you need to style differently. (Thanks to Scott Salam for this one.)

The other way of avoiding collisions is to specifically unload and reload CSS files by modifying <link> tags in the page header. You can either remove one <link> tag and add a different one, toggle the disabled attribute for a tag between true and false, or change the href attribute of an existing link. These methods are demonstrated for styling an iframe in the CSS styling and branding your app sample, which swaps out and enables/disables both WinJS and app-specific stylesheets. Another demonstration for switching between the WinJS stylesheets is in scenario 1 of the HTML NavBar control sample  (js/1-CreateNavBar.js):

function switchStyle() {
    var linkEl = document.querySelector('link');
    if (linkEl.getAttribute('href') === "//Microsoft.WinJS.2.0 /css/ui-light.css") {
        linkEl.setAttribute('href', "//Microsoft.WinJS.2.0 /css/ui-dark.css");
    } else {
        linkEl.setAttribute('href', "//Microsoft.WinJS.2.0 /css/ui-light.css"); 
    }
}

The downside of this approach is that every switch means reloading and reparsing the CSS files and a corresponding re-rendering of the page. This isn’t much of an issue during page navigation, but given the size of the WinJS files I recommend using it only for your own page-specific stylesheets and using the win-ui-light and win-ui-dark classes to toggle the WinJS styles.


Developers sometimes ask about handling devices with different screen sizes, and whether that was something you’d could do with media queries in addition to the media queries for the different view states (snapped, filled, fullscreen-landscape, and fullscreen-portrait).

The answer is really to use adaptive (aka responsive) layout techniques with CSS so that you don’t really worry about screen size—you’re typically only writing media queries for the view states, since the relative positioning of items in the page layout can change quite a bit between them. In those view state media queries, you might be changing the size of an element that contains a grid layout div, or that of a ListView, or some other control, where that size change will trigger a resize event that a control like ListView picks up to refresh its layout. Apps also use the same event to change the layout of a ListView according to view state, such as switching between GridLayout (2D) and ListLayout (1D, vertical).

It’s worth noting that inside media queries there are the vh and vw units (viewport height and viewport width) that can be used like px, em, etc. That is, saying height: 6.5vh in CSS will give you 6.5% of the height of the app window. You can certainly accomplish the same thing with a CSS grid, where you know that certain fractional rows/columns will essentially come out the same thing. For example, if you have a grid that’s styled to 100% width and height, and use –ms-grid-columns: 1fr 1fr 1fr 1fr; that’d be the same as using –ms-grid-columns: 25vw 25vw 25vw 25vw. Or –ms-grid-columns: 25% 25% 25% 25%. But if that element was only 50% width, then the 1fr and 25% values will scale, but the 25vw would not, unless the display width itself changed.

I mention those because if you have layout that’s not strictly proportional and does depend on screen dimensions, you can use vh and vw like this, or you can standard media queries against width and height, combined with the view states, as needed. For example, say in snap view you’ll have your base layout for a 768 vertical dimension, but if you’re on something greater than, say, 1200, you might want to include another piece of information in the header or such. In that case, the extra element would be present in the HTML, then you’d have a media query like this:

@media screenand(-ms-view-state: snapped) and (max-height: “1199px”) {
/**/
}

wherein you might set display: none for that extra element.

Within JavaScript you can also obtain window and element dimensions as you need them if you’re working with layout programmatically (such as cases where you need to do some extra computation).

So it really depends on what you need to do, layout-wise, but the platform can provide all the information you need both declaratively and programmatically. You can also use Media Query Listeners (another W3C standard) to get events for view state changes.


I’ve seen this come up a number of times recently on the MSDN forums–apps that are using what appears to be a valid relative URI in the app package, like images/someImage.png, but that URI fails to work as expected. This happens from within HTML, CSS, and JavaScript.

Most often this error is caused by using such a reference from inside a WinJS page control, where the context for relative URIs is the page HTML file itself, not the default.html page that into which it’s loaded. That is, from a default.html page in the package root (or a CSS file on that level), a relative URI to a folder that’s exists on the same level in the package will work. However, page controls are typically stored in subfolders, like pages/home/home.html (and .css), in which case a relative URI would start at pages/home and look there.

In such cases, you could go up to parent folders, e.g. ../../images/someImage.png where the first two ..’s would go up from pages/home to the package root. This is somewhat fragile, however, and would break if you ever relocated the page control’s html tile in your project.

The best solution in such cases is to be sure to prefix the URI with a /, as in /images/someImage.png. The leading / means “package root” and properly references resources within the app package. This makes the URI an absolute URI in relation to the package root, and is effectively a shortcut for the true absolute URI form: ms-appx:/// or ms-appx://<package_id>/ (which says the package_id is optional). The leading / is thus the same as prefixing URIs with ms-appx:/// (the triple-slash version).

When in doubt, then, if some URI just isn’t working, especially with page controls, try preceding the URIs with a /.