Site Meter
 
 

Full-height app layouts: A CSS trick to make it easier

In HTML layouts, there’s a fundamental difference between width and height. The natural state of affairs is that your page’s width is constrained to the width of the browser window, but its height is unconstrained – it grows however tall is necessary to contain its contents.

image

That makes sense for documents, but it’s a pain for application UIs. Native application UIs tend to slice up the screen both horizontally and vertically into a nested set of panels, some of which may scroll/resize, others being docked against particular edges of their parent panes.

image

So, what’s the robust way, with HTML/CSS, to set up a nested collection of panes that exactly divide both the width and height of the browser window?

Hang on, didn’t we have this one solved back in 1999?

Hmm… this reminds me of something… I remember: the <frameset> tag from HTML 4. Yes, HTML frames do divide the browser window both horizontally and vertically, exactly consuming the available screen area. So why don’t we use them any more? There are loads of reasons, including:

  • What you’re building is logically one page, but technically each frame is a separate HTML document, which makes interactions between them so much more complex
  • It’s not really practical to support deep-linking to (i.e., bookmarking) particular UI states
  • Mobile devices and tablets have very limited support for HTML 4 frames. iOS in particular requires the user to use two-fingered scrolling within panes. This is UX death.

OK, so what’s the 21st century solution?

I’ve used lots of hacks and tricks over the years to get columns and panes into my web applications, often involving JavaScript, $(…).height(…), window.body.clientHeight, and the onresize event. Ugh. Fragile and messy. But at long last this week I learned there is actually an elegant and robust way to set up nested exact-height panes with pure CSS, and it works on all browsers back to IE 7, even on current mobile browsers that don’t support position:fixed.

You know that if you set position: absolute on an element, then you can make it appear at a specified distance from the top, left, right, or bottom from its parent element. Well, it turns out that you can specify both top and bottom, or both left and right, and then it will dock against both edges and always resize to match its parent’s dimensions.

Let’s define some generic CSS rules for panes:

/* Generic pane rules */
body { margin: 0 }
.row, .col { overflow: hidden; position: absolute; }
.row { left: 0; right: 0; }
.col { top: 0; bottom: 0; }
.scroll-x { overflow-x: auto; }
.scroll-y { overflow-y: auto; }

Now it’s easy to set up a fixed-height header, variable-height body, and fixed-height footer:

<body>
    <div class="header row">
        <h2>My header</h2>
    </div>
 
    <div class="body row scroll-y">
        The body
    </div>
 
    <div class="footer row">
        My footer
    </div>
</body>

Of course, you also have to configure the heights and positions of the three rows by using a bit more CSS:

.header.row { height: 75px; top: 0; }
.body.row { top: 75px; bottom: 50px; }
.footer.row { height: 50px; bottom: 0; }

The result? It’s the classic mobile phone app layout. Screenshot with some colours and content added for interest:

image

Try it in your desktop or mobile browser here. Works on IE7+.

More nesting (or, tablet-style layouts)

The reason for setting up the generic CSS rules in that way is that it makes it easy nest panes arbitrarily. For example, you could split the main viewport into two vertical columns:

<body>
    <div class="left col">
        Left col here    
    </div>
    <div class="right col">
        Right col here
    </div>
</body>

… and then subdivide the right column so it has a fixed header, variable-height body, and fixed footer, just by putting some more row divs inside it:

<div class="right col">
    <div class="header row">
        View or edit something
    </div>
    <div class="body row scroll-y">
        Here’s some content that can scroll vertically
    </div>
    <div class="footer row">
        Some status message here
    </div>
</div>

Here’s the CSS to configure the widths/heights/positions of those panes:

.left.col { width: 250px; }
.right.col { left: 250px; right: 0; }
.header.row { height: 75px; line-height: 75px; }
.body.row { top: 75px; bottom: 50px; }
.footer.row { height: 50px; bottom: 0; line-height: 50px; }

Going further, in the right-hand pane’s body, you could also nest a horizontally-scrollable row:

