Mark Dalgleish

Mark Dalgleish

UI Engineer - Melbourne, Australia

Getting Closure in JavaScript

On May 23-24 2012, the first Web Directions Code event was held in Melbourne. With twenty talks from local and international presenters, including Faruk Ates and Ryan Seddon (Modernizr), Divya Manian (HTML5 Boilerplate, HTML5 Readiness), Dmitry Baranovskiy (Raphael), Dave Johnson (Phonegap) and, of course, the inimitable John Allsopp, one of the masterminds behind Web Directions.

I was extremely excited to have been invited to be part of such a great new conference targeted at the more code-oriented aspects of our industry. Presenting on the second day of the event (a whole day dedicated to JavaScript), I gave a 15 minute talk titled “Getting Closure”.

As the official description reads, it was an in-depth look at how JavaScript’s first-class functions and lexical scope allow us to write powerful and expressive code. Through the single topic of immediately invoked function expressions, we touched upon function scope, closures, JavaScript “classes”, CoffeeScript and ECMAScript 5.

Most importantly, my presentation aimed to clear up any confusion around closures, one of JavaScript’s most powerful features. Interestingly enough, even those who I consider seasoned veterans found that my presentation helped solidify their mental model and ensure that their terminology is correct, something that can be quite tricky with such an abstract concept.

Web Directions Code 2012 was a fantastic event and I’m greatly looking forward to more in the future. Many thanks, as always, to Maxine Sherrin and John Allsopp for providing such an awesome conference and for their tireless efforts in bolstering the Australian web developer community.

Further reading:

Using 3D CSS Transitions With Fathom.js

If you’ve started making presentations using the browser, your work might still resemble a standard Keynote or Powerpoint presentation. The main benefit of creating presentations in HTML is that you have all the power of modern browsers available to you. This article will focus on just one aspect of this: CSS 3D transforms and transitions.

Presenting Fathom.js

Fathom.js, the jQuery-based presentation framework I created for MelbJS, might look fairly simple but its simplicity is deceptive. For example, you can build fairly powerful yet straightforward 3D transitions purely with CSS. It’s easier than you might think.

It’s important to note that Fathom.js uses CSS classes to indicate which slides are active or inactive. By default, all slides have the class of ‘slide’. When a slide is active it has the class ‘activeslide’, and it has the class ‘inactive’ when (you guessed it) it’s inactive. This allows us to add different styles to each state and a transition between them with a few lines of CSS. Now that we have 3D transforms at our disposal, let’s create an example 3D transition.

Let’s Get Started

First of all, we’ll create a standard Fathom.js presentation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<!DOCTYPE html>
<html>
  <head>
    <title>Fathom.js 3D CSS Transitions</title>
    <script src="jquery.min.js"></script>
    <script src="fathom.js"></script>
    <script>
      $(function(){
        $('#presentation').fathom();
      });
    </script>
    <style type="text/css">
      .slide {
        width: 800px;
        height: 600px;
      }
      .slide .content {
        width: 800px;
        height: 600px;
        background-color: #202020;
        border-radius: 10px;
        font-family: Helvetica, Arial, sans-serif;
        font-size: 20px;
      }
      h1 {
        font-family: Georgia, Times New Roman, serif;
        font-size: 24px;
      }
    </style>
  </head>
  <body>

    <div id="presentation">

      <div class="slide">
        <div class="content">
          <h1>Hello there</h1>
          This is a test slide. In 3D.
        </div>
      </div>

      <div class="slide">
        <div class="content">
          <h1>Hello there</h1>
          This is a test slide. In 3D.
        </div>
      </div>

      <div class="slide">
        <div class="content">
          <h1>Hello there</h1>
          This is a test slide. In 3D.
        </div>
      </div>

    </div>

  </body>
</html>

At the moment, this is a fairly run-of-the-mill presentation, except that we’ve added an extra ‘content’ div as a container inside the ‘slide’ div. This is so that Fathom.js can use the positioning of ‘div.slide’ for its calculations, while we’re free to modify any elements nested inside it.

Getting Some Perspective

Knowing this, our first step is to add a perspective to the ‘slide’ class, which is required before the browser will render transforms in 3D, and a 3D transform to the inactive slide’s ‘content’ class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.slide {
  -webkit-perspective: 600;
  -moz-perspective: 600;
  -ms-perspective: 600;
  -o-perspective: 600;
  perspective: 600;
}

.inactiveslide .content {
  -webkit-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -moz-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -ms-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -o-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
}

Now our slides are facing backwards, tilted back, rotated left, and scaled down to one-tenth size when inactive. This is a pretty good start, especially considering it’s only two extra style declarations. Obviously, we’re not finished yet since we need to transition between the two states. Let’s expand this a little:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.inactiveslide .content {
  -webkit-transition: all 1000ms ease;
  -moz-transition: all 1000ms ease;
  -ms-transition: all 1000ms ease;
  -o-transition: all 1000ms ease;
  transition: all 1000ms ease;

  -webkit-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -moz-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -ms-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -o-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);

  opacity: 0;
}
.activeslide .content {
  -webkit-transition: all 2500ms ease;
  -moz-transition: all 2500ms ease;
  -ms-transition: all 2500ms ease;
  -o-transition: all 2500ms ease;
  transition: all 2500ms ease;
}

