Ajax Bestiary: A Javascript Field Guide
 
Ajax Bestiary: A Javascript Field Guide
 
 

Entries Tagged as 'Tutorial'

Full Screen API embiggens the lowliest of web apps

Posted by Dave Mahon

Please forgive the Simpsons reference in the title, but let’s face it, browsers eat up an enormous amount of screen real estate with menu bars, super bars, status bars, toolbars, scrollbars. And that’s all before you get to the white space and toolbars and menus your own app requires.

Sure, you can press F11 today in pretty much every browser and achieve that end, but how many of us actually do that? If we’re the power users and we don’t do that, what hope do we have for our users?

The Full Screen API will make it easy to do automatically. Better still, pre-release versions of this API are already supported in Firefox 9 and Chrome 15 (though disabled by default in Firefox 9).

To get you started, the Mozilla Developer Network has released its own guide to the interface.

In brief, there are a few key requirements:

  1. The calling object for requestFullScreen() must be a DOM node
  2. The DOM node may not be the child or parent of a plugin
  3. If the DOM node is an IFRAME (or is the parent or child of one), the IFRAME must have an allowfullscreen="true" attribute

So what does real-world code look like? Let’s take a look at Mozilla’s own example:

function toggleFullScreen() {
  if ((document.fullScreenElement && document.fullScreenElement !== null) ||    // alternative standard method
      (!document.mozFullScreen && !document.webkitIsFullScreen) {               // current working methods
    if (videoElement.requestFullScreen) {
      videoElement.requestFullScreen();
    } else if (elem.mozRequestFullScreen) {
      videoElement.mozRequestFullScreen();
    } else if (elem.webkitRequestFullScreen) {
      videoElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
    }
  } else {
    if (document.cancelFullScreen) {
      document.cancelFullScreen();
    } else if (document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if (document.webkitCancelFullScreen) {
      document.webkitCancelFullScreen();
    }
  }
}

As you can see, the naming conventions aren’t quite there yet. Further, neither Firefox nor Chrome quite apply the CSS expectations of the developing standard.

When the calls fire successfully, either fullscreenchange, mozfullscreenchange or webkitfullscreenchange events will fire. Likewise, when it fails, either fullscreenerror, mozfullscreenerror or webkitfullscreenerror events fire.

Note also that in Firefox 9, you cannot use a form in fullscreen mode; pressing any key inside of an input seems to take you out of Full Screen mode. The browser provides no instructions for exiting fullscreen mode, so you will need to provide them yourself.

Confusingly, the ALLOW_KEYBOARD_INPUT parameter in the webkitRequestFullScreen() call seems to block the keystroke handler in the MDN sample, though forms once again work. It also provides instructions for exiting Full Screen mode, though it neglects to mention that Esc will do it as well, instead referring to Command + Shift + F on my Mac.

Still, Firefox 10 is expected to support the API out of the box, so we can start to see the development of true browser application environments. The possibilities for kiosk environments are also exciting.

Grids a la CANVAS

Posted by Dave Mahon

There are lots of grid libraries out there. Naturally, this means we need to reinvent the wheel. With HTML5, we have a fantastic new tool called the CANVAS tag which frees us from the limitations of CSS.

The canvas tag is beautifully documented and supported on most modern browsers. Further, aside from IE 6-8, the vast majority of the browsers support the element as it is defined, which means we don’t need wrapping libraries to deal with browser quirks.

Using all of these libraries as baselines, there are a few essential pieces of functionality:
We must be dimension agnostic. Whether our space is 480px or 2560px wide, it should work well
We must support any number of columns
We must support n-wide columns
We must support arbitrary gutter sizes
We must get results consistent with those other libraries

To that end, I present the grid() function. It returns an object with an array of offsets, as well as plenty of other information that facilitates writing still more content to the canvas (or advanced features like simulating sub-columns). It has no library dependencies, although it currently does not gracefully handle older browsers.

Basic usage is simple:

result = AB.grid(canvas, 16, 20);

This one line takes the canvas element specified in the first parameter and divides it into 16 columns separated by 20 pixel gutters. The function returns an object that looks like this:

{
   cols: 16,
   element: canvas#tutorial,
   gutter: 20,
   offsets: [0, 60, 120, ..., 900],
   width: 40
}

You can also pass arrays:

result = AB.grid(canvas, [1, 3, 1], 10);

This divides the element into a 20% panel on the left and right and 60% in the middle. Now our response looks like this:

{
   cols: 3,
   element: canvas#tutorial,
   gutter: 20,
   offsets: [0, 194, 756],
   width: 186
}

Let’s go changing the colors:

result = AB.grid(canvas, [1, 4], 10, { foreground: '#CCC', background: '#EEE' });

Need to pad the outer margins as well?

result = AB.grid(canvas, [1, 4], 10, { includeOuter: true });

Maybe we just want to simulate the arithmetic, without drawing anything?

result = AB.grid(canvas.width, [1, 4], 10);

This would also work:

result = AB.grid(canvas, [1, 4], 10, { overrideWidth: canvas.width });

Now you’re probably saying that this is all well and good, but why bother when we are already using a CSS layout library for the site in general? Aside from making it easier to construct graphs, we can also use this to generate perfectly sized images within the client.

The following three lines will create an image tag from a canvas and do it faster than the human eye can detect:

var img = document.createElement('img');
img.setAttribute('src', canvas.toDataURL());
document.getElementsByTagName('body')[0].appendChild(img);

You could almost as easily assign it as a background image (here I am finally employing jQuery):

$('body').css('background', 'url(' + canvas.toDataURL() + ')');

Note that you must wrap the image data in the strings url( and ) for this to work.

Check out the code on GitHub.

How to: Detect display density

Posted by Dave Mahon

In some of my past projects, I’ve had a strong desire to know the pixel density of the display device. In traditional desktop environments, we could reasonably trust that an assumption of 72dpi would work well, but on mobile devices this is not the case. The iPhone 4 packs twice as many pixels in an inch as the 3GS and Android devices vary wildly, while it sometimes feels like you can visually distinguish the individual pixels on older Blackberry devices.

Meanwhile, you still need everything to fit as close to pixel-perfect as possible. On the server-side, this requires a massive database, like Tera-WURFL, but on the client-side, we can use a fast and simple script.

For the purposes of this post, I will use jQuery, but you can use the framework of your choice to achieve the same end.

var test = $('</div>');
 test.height("1 inch").width("1 inch");
 var yDensity = Math.round(test.height());
 var xDensity = Math.round(test.width());
 test.remove();

First, I create a DIV with some minimal level of content, since mobile browsers like to collapse (or even ignore!) empty DOM nodes to improve performance.

Then I set its dimensions. I tried setting the dimensions as part of the style, but curiously, Firefox chose to ignore it so I didn’t even bother testing this behavior in Safari.

Now I can use the height() and width() methods in jQuery, which return an integer representing the number of pixels. Note that I have to round this figure, because those listed pixel densities are sometimes approximate. You’ll definitely get a float if you use metric units, of course.

Finally, I remove the node, as we no longer require it. Note that we never attached it to the DOM, so we never influenced rendering of the actual page.

Event-driven development with Backbone

Posted by Dave Mahon

Do you really care if the user clicks an option inside of a SELECT? In most cases, it’s only a means to the end of getting the new value. So why is your change event handler sending data to the server and modifying the DOM? This leads to spaghetti code, violation of DRY and ever more expensive maintenance costs as your app’s interface evolves.

Thus was born Backbone, an extension of underscore, with default support for jQuery, and with a bit of editing, support for MooTools.

So let’s say you’re Amazon and you’re trying to find a way to comply with the legal requirements in some states that say affiliate sold items are subject to sales tax. This is complex and you probably only want to get the most relevant data as late in the process as possible. Backbone makes it surprisingly easy.

Bind to a change event on our data object:

var destination = new Backbone.Model();

_.extend(destination, {state: null});

var order = {
  subtotal: 100,
  tax: 0,
  total: 100
};

//underscore makes it easy to pseudo-subclass
_.extend(order, new Backbone.View());

destination.bind( "change", function() {
  //Pass null in the first parameter to save all model properties
  destination.save(null, {
    success: function(model, reps, xhr){
      //Apply the tax rate
      order.total = order.subtotal + order.subtotal * xhr.taxrate;

      //Update display. Note that this is a no-op by default and
      //you should define it as needed elsewhere.
      order.render();
    }
  } );
} );

Now for our event handler on the SELECT. Note that I’m using jQuery, but you could use any library (or none at all).

$("#state").change( function() {
  //Do whatever pre-processing is required
  var v = $(this).val();

  //This could be generalized very easily
  destination.set({
    state: v
  });
} );

Why you should care about MooTools

Posted by Dave Mahon

Let me start by saying that I love jQuery. Most of the time, it makes it very easy to write sane code. Sometimes, however, it just feels weird.

For example, let’s say we need to insert a link at the beginning of a div. You might write something like:

$('#myDiv').prepend('<a href="http://my.host/path" title="This is a cool link!">Click here</a>');

-or-

$('<a href="http://my.host/path" title="This is a cool link!">Click here</a>').prependTo('#myDiv');

It seems fairly straightforward, but it’s a maintenance nightmare, because you now have raw HTML buried inside of your Javascript. If it’s complicated HTML that is being dynamically constructed, it’s all too easy to introduce hard to find browser parser errors.

MooTools, on the other hand, gives you a powerful alternative with its Element class.

To create a new DOM element you literally call a constructor and pass the tag name and attributes to it. So, for example, that same link would be built with:

var myLink = new Element('a', {
 href: "http://my.host/path",
 title: "This is a cool link!",
 text: "Click here"
});

At the price of a handful of few extra bytes, you get clear, legible code. The equivalent of .prepend() is thus:

$('#myDiv').grab(myLink, 'top');

While this might at first seem less intuitive, you can use the same method call to insert that link before or after the selected element. Sure, you could do that with .before() or .after() in jQuery, but imagine if you needed to make the insertion point variable!

There are also a slew of additional benefits, like ECMAScript 5 array methods, support for multiple inheritance, and event handlers that make it easy to detect if the user is pressing Alt while right clicking.

Give MooTools a try, but before you do, I strongly urge you to read Sean McArthur’s excellent post, A Better Way to use Elements, on the difference between $() and $$(). If you come from a background of using Prototype, it will seem perfectly natural, but jQuery users can be caught by surprise.

Getting Started With Chrome Frame

Posted by Don Albrecht

As a follow up to my last post, I thought I’d put out an instruction on actually creating a Google Chrome retreat.

Step 1: Add a Chrome Frame header to your site.

Add this to the head of the Chrome Frame dependent pages:

<meta http-equiv="X-UA-Compatible" content="chrome=1">.

If this isn’t practical, You can also configure your server to send an HTTP header of

X-UA-Compatible: chrome=1

I advise against this approach, however because server configs won’t be immediately available to other users on a site.  By its nature, a Chrome Frame retreat should be considered a reasonably unique event.  It’s good to clearly document this dependency for anyone else who may need to debug things later.  Once you’ve started to retreat, make sure you’re applying the header to every page of the site.  The two engines render things differently and you may create an inconsistent experience for your users if they are transitioning between Chrome Frame and Trident environments frequently.  In my experience, border-radius and drop shadow toggling on and off is a pretty strong “It’s Broken” signal for end users.

Step 2: Identify your Dependency Wall.

  1. Identify the range of pages / screens in your web app that you can’t support in IE.
  2. Identify the navigation routes to those pages.
  3. Leverage the routes to find natural locations for gatekeepers.
      1. Login Screens (the perfect location)
      2. Tutorial Screens (A natural context fit for users)
      3. An Advanced Mode (Similar to the “enriched mode” Microsoft uses for ActiveX requiring cloud apps).
    • If you’re lucky, this will be a screen somewhere beyond your conversion pipeline.
  4. Create an interception event for IE users.  You can bake this into your app, but the easy way is with conditional comments.
    1. Identify the Dom Nodes in your layout that must be presented to the user for them to progress.
    2. <div id=’loginbox’>…</div>
    3. Create a conditional comment to hide the nodes and prompt the user to install Chrome Frame.
    1. Be careful of z-index.  The chrome install iframe doesn’t work well if IE is floating some other content node on top of the install button.
    2. UN-INSTALL chrome frame once you’ve tested your retreat perimeter.  This is a big deal for ongoing development as you won’t be testing in IE otherwise.
    3. Becareful about your conversion pipeline.  Your key selling points may be behind the perimeter, but the act of demanding Chrome Frame is a pretty severe barrier to forward movement.
  5. <html>
    <body> <div id='prompt'></div>
    <!--[if IE]>
    <script type="text/javascript"
    src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
    <style>
    #loginBox{
    display:none;
    }
    </style>

    <script>

    window.attachEvent("onload", function() {
    CFInstall.check({
    mode: "inline",
    }); });
    </script> <![endif]-->
    </body>
    </html>

    Other Lessons Learned

    Here’s a few lessons learned from deploying this.

     

My Friend “use strict”

Posted by Don Albrecht

At this point you’ve probably been seeing it around a bit, but if you haven’t  you should probably start placing

"use strict";

at the top of every js file you write.

So why should you use it?

The big win for me has been catching all the time I fail to define a local variable before assigning to it.  For those of you unaware, writing a = ‘b’; when a is undefined used to create a globally scoped variable a.  With strict mode, it throws an exception.

In the past, I’ve spent many an hour debugging errors I’ve created by this mistake.  Now, I just get exceptions and all is happy.

There are several other ways ‘use strict’ can make your coding life easier, but this feature alone has made it a life saver for me.

In Fact, if you aren’t ready to use it globally in your project yet.  you can use it locally at a function level but placing the statement in the first line of the function like so:

function(){
 
    "use strict";
 
    //other code
 
   }

What is the best part about “use strict”?

The best part about using “use strict” is that for all practical purposes, browser support doesn’t matter.  Compared to all of the other shiny new features emerging for javascript, ‘Strict Mode’ gets the biggest bang for its buck when it’s available for the browser you the developer use.  After all, you’re the one creating and testing the bugs it’s going to catch.  This is a case where ‘Runs On My Machine’ is almost all you need; ‘Runs On My Bosses Machine’ might be handy as well tho.

Creating Dynamic Styles with Handlebars.js and InsertRule

Posted by Don Albrecht

2 months ago, I wrote about InsertRule: a handy way to dynamically write style rules.  The problem is, building giant strings for complex CSS3 style rules is more than a little tedious to write and debug.  Today I’m going to discuss a method for streamlining this process using templates.

Step 1: Create a template for the CSS rule.

I’m using an html script tag for demonstration purposes.  In a  production setting you’ll want to precompile this.

    <script id="tab-template" type="text/x-handlebars-template">
        -webkit-border-top-left-radius: {{leftRadius}}px;
        -webkit-border-bottom-left-radius: {{leftRadius}}px;
        -moz-border-radius-topleft: {{leftRadius}}px;
        -moz-border-radius-bottomleft: {{leftRadius}}px;
        border-top-left-radius: {{leftRadius}}px;
        border-bottom-left-radius: {{leftRadius}}px;
        -webkit-border-top-left-radius: {{rightRadius}}px;
        -webkit-border-bottom-left-radius: {{rightRadius}}px;
        -moz-border-radius-topleft: {{rightRadius}}px;
        -moz-border-radius-bottomleft: {{rightRadius}}px;
        border-top-left-radius: {{rightRadius}}px;
        border-bottom-left-radius: {{rightRadius}}px;
        -moz-box-shadow: 0 0 5px #888;
        -webkit-box-shadow: 0 0 5px#888;
        box-shadow: 0 0 5px #888;
        
        width: 100px;
        border: 1px solid black;
        padding: 5px;
        background-color: #cecece;
</script>

Step 2: Define your custom attributes.

Handlebars takes an object as input to replace the dynamic variables in the template.  Here we’re specifying a large left radius and a right radius of 0.

var source   = $(“#tab-template”).html();
var tab_template = Handlebars.compile(source);
var rule =  tab_template( {leftRadius: 20, rightRadius: 10});

 

Step 3: Writing the Style

Use Style.Insert to attach the new rule, We’re using an ID here, but it could just as easily be any other selector.

 $.style.insertRule( ['#displaynode'], rule  );

You can learn more about InsertRule here:

InsertRule, A handy way to add CSS rules on the fly with jQuery

Templatize your dynamic site with mustache

Posted by Dave Mahon

Let’s face it. We spend a lot of time writing markup in all sorts of languages. Whether we’re producing the next big social networking site or creating a funky template, there is a lot of boilerplate that is continuously repeated. More importantly, chances are we’re implementing it in a mix of JavaScript, HTML and our backend language of choice.

It makes managing our presentation a muddled mess. That’s where template engines come in. If you’re using pure PHP, you could use Smarty, but this is a JavaScript-focused blog and you probably have some of your presentation in pure JavaScript anyway.

That’s where mustache comes in. Available in most web languages, it provides a simple and flexible way of integrating content into your site.

Let’s say we’re implementing a receipt purely in JavaScript. Our data looks like this:

{
  "title": "Our Web Store",
  "items": [
    {"name": "Widget",
     "unitprice": "1.45",
     "quantity": "2",
     "totalprice": "2.90"
    },
    {"name": "Whosit & Whatsit",
     "unitprice": "0.45",
     "quantity": "1",
     "totalprice": "0.90"
    }
  ]
}

First, we’ll integrate the mustache library, like so:

<script type="text/javascript" src="mustache.js"></script>

Next, we’ll define the template:

var template = "<h1>{{title}}</h1>

{{#items}}
    <dt<strong>{{name}}</strong></dt>
    <dd>${{totalprice}}</dd>
    <dt>{{quantity}} @ {{unitprice}}</dt><dd></dd>
{{/items}}";

Finally, we simply have to render the HTML, like so:

var html = Mustache.to_html(template, json);

This will produce output like:

<h1>Our Web Store</h1>
<dt><strong>Widget</strong></dt>
<dd>$2.90</dd>
<dt>2 @ 1.45</dt><dd></dd>
<dt><strong>Whosit &amp; Whatsit</strong></dt>
<dd>$0.90</dd>
<dt>1 @ 0.45</dt><dd></dd>

You can then use your DOM manipulation technique of choice to insert the markup.

Note that it automatically escaped the ampersand. Had we used three curly brackets, then it would have allowed it to remain literal.

What happens when we items is an empty array or, worse, isn’t there at all? It simply doesn’t appear, because the hash prefix in our template makes it conditional. You can invert the condition (say, for presenting a special message for missing information) by using a caret (^).

Since the items element was actually an array, mustache also automatically knew to loop through, which could potentially be handy for commenting systems.

Getting and setting values for jQuery widgets

Posted by Dave Mahon

If you come from an OOP background you’re likely used to defining class variables like this:

class MyClass {
   var internalValue1;
   var internalValue2;
   function constructor() {}
}

The values which are to be stored in your object are simple, straightforward, likely to be typed, and the compiler will detect typos in the variable names. JavaScript, as a loosely types language which treats its objects more like collections than discrete objects, can’t really offer us those protections. Conversely, that also means that we have a great deal of flexibility.

In essence, we can dynamically subclass our objects, which you have to admit is a neat trick.

So, for jQuery.UI widgets, the variables stored within our widget are not defined until we assign them. The act of assignment defines the variable name, which can then be called upon at will.

Assignment is simply:

this._setData(‘variablename’, variablevalue);

Retrieval of this value is similarly straightforward:

this._getData(‘variablename’);

This does mean that the old problem of misnamed variables is alive and well. It also means that you need to be prepared to handle an undefined response to the _getData call.