<div class="body row scroll-y">
    <p>Here's a horizontally-scrollable thing:</p>
 
    <div class="scroll-x">
        Any content in here, if it's too wide, becomes independently scrollable
    </div>
 
    <p>That's enough - bye.</p>
</div>

The result of all this? Well, it’s a structure like this:

image

… but that’s pretty boring to look at, so here’s a version where I threw in a rough effort of some iPad-like styling:

image

Here’s a live example. It still renders correctly on very small screens (like a WP7 or iPhone) but this 2-column layout really needs a wider screen to be usable.

Enabling touch scrolling

The scrolling looks and works fine on a desktop browser, but on a mobile browser it varies:

  • On WP7, you’ll see no scrollbars, but you’ll get one-finger touch scrolling, without the lovely inertia/momentum effect. This is kind-of OK, though imperfect.
  • On iOS, you’ll see no scrollbars, and it requires two-finger scrolling (which is horrible), and it won’t use the inertia/momentum effect either. Badness.
  • There are similar problems on Android

With iOS 5, it will be possible to enable fluid, native touch-based momentum scrolling to our divs just by making a tiny tweak to the CSS, thanks to the new “touch scrolling” feature:

.scroll-x, .scroll-y { -webkit-overflow-scrolling: touch; }

I really hope this catches on and becomes a standard.

But in the meantime, for visitors on other mobile OSes, and until iOS 5 becomes prevalent, you can use the open-source iScroll library that provides one-finger momentum scrolling for Webkit (iOS and Android) and Mozilla browsers.

Enabling momentum scrolling on any given element requires only one line of JavaScript (assuming you’ve referenced iScroll.js):

new iScroll(theElementYouWantToEnableItFor);

For my example, I used the following block of JavaScript, which enables touch scrolling on all the .scroll-x and .scroll-y elements:

<!-- Touch scrolling -->
<!--[if !IE]><!-->
<script src="script/iscroll.js" type="text/javascript"></script>
<script type="text/javascript">
    var xScrollers = document.getElementsByClassName("scroll-x");
    for (var i = 0; i < xScrollers.length; i++)
        new iScroll(xScrollers[i], { vScroll: false });
 
    var yScrollers = document.getElementsByClassName("scroll-y");
    for (var i = 0; i < yScrollers.length; i++)
        new iScroll(yScrollers[i], { hScroll: false });                
</script>
<!--<![endif]-->

You could do this in fewer lines if you’re using a library like jQuery or XUI (which is a tiny implementation of a small part of the jQuery API surface, intended for mobiles). Here’s the resulting mobile-style scrollbar:

image

Of course, to see the momentum effect, you’ll need to run it in your Chrome/Safari/Firefox browser.

Credits: Thanks to Rob Swan and FellowshipTech for their articles and projects where I found the CSS positioning trick that underlies this approach to exact-height layout.

Knockout 1.3.0 Beta Available

iconKnockout version 1.3.0 is coming soon, and it’s going to be a huge release with a big stack of features that many of you have asked for.

The key theme is developer happiness – the new features will let you get more done with less code, eliminate some previous limitations, and offer more power to extend and customize how things work.

New to Knockout? If you’ve never used it before, don’t start with this post – start with the interactive tutorials.

The 1.3 code is now stable and feature-complete, so it’s time for a beta release. You can get it from…

For Visual Studio users, the NuGet package ID is knockoutjs, and this latest version is 1.2.9.0 (reserving version 1.3.0 for the final 1.3 release).

So, what’s new? Well, full documentation for the new features is still to be written, but here’s a brief rundown of the main enhancements.

1. Control flow bindings

Previously, if you wanted to display a repeating sequence of UI elements (“foreach”), or have a section of the DOM exist only if some viewmodel condition was true (“if”), you’d need to create an entire template to define that. A bit cumbersome for something so simple and commonplace! Now you can use the new control flow bindings – if, ifnot, with, and foreach – to achieve basic, declarative control flow without the need for a template. This can make your code a lot more elegant and to-the-point.

Here’s an example of using “foreach” and “if”:

Tip: Switch to the “result” tab to see the output

