Wednesday, May 28, 2014

An Introduction to Cross-Platform, Cross-Browser WinJs

In case you missed the big announcement, Microsoft has open sourced their Windows Library for JavaScript, otherwise known as WinJS. Many of you may not have noticed this because, like me, you assumed WinJS was just a shim for accessing the Windows Runtime from JavaScript. If this blog were one year older, that would be true, but it’s no longer the case. Although the library was initially built to create Windows 8 experiences, the team has worked hard to expand those experiences to the Windows Phone and finally to the browser itself. That means it provides truly cross-platform, cross-browser experiences. You can build a web-based app using the library and literally run it on your Android browser. It’s that simple.

I covered some of the highlights in my re//Build WinJS Windows Store app that I wrote as a slide presentation using HTML and JavaScript. That app was specifically designed to run on Windows 8.1 so I did not adapt it to the web. To make a web-based example, I had to pull down the WinJS library from GitHub (the instructions to pull it down and build it are on the main page). It was a little strange pulling a Microsoft project via git and then compiling it via grunt, but there you go. Times are changing! I copied the build over to a new directory and got to work on a very simple example I creatively named Jeremy’s Books. You can play with the example online or inline. You should be able to scroll and tap/touch your way through the list (tap on the link to open it). Be sure to refresh so you can see the animations.

There are really six things I’m showing here:

  1. How to scaffold a basic WinJS application
  2. Data-binding via templates
  3. Using the WinJS provided stylesheets
  4. Page entrance animations
  5. Tap animations
  6. The ListView control

For a more comprehensive walkthrough, you can check out all of the controls and experiment online at the Try WinJS site.

The basic pattern for wiring up the app looks like this (find it in the app.js source file)

WinJS.Application.onready = function () {
    WinJS.UI.processAll();
};
WinJS.Application.start();

The call to process all is what triggers parsing the declarative markup and instantiating the actual controls. After I’ve processed the controls, I trigger a page enter animation to animate the elements in from the right. When the animation finishes, I find the controls that were generated as the result of data-binding to wire in click handlers for a tap effect (tap on a book cover and you’ll see it respond).

WinJS.UI.Animation.enterPage(document.getElementById('mainDiv'), null).then(function () {
    var controls = document.getElementsByClassName("book"), i;
    for (i = 0; i < controls.length; i+=1) {
        addPointerDownHandlers(controls[i]);
        addPointerUpHandlers(controls[i]);
    }
});

Each element gets multiple handlers for different types of input:

function addPointerUpHandlers(target) {
    target.addEventListener("pointerup", onPointerUp, false);
    target.addEventListener("touchend", onPointerUp, false);
    target.addEventListener("mouseup", onPointerUp, false);
}

And the corresponding animation is triggered:

function onPointerUp(evt) {
    WinJS.UI.Animation.pointerUp(evt.target);
    evt.preventDefault();
}

For the list of books, I create a simple JSON array and then bind it to a list. I use the WinJS convention of assigning it to a namespace to make it easy to reference from markup:

WinJS.Namespace.define("Book.ListView", {
    data: new WinJS.Binding.List(books)
});

Now that it’s wired up, let’s take a look at the HTML. The basic HTML simply includes the base for WinJS and the UI as well as several stylesheets. I’m using the “light” theme but you can swap that to the dark theme to see how it changes (I prefer dark but have grown accustomed to light themes for presenting). I use a special data-win-control attribute to define the item template:

<div id="bookTemplate" data-win-control="WinJS.Binding.Template" style="display: none">
    <div class="book">
        <img src="#" data-win-bind="src: img; alt: title"/>
        <h4><a href="#" target="_blank"
 data-win-bind="href: link; textContent: title"></a></h4>
    </div>
</
div>

Notice in the image tag that the attributes are not bound directly. Instead, the binding attribute is used to provide a list of attributes on the parent element and the properties to bind their values to (in this case, the src attribute is bound to the img property, and the alt attribute is bound to the title property of the data). Next, I declare the list view with options. I especially like that WinJS crunches whitespace so I can format my options across multiple lines. The options provide a template for the list view to use, indicate how to handle taps and swipes, provide the item template and also where the data is pulled from. Some may argue that is too much imperative code being squashed into a declarative attribute; however, the options may also be set up programmatically.

<div class="bookList" data-win-control="WinJS.UI.ListView"
        data-win-options="{
        itemDataSource: Book.ListView.data.dataSource,
        itemTemplate: bookTemplate,
        selectionMode: 'none',
        tapBehavior: 'none',
        swipeBehavior: 'none',
        layout: { type: WinJS.UI.GridLayout }
        }"
></div>

You should note that I passed plain JSON to the list binding option. I do not bind directly to the object, but instead bind to the exposed dataSource property. As you can see, it was very straightforward to wire up a fluid user experience with selection, animated feedback and scrolling. Although I’m optimistic about using this library in the future, right now the sheer size is a deal-breaker. The uncompressed JavaScript for the UI by itself is almost 3 megabytes in size, then add another megabyte for the base. In addition add the 150KB stylesheets and you’re looking at a lot to load in the browser. I am hoping in the future there will be options to build to specific controls and provide a trimmed-down version that only contains the bare necessities for what will be used in an app.

There’s obviously a ways to go, but the real promise is the ability to create experiences that can truly share both code and markup between multiple targets, whether they are platforms and browsers or native experiences such as Windows Store apps. I’ll be keeping a close eye on this framework as it evolves and look forward to sharing more in future posts.

Grab the source and see the demo.