This post is part 26 of 33 in the series Programming by Stealth

After our brief division in the previous instalment, it’s time to get back to learning new things. We’ll learn about data attributes – a mechanism for embedding data into HTML elements.

We’ll also revise what we learned about defining our own object prototypes to start including prototypes in our APIs.

Finally, as a practical worked example, we’ll build a better clock API for Allison’s website. Each Sunday she streams the live recording of her podcast from podfeet.com/live at 5pm at her house. To avoid timezone confusion, Allison would like a clock on that page that shows the current time at her house.

As usual I’ve packaged all the files needed for the worked example into a ZIP file which you can download here.

Solution to PBS 24 Challenge

The challenge at the end of the previous instalment was to write the contents of the function pbs.renderClock() in the file pbs.renderClock.js so it renders a clock showing the current time in an arbitrary timezone. A file named pbs24-assignment.html was provided to test your code with, but did not need to be altered. The test file will only work if it’s in the same file as the contrib folder, which contains the Moment.js library. For completeness, I’ve included the Moment.js files (in the contrib folder), pbs24-assignment.html, and my suggested pbs.renderClock.js in this instalment’s ZIP file.

Below is my suggested pbs.renderClock.js. Again, if your code is different to mine but works, it is no less correct.

I want to draw your attention to two points within my solution.

Firstly, the use of closures within pbs.renderClock(). Three variables are created to represent the parts of the clock ($hours, $separator & $minutes), and they are created in pbs.renderClock()‘s scope. The function for updating the clock is defined within pbs.renderClock(), so, thanks to closures, it retains access to these variables permanently.

Secondly, my code uses a jQuery function we have not seen before – .fadeTo(). This function gradually shifts the opacity of a DOM element to a given value over a given amount of time. The first argument is the time to take for the fade in milliseconds, and the second the target opacity as a number between 0 and 1 inclusive (0 is fully transparent and 1 is fully opaque).

HTML Data Attributes

It is possible to save data into a HTML tag using an attribute with a name that starts with data- and then has a name of your choosing. For example, you could put the SKU of a product into a listing doing something like:

Data attributes should be named in all lower case, and different parts of the name separated by dashes e.g. data-unit-price not data-unitPrice.

jQuery and Data Attributes

Using jQuery, you can both read and write data attributes. The only slight confusion is that jQuery follows the HTML5 spec, and applies a mapping to data attribute names, converting them to camel case. So, the raw HTML data attribute data-unit-price becomes unitPrice in jQuery.

The jQuery for interacting with data attributes is .data(). The first argument is always the name of the data attribute (in the short camel case format). If there is no second argument, the function returns the current value of the data attribute, if there is a second argument, then the function use it as the new value for the data attribute.

For example, given the following HTML snippet:

You could access the SKU of the dongle with:

And you could set a new SKU on the widget with:

When setting data attributes in HTML, you are limited to setting string values, but when you use jQuery, you can add any value at all to a data attribute, including references to objects.

An Improved Design Pattern for APIs

To see data attributes in action, and, to remind ourselves how to create our own prototypes, we’ll build a better clock API that allows arbitrarily many clocks to be added to a single page by building a prototype. Before we start our API, that let’s remind ourselves of how we build a basic prototype:

Given that prototype, we could interact with it like so:

Now that we know about self executing anonymous functions and namespaces, let’s update that template to make it adhere to more best-practices.

We can now interact with our updated prototype like so:

I want to draw your attention to the start and end of the self-executing anonymous function:

When we define the function, we say that we will name the first argument pbs, the second $, and the third undefined. When we call the function we only pass two arguments, pbs, and jQuery.

The first argument is exactly like we have seen before, we pass the namespace, and we use the same name for it within the anonymous function.

In our previous examples, there were no other arguments, so what is going on with the other two?

When ever you use the jQuery library, it is always presented as a function object named jQuery. By default, the variable named $ is assigned equal to jQuery. This default can be over-ridden. It is possible to use jQuery, without $ existing. Obviously, $ is much shorter to write, so, it would be nice to be able to safely use $ within our API’s code. That is what the second argument achieves. When defining what we will refer to the arguments as within the anonymous function we name the second argument $, but when calling the function, we pass jQuery.

Finally, there are bad developers in this world, and they sometimes do silly hacky things, like, defining a variable named undefined. When you assign a value to undefined, you effectively re-defining undefinedness. To be absolutely sure undefined really is undefined within our function, we name the third argument undefined, and then only pass two arguments.

Worked Example – a Better Clock API

Armed with our improved API design pattern, and our knowledge of data attributes, let’s built a better clock API.

The main features of this new API will be:

  • Object oriented code – we will define a prototype to represent clocks.
  • For each clock, a reference to the object representing it will be added to the span element containing it using a data attribute.
  • It will be possible to set the timezone for clocks in the HTML through the use of data attributes.
  • It will be possible to have clocks automatically initialise when the page loads.

The final code will be included below, but let’s built it up piece-by-piece. It’s good practice to validate data passed to the functions in your API, so let’s start by defining some data validation functions.

Our API relies on jQuery, so we should write a function to test if a value is a reference to a jQuery object:

Our API transforms single HTML span elements into clocks, so we also need a function to check if a given value is a reference to a jQuery object representing exactly one span element:

Since the whole point of this API is to support clocks in any timezone, as also need a function to check that a given value is a valid timezone specifier. What matters is not so much that the timezone makes sense to humans, but, that the timezone makes sense to the API our code will rely on for dealing with time – MomentJS.

The MomentJS API provides a function moment.tz.names() which returns an array of all valid timezone names as strings. For a value to be a valid timezone, it must be a string, and, it must be in the array returned by moment.tz.names().

We could loop through the entire array returned by moment.tz.names() each time we need to test a value, but that would be very inefficient. Instead, this is a good opportunity to see a very common technique in action – so-called lookup tables.

A lookup table is simply a plain object where every valid string is a key that maps to the value true. Once that lookup table exists, you can check if a string is valid in a single step – if the value maps to true in the lookup table, then it is valid.

Consider the following simple lookup table:

We can now write a function to test if a given string is a day if the week like so:

Using this approach, we can build a lookup table of all valid timezone, and then write a very efficient validation function like so:

With that groundwork laid, let’s write the constructor for our world clock prototype. Because our API transforms HTML span elements into clocks, the first argument to the constructor must be a jQuery object representing a single span element. Clocks also need a timezone, but we can be a little more flexible there. We should allow the timezone to be specified as a second argument to the constructor, but, we should also allow the timezone to be directly specified within the HTML of the <span> tag using the data attribute data-timezone. Finally, we can have a default timezone if none is provided by either of the possible mechanisms – I’ve chosen London, because that’s where Greenwich is. Because there are multiple possible sources of the timezone information, we need to decide on their order of importance. I’ve chosen to give the constructor the highest precedence, then the data attribute, and then the default.

Here’s the code for the constructor for the pbs.WorldClock prototype:

Notice that we made use of our previously defined validation functions to make sure all is well before we initialise the clock. When we’re sure everything’s in order, we empty the span element and then append the needed inner span elements for the various components of the clock. To allow access to the object representing the clock via the span element that contains it, we add a reference to the object (this) into the span as a data attribute.

Finally, we call the .start() function on our newly built object to start the clock running. At this point in the code we have not defined that function yet, but we’ll get to it shortly.

Clocks built with our prototype contain one piece of data that should be made accessible to users of the API – the timezone. To allow users to get and set the timezone of any clock, we should add an accessor function to the prototype:

The last thing we need before we can write the function to start our clock running is a function to render the current time into a clock. I’ve chosen to use a private function to do this work, rather than a function that’s part of the prototype. Because this function is not part of the prototype, it can’t make use of the special variable this for accessing the internals of a clock. Instead, we need to pass the clock to be rendered as an argument.

We can now add function for starting and stopping clocks into the prototype:

Finally, let’s add the ability to automatically transform spans into clocks. As a first step, let’s build a function to search one or more containers for spans with the class pbs-worldclock-auto, and turn each of them into a clock:

Now that we have a function for scanning parts of a document for clock spans and automatically initialising them, let’s add an event handler to the document to automatically initialise clocks when the page loads:

We have all the pieces for our API now, so let’s put them all together to form a complete and documented API:

You can see the API in use in pbs26.html:

The HTML file will only work if it is in the same folder as the contrib and lib folders from the ZIP file. Assuming you extracted the ZIP into your local web server’s document root, and that your local web server is running, you should be able to see the example in action at http://localhost/pbs26/pbs26.html. Alternatively, you can see it in action on my web server.

You can generate the public documentation for this API by opening a terminal in the folder you extracted the ZIP file to, and running the command:

jsdoc lib/pbs.WorldClock.js --destination docs -c jsdoc.conf.json

You can generate the developer documentation, including all the private variables and functions, with the command:

jsdoc lib/pbs.WorldClock.js --destination docs-dev --private -c jsdoc.conf.json

Assuming you extracted the ZIP file into your local web server’s document root, and that your local web server is running, you should now be able to access the public documentation at http://localhost/pbs26/docs/, and the developer documentation at http://localhost/pbs26/docs-dev/. Alternatively, you can access both sets of documentation on my web server: public docs & private docs.

In this example we initialise the first clock ourselves by explicitly calling the constructor of our pbs.WorldClock prototype, while we allow the second clock to be automatically initialised. In the first case we pass the timezone to the constructor as an argument, but in the second case we never call the constructor ourselves, so we can’t do that. Instead, we specify the desired timezone directly in the HTML using a data attribute.

We did not just use data attributes to allow a timezone to be specified, we also had the constructor add a reference to the object representing a clock into the span that contains it using a data attribute. This linkage can be useful, for example, you could enter the following in the console to stop clock 1:

We can later re-start it with:

A Challenge

The API above is functional, but not very configurable. To make it more useful, add the ability to configure the following options:

  1. Time format – 12 or 24 hour
  2. Whether or not to show seconds
  3. Whether or not to blink the separator(s)

Each of these options should be configurable in three ways – via data attributes, via the constructor, and via accessor methods.

Finally, create two functions, pbs.WorldClock.stopAll() and pbs.WorldClock.startAll() to allow users to easily stop and start all the clocks on a page. For bonus credit, can you write the functions such that they accept a jQuery object as an optional argument. If the argument is present, only the clocks contained within elements represented by the jQuery object should be stopped or started, and if the argument is not present, all the clocks in the entire document should be stopped or started.

Feel free to use your own namespace for your version of the library. If you choose to do that, it would be good practice to acknowledge where the original code came from in your documentation.

Final Thoughts

At this stage we’ve learned how to define the structure of web pages with HTML, to style them with CSS, and alter the structure of web pages with JavaScript. We’ve learned to use APIs written by others, and, to write our own APIs, either for private code re-use, or, for sharing with the world.

So far, we’ve omitted an entire facet of the web – user input. Web forms allow users to enter information, and to trigger events. We need to learn the HTML markup to define them, the CSS to style them, and the JavaScript to bring them to life – that’s where this series is heading next.