The "with" binding changes the binding context to whatever object you specify. This makes it easy to compose many independent view models together – you have a host model that contains references to its children, and then use "with" to bind different sections of the page to different child models. Example:

Of course, the "if", "ifnot", "foreach", and "with" bindings can all be combined and nested arbitrarily.

2. Containerless control flow

What if you want to set up template-less control flow (as in the above example), but without having to use extra container elements just to hold the "foreach", "if", etc., bindings? In that case, you can use the new comment-based control flow syntax like this:

The comment-based control flow syntax works with the "if", "ifnot", "foreach", "with", and "template" bindings. In a large number of cases, this means you can skip string-based templates and use the binding-based control flows instead, which can run much faster depending on what exactly you’re doing.

3. Access to parent binding contexts

Whether you’re using regular nested templates, or nesting the new control flow blocks, sometimes you want to reference properties that exist at the upper levels of binding. There are four new pseudo-variables that you can use in any binding (inside or outside traditional templates):

  • $data – returns the current item
  • $parent – returns the item from the parent binding context
  • $parents – an array containing all the parent binding contexts. $parents[0] == $parent, then $parents[1] is the level above that, and so on.
  • $root – returns the item at the top level of binding (usually your primary view model)

Example:

4. jQuery-style event handling

In most cases, data-bind attributes can be kept clean and elegant. But one rough spot in the past has been event handlers, because to pass parameters, you were generally guided to use function literals. For example:

<button data-bind="click: function() { viewModel.items.remove(this) }">Click me</button>

Many of you rightly pointed out that this is pretty ugly. It’s much nicer if bindings can be kept minimal and declarative! To fix this, KO 1.3 lets you replace that with:

<button class="remove-item">Click me</button>

… and then in your JavaScript, add this (if you’re using jQuery):

$(".remove-item").live("click", function() { 
    viewModel.items.remove(ko.dataFor(this));
});

There are two new helper functions that make this possible:

  • ko.dataFor(domElement) – returns the data item associated with a particular DOM element
  • ko.contextFor(domElement) – returns the entire binding context associated with the DOM element, so you can retrieve its $parent property, etc.

Example:

5. Binding providers (and hence external bindings)

Most KO developers find the default data-bind attributes provide a very convenient, easy-to-maintain way of declaratively associating viewmodels with views. But if you would prefer to store your binding configuration elsewhere, the new binding providers API is an extensiblity point that allows alternative configuration mechanisms to be plugged in.

For example, here’s one possible alternative way that you could configure bindings. Notice the absence of data-bind attributes – I’m using a binding provider to set up the binding configuration in JavaScript:

To be clear, binding providers are a general extensibility mechanism, not any specific configuration format. The above example is just one possible way it can be used to define bindings in JavaScript. I’m looking forward to seeing suggestions from the community about how this extensibility can be used, and for what the most elegant and convenient way of defining bindings might be. I know some of you have already suggested externalising bindings (e.g., Brandon Satrom’s knockout.unobtrusive plugin); this new API will make it easier to implement custom binding mechanisms. Bring on more plugins!

6. Throttling

Occasionally, it’s desirable to limit how fast observables and dependent observables update. For example, you might be using a dependentObservable to invoke Ajax requests whenever a set of observables change. Sometimes you might want to change multiple underlying observables, but only have that dependentObservable re-evaluate once, after all the changes (so that it only fires one Ajax request). Effectively, you want a kind of atomic update.

This is now pretty easy to do: you can apply an extender called "throttle" either to observables or to dependent observables. For example:

// Will not notify changes faster than once per 500ms 
var myObservable = ko.observable("initial value").extend({ throttle: 500 });
 
// Will not re-evaluate *or* notify changes faster than once per 500ms 
var myDependentObservable = ko.dependentObservable(function() { 
    // Evaluation logic goes here, usually referencing other observables 
    return 123;
}).extend({ throttle: 500 });

The way this works is that, when throttled, notifications and evaluations are done asynchronously, and don’t occur at all until they stop being triggered for the specified throttle timeout duration.

In the case of making a dependent observable only update once even if there are multiple synchronous changes to its dependencies, you can use a very short timeout (for example, 1ms), and then it will re-evaluate as soon as possible after any series of synchronous changes to its dependencies. Example:

This is all I have time to write about just now, but there are also some other new extensibility features I haven’t mentioned. I’ll follow up with more as soon as I can.

Backward compatibility notes

This beta version of Knockout 1.3 should work with your existing applications, with the following exceptions:

  • It has dropped compatibility with old versions of jquery.tmpl. If you’re using a version of jquery.tmpl older than 1.0.0pre, you’ll need to upgrade. You can find it at https://github.com/jquery/jquery-tmpl.
  • The API for registering custom template engines has changed, so any existing custom ones will need to be updated. The change makes it much easier to define custom view engines (you don’t have to worry about "rewriting" any more). I’ll write a separate post about this soon.
  • Since the binding algorithm has been enhanced in many ways, any third-party plugins that specifically try to alter how bindings work may not work immediately (for example, the "namespaces" plugin). I’ll be talking to the creators of those plugins to ensure both KO and the plugins are updated to work together properly. Apologies for any inconvenience! The new extensibility APIs should make it much easier to implement these kinds of plugins now anyway.

Please try it out and let me know what you think of the new features!

Review: Open-source components used in learn.knockoutjs.com

Over the last two months I’ve been using my wife-approved weekend coding time to build learn.knockoutjs.com, a set of interactive in-browser coding exercises to help people learn the basics of knockout.js in a convenient and fun way. If you haven’t already seen it, grab a hot drink and treat yourself to 15 minutes of enjoyable learning right now!

image

Since this was a one-person weekend project, I’ve been using all possible means to minimise the total development time. A key part of that has been using existing open source components so I can avoid reinventing stuff wherever possible.

In the end, I was surprised to realise just how many open source projects went into building this single-page application, and how high-quality and easy-to-use most of them were. So, I’d like to give them a bit of recognition by listing them here and giving you a mini-review of each. Maybe you’ll benefit from a few of these in your own projects.

1: Knapsack

Knapsack (NuGet: knapsack) is Andrew Davey’s JavaScript / CSS dependency manager for ASP.NET. It lets you define a series of JS/CSS references like this:

Html.ReferenceScript("Scripts/Lib/knockout-1.3.0.latest.js");
Html.ReferenceScript("Scripts/Custom/bindings.js");
Html.ReferenceScript("Scripts/Custom/appViewModel.js");
Html.ReferenceStylesheet("Content/App/boilerplate.css");
// ... etc ...

… and then emit the actual <script> / <link rel="stylesheet"> tags like this:

@Html.RenderScripts()

What’s the point? Well, in debug mode, it just emits one <script> tag per referenced script, as usual, so you can use your browser’s JavaScript debugger as always. But in release mode, it emits just one <script> tag pointing to a special URL that returns all of the JavaScript code combined into a single file, minified using the MS Ajax Minifier library. So, if you view source on learn.knockoutjs.com, you’ll see only one or two <script> tags even though I’m actually using dozens of JS files.

It also sets all the right cache/etag headers so the browser can avoid making unnecessary HTTP requests, but doesn’t risk using out-of-date scripts because the dynamic script URL changes whenever you change the referenced script files (the URL includes a hash of your JavaScript source code).

I’ve been very happy with knapsack, and would definitely recommend it. The one caveat I discovered is that it automatically references all JavaScript files in any folder that you point it at, which is surprising if you intentionally didn’t reference one of the files in that folder. That aside, if you can organise your JS/CSS files into folders to match how they should be combined, you’ll probably be very happy with Knapsack too.

Note: Knapsack will soon be changing its name to something else due to a cease-and-desist.

2: YamlSerializer

As part of my minimum viable product kind of philosophy for the site, I didn’t create any kind of editor for the tutorials, and I didn’t even store them in a database. The simplest editor is a text editor, and the simplest data store is files on disk. So, the tutorials are written in YAML – a human-editable structured text format.

To parse these YAML files, I used YamlSerializer (NuGet: yamlserializer), a .NET library written by Osamu Takeuchi. So, I have .NET classes like this:

public class Tutorial
{
    public string Title { get; set; }
    public virtual ICollection<tutorialStep> Steps { get; set; }
    // ... various other properties ...
}
 
public class TutorialStep
{
    public string Instructions { get; set; }
    // ... other properties ...
}

… and YAML files like this:

title: Introduction
steps:
 - instructions: |
    ##Welcome!
    Blah blah blah, here's what to do...
 - instructions: |
    ##Doing the next thing
    Do the following things...

… and YamlSerializer parses Tutorial/TutorialStep instances like this:

Dictionary data = new YamlSerializer().Deserialize(allText);
return new Tutorial {
    Title = (string)data["title"],
    Steps = (from stepDict in (Dictionary)data["steps"]
             select new TutorialStep {
                 Instructions = ((string)stepDict["instructions"]).Trim(),
                 // ... other properties ...
             }).ToList(),
    // ... other properties ...
};

It’s maybe not quite as pretty as it could be with a more generics-oriented API (or maybe even a dynamic one?) but it’s robust, fast enough (though I do cache the parsed result), and I had it working in minutes. So I’m satisfied with that!

3: Ninject

image

I didn’t strictly need to use Dependency Injection in this project, but since it takes exactly 10 seconds to set up thanks to NuGet, I did it anyway. My favourite DI container is Ninject (NuGet: Ninject.MVC3), a very easy-to-configure product from Nate Kohari. The MVC 3 version hooks itself into the MVC 3 Dependency Injection extensibility point automatically (internally, it uses David Ebbo’s WebActivator to make itself run when your app first starts).

In this site, it’s only used to supply ITutorialRepository instances to my one-and-only HomeController. Really I could have just hardcoded the dependency, as there is only one tutorial repository (YamlTutorialRepository), but hey it genuinely did take less than one minute to set up the DI and it made me feel less like I was just hacking things together…

4: CodeMirror

image

See those lovely in-browser code editors with context-specific syntax highlighting, the ones that even work on IE6? That’s CodeMirror, a JavaScript library from Marijn Haverbeke.

This was not particularly easy to get working just as I wanted – I hit a range of browser-specific performance problems mainly because of my compulsive need to put rounded corners and dropshadows on every element on the page. Nonetheless, it’s a completely vital component, the entire site couldn’t exist without it, and it’s an impressive technical achievement in its own right, so I’m willing it to give it plenty of slack.

The one alternative code editor I tried was Ace – it looks very promising, but had serious layout issues in IE (e.g, if you resize the font). In the future Ace might overtake CodeMirror to become the smartest and best-performing in-browser code editor, but it’s not there yet.

5: Amplify.js

image

Did you notice that if you work on one of the tutorials, then leave the tutorial (possibly even the whole site), and then later come back, the site will offer to restore your code and your position in that tutorial? How can this be, given that you didn’t even register for an account? It’s HTML 5 local storage, of course.

To implement this, I used Amplify.js (NuGet: amplifyjs), a JavaScript library from appendTo. Of all the JavaScript libraries I used, this one was the easiest to get working. It was completely trivial, in fact – just about three lines of code in total.

Whenever you run your code, the site saves your state:

amplify.store("tutorialState." + tutorial.Id, { view: ..., viewModel: ..., stepIndex: ... });

Similarly, whenever you load a tutorial, the site checks if you have saved state:

var savedState = amplify.store("tutorialState." + id);

… and if so, offers to restore it. The site doesn’t care what browser you’re running, because Amplify.js figures out what local storage options exist and uses whichever one is going to work for you.

6: json2.js

Amplify.js needs your browser to support JSON.stringify, the JavaScript API for serializing an object to JSON. All decent browsers do, but just in case you’re running IE 7 (What kind of web developer are you? Shame on you!), I reference json2.js (NuGet: json2) which provides an implementation of JSON.stringify. Json2.js is the work of legendary JavaScript guru and JSON inventer, Douglas Crockford.

7: Highlight.js

In the tutorial instructions, there are lots of little snippets of code showing you what to do. For the syntax highlighting here, I used highlight.js from Ivan Sagalaev.

There are dozens of code-highlighting JavaScript libraries, and I’ve no reason to say this one is better than the rest. I chose this one simply because it lets me “highlight” a certain DOM element at an arbitrary time of my choosing, and doesn’t get fussy about doing things when the page first loads. Also it tolerates existing HTML markup in the element you’re highlighting, which is how I was able to put special yellow backgrounds on selected bits of code to emphasise the key points.

Highlight.js was pretty easy to work with. I made a Knockout custom binding to display the tutorial steps, and whenever it updates the UI, it highlights any code blocks like this:

$('pre code', element).each(function () {
    hljs.highlightBlock(this, '    ' /* tab = four spaces */)
});

To customise the colour scheme, you just edit its CSS file. Pretty convenient.

8: Showdown.js

I said the tutorials are written in YAML, but that’s just how the .NET objects are represented as text. To represent the actual markup within the instructions (e.g., the headings, links, code blocks, etc.), I used Markdown, a simple text-formatting DSL invented by John Gruber. So I’ve got “markdown” syntax within my YAML files, like this:

 - instructions: |
    ##How to write markdown
    To emphasise text, use *asterisks*. To include a code snippet, just indent it like this:
 
        var blah = "this is some JavaScript"
 
    You can put bits of text into code font `using backtick quotes`.

That markdown text gets sent to the client (in JSON form, of course – so many text-based formats!). To render it as HTML in the browser, I use showdown.js, a Markdown-to-HTML conversion library from John Fraser. This is pretty straightforward – I have a Knockout custom binding like this:

ko.bindingHandlers.markdown = {
    update: function (element, valueAccessor) {
        var markdownValue = ko.utils.unwrapObservable(valueAccessor());
        var htmlValue = markdownValue && new Showdown.converter().makeHtml(markdownValue);
        $(element).html(htmlValue || "");
    }
};

… and then I can bind elements to show markdown data like as follows, and it will refresh the display whenever the markdown text changes:

<div data-bind="markdown: someMarkdownValue"></div>

9: jQuery and friends

image

Obviously I used jQuery. I won’t bother discussing the core library because everyone knows about that, but I will mention the jQuery plugins I used:

  • jQuery UI, not for any of the visual elements, but merely for its “draggable” API. (You can drag to resize the panes, see?). I didn’t need any of its buttons, menus, etc.
  • jQuery Impromptu, to display the overlaid prompts (e.g., for choosing a tutorial or for explaining that your code will be overwritten when you click “Help, it’s not working!”). To be honest, this is the only component I regretted using. It’s is probably great if you use it for its intended use case (which includes things like multi-step wizards), but all I wanted was to move an existing DOM element in front of a gray background, lightbox style. jQuery Impromptu tries to do all manner of extra things that that screwed around with my DOM and its default animation was horribly stuttery on Chrome and Safari. In the end, I butchered its source code almost beyond recognition to get the desired behaviour with tolerable performance. It would have been much quicker for me to write my own version from scratch…
  • jQuery Templates (used internally by Knockout, and soon to be replaced with JsRender)
  • jQuery Address (used internally by knockout.address for the hash-based navigation)

10: … and of course, Knockout.js

icon

Not surprisingly, the UI is all orchestrated with knockout.js (NuGet: knockoutjs), so I have a very tidy MVVM architecture for the client-side code. There are objects to represent each of the major UI elements (panes, draggable splitters, code editors, prompts, etc.), and Knockout takes care of keeping the visible UI in sync with that whole object graph.

But I don’t need to explain this – you can learn much more from the tutorials

Summary

Building an application by smashing together a lot of different open-source projects is pretty quick. The end result is not always a perfectly harmonious symphony, as each component brings its own coding styles and conventions, but it works.

What I’ve learned about components is kind of predictable: the most useful ones are

  • Focused. Just solve one problem, please.
  • Self-contained. I’m not going to accept a tall stack of dependencies; it’s too risky.
  • Clearly documented. First I want examples, then a systematic list of API functions.
  • Available on NuGet Smile

It wasn’t difficult to find components to suit my requirements. It’s great to see the open source ecosystems are thriving, both on .NET and JavaScript.