With a few extra lines (excluding vendor prefixing), we’ve managed to set up two different transitions for activating and deactivating slides. Obviously, cleaning up this CSS with a pre-processor like SASS or LESS, or Lea Verou’s -prefix-free would be a good idea.

Easing Into It

Those of us who are used to the custom easing options when animating with jQuery might find the standard easing options with CSS transitions a little lacking. If we want to use one of the classic Penner easing methods, made famous with Flash and jQuery (with jQuery Easing Plugin), one option is to use one of my favourite online developer tools, Ceaser, which allows you to create a custom easing method or select from a set of existing well known easing equations. By selecting ‘easeInOutExpo’ from the drop down list, we can grab the CSS which provides us with our custom easing bezier curve.

If we use the custom easing in our code, it looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.inactiveslide .content {
  -webkit-transition: all 1000ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -moz-transition: all 1000ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -ms-transition: all 1000ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -o-transition: all 1000ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  transition: all 1000ms cubic-bezier(1.000, 0.000, 0.000, 1.000);

  -webkit-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -moz-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -ms-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  -o-transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);
  transform: rotateY(180deg) rotateX(-60deg) rotateZ(20deg) scale(0.1);

  opacity: 0;
}
.activeslide .content {
  -webkit-transition: all 2500ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -moz-transition: all 2500ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -ms-transition: all 2500ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  -o-transition: all 2500ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
  transition: all 2500ms cubic-bezier(1.000, 0.000, 0.000, 1.000);
}

See It In Action

You can see the final result here. Obviously this is fairly basic, but works as a great starting point to expand on the idea further.

To see another example, I recently gave a presentations at Web DirectionsWhat Do You Know event in Melbourne using these techniques called ‘Embracing Touch: Cross Platform Scroll’.

Wrapping Up

As you can see, it’s really quite simple. A presentation makes for a great way to introduce yourself to the wonders of 3D positioning using CSS. The next time you’re working on a Fathom.js presentation, why not get your hands dirty and try out some 3D transitions for yourself?

$.html5data - Creating Highly Configurable jQuery Plugins - Part 2

In my previous post on allowing users of your plugin to customise it in many different ways, I briefly touched on the use of HTML5 data attributes. If you want to provide a hash of settings from your markup, one way to do this is by writing an object literal as the value of a data attribute:

1
<div data-myplugin='{foo:"true", bar: "10"}'></div>

Even though this works, I react the same way to an object literal in markup as most JavaScript developers react to an onclick attribute.

If we are to follow the conventions of HTML when writing HTML (as we should) then each setting should have its own attribute.

The ‘data’ method in jQuery, which previously had nothing to do with HTML5 data attributes, now (as of v1.4.3) reads data values from the DOM. For example:

1
<div id="foo" data-foo="true" data-bar="10"></div>

These options can be turned into a single hash by calling $(‘#foo’).data(). The problem is that we have now introduced another form of the global namespace problem. All plugins that use HTML5 data attributes have the potential to conflict with the attributes from another plugin, especially if some of the settings are particularly generic. It’s not hard to imagine more than one plugin using ‘data-width’ or ‘data-height’.

To counter this, we are encouraged to namespace our data attributes, so ‘data-width’ becomes ‘data-myplugin-width’. The problem is that by following best practices, we essentially break the useful functionality that jQuery’s ‘data’ method provides us:

1
<div id="foo" data-myplugin-foo="true" data-myplugin-bar="10"></div>

Becomes:

1
2
3
4
{
  mypluginFoo: true,
  mypluginBar: 10
}

This is no longer in a format that we can merge with our settings through ‘$.extend’ since the property names won’t match.

At this stage we could write some custom code to iterate over the object, filter out anything that doesn’t begin with ‘myplugin’, then strip off our namespace and make ‘Foo’ and ‘Bar’ lower case. But this is way too much work to do regularly and ideally should be available to us in the form of a jQuery plugin.

This is where the $.html5data plugin comes in. Unlike jQuery’s ‘data’ method, it is designed from the ground up to work with HTML5 data attributes. Because it isn’t overloaded to read and write data in memory stored against DOM elements, like the ‘data’ method is, it is able to have an API better suited for data attributes.

Using the previous example, if you wanted to get an object containing all the settings that use the ‘myplugin’ prefix, $.html5data lets you specify the namespace:

1
$('#foo').html5data('myplugin');

This would return the following object, no matter which other data attributes were set:

1
2
3
4
{
  foo: true,
  bar: 10
}

Visit the $.html5data project page for more information or contribute on GitHub.