I will be using Stephen Chapman’s Countdown Timer on About.com. There is a fair number of countdown timers available on the Web, with most counting down to a very specific holiday or event, and that are limited in scope. I chose Stephen’s because it offered some great flexibility, but could still use some improvements. The script is not unlike many you may come across while searching for a tutorial to jump start the process. Below you will find Stephen’s original script, modified slightly for readability.
Deleting what is unwanted
One of the first things you need to do before refactoring is to remove anything that will not serve your purpose. Most of this code is necessary for the functionality of our countdown timer, but there are two utility functions that we will not need. This is the start and loaded function that are tightly coupled to the window.onload event. In your application you will likely want to bind functions with an event handler, so for the purpose of this tutorial we will use a basic onload event in order to eliminate confusion.
Letting your pattern emerge
Now that you have only what you need, you need to figure out whether or not this functionality will play a small role in the big picture, or if it will be the lead star. I will assume this is one of many features that will appear in your application, and that you need to first write a draft or stub (as some programmers like to call it) of your own custom micro library. You will discover quickly that this emerging pattern very closely mimics the Module Pattern described in depth on the Yahoo! User Interface Blog. Take a look at the stub below, and start to think about how it might apply to the countdown timer script before moving forward.
At this point in the design the stub appears quite cryptic compared to the initial countdown timer. Remember though, you are planning to add other features and functionality unrelated to the timer, so we need to make room for those scripts as well. Initially, the code is nothing more than a namespace blueprint, placed within the global JSANT object. You need to provide access to public properties and methods, and also accommodate private utility properties and methods that are only visible to these public properties and methods. Below is an example of what this access would look like.
Getting into the guts
Now you can begin to build upon this blueprint, and stub the properties and methods that you need for the countdown timer. Stick with what has been provided, and suppress the desire to rename functions or variables. If you get into a jam, it will be easier to compare apples to apples. Get your global variables into the JSANT object, and get into the guts of the old script to pull out the necessary functions that will act as your methods.
You might be wondering how to know if a method should be public or private. A good rule of thumb is to think of a public method like you would a foreman on a home construction site. The foreman is responsible for taking all the requirements (parameters) from the home owner (user agent), and delegating responsibility. There are carpenters, electricians, and bricklayers, all with unique responsibilities, working behind the scenes (private methods). Although foremen will help occasionally with the heavy lifting, their primary responsibility is to make sure the homes display properly when the owners (users) come for a showing.
Beginning to refactor
This is where the process can get interesting. I prefer to move all the code into the appropriate “slots”, and then refactor the code only as needed to get it functional. The first rule of thumb in refactoring old scripts is that at this juncture, you are not trying to gain efficiencies. You simply want it to work as-is, or with minor edits.
In the original script, setCountdown was a public function. Because this function only performs a utility task, I decided it was better suited as a private method. The displayCountdown now only accepts the ID of the element that will display the countdown timer. However, we still need to perform the task that sets the initial time, so I created the countdn method. The countdn_value property will hold this decremented value of time in seconds after it is initialized. I also modified the tz property to represent EST.
Gaining efficiencies — refactoring on steroids
Technically, we could leave our script alone. We no longer have global variables, we took advantage of the Modular Pattern, and our script performs as expected. Other developers could use it on pages throughout the application without having to know anything other than the ID of an element in the DOM. Yet, after looking closer, you might have noticed that this script is not very flexible. Every developer can only use one version, which will perform a single countdown, with a single set of parameters.
What if you wanted multiple countdowns, possibly on an ecommerce Web site? One will countdown to Christmas, and the next will countdown until the end of a sale, or until the hour when the last order will ship for that day. You get the point. Take a look at the final iteration of this script, and notice some of the efficiencies — most notably the elimination of the countdn method and countdn_value property. These are no longer needed, because time can be tracked for each countdown through a key/value pair added to the params object. This is a commonly known trick for passing data around without requiring each function to know all the parameters.