Refactoring Code in to an Object-Oriented Paradigm #4: Extendible, Readable, Descriptive

This is article number 4 of a series of article, which simply document my refactoring of some code I originally wrote (pretty poorly) last year. To get the jist of what’s going on. Check the original post – Refactoring Code in to an Object-Oriented Paradigm.

Making code Extensive, Readable and Descriptive

We’re nearly there with the Javascript, the worst has been cleaned up generally. Now we’re getting on to being developer-friendly, which is pretty important, you want people to extend your program. Even if you want to have control over the way they interface with your program, you still want people to interface with it somehow.

There are a few ways you can make things easier for developers, we’ve reorganised the code in the previous step, but it’s an important step, just by putting the constructor code up the top, you can suddenly look at the code and understand it just by scrolling down the page once. At least, that’s the idea, but sometimes it doesn’t hurt to actually explain what a method is doing, or what a specifically tricky part of the code does. This is why we use comments above each function, and in places actually within the code.

Finally, while it’s not quite keeping exactly the same functionality, we’re going to implement a system which allows other functions to listen for certain events. This means there’s a properly implemented way of extending our code. To ease our compatibility stage at the end, I’m going to use a custom function for custom event handling, that I’ve developed myself.

/** AutoScrobbler is a bookmarklet/plugin which extends the Universal Scrobbler
*   web application, allowing automatic scrobbling of frequently updating track
*   lists such as radio stations.
*   This is the constructor, injecting the user controls and starting the first
*   scrobble.
function AutoScrobbler() {

var userControls = "<div id=\"autoScrobbler\" style=\"background: #FFFFFF; border-top: 1px solid #000000; border-left: 1px solid #000000; position: fixed; bottom: 0; height: 50px; width: inherit;\">"+
"<input id=\"autoScrobblerStart\" type=\"button\" value=\"Start auto-scrobbling\" onclick=\"autoScrobbler.start();\" /> | <input id=\"autoScrobblerStop\" type=\"button\" value=\"Stop auto-scrobbling\" onclick=\"autoScrobbler.stop();\" />"+
"<p><span id=\"autoScrobblerScrobbleCount\">0</span> tracks scrobbled</p>"+
document.querySelector("#disclaimersContainer").innerHTML += userControls;
this.startElm = document.getElementById("autoScrobblerStart");
this.stopElm = document.getElementById("autoScrobblerStop");
this.loopUID = -1;
this.lastTrackUID = undefined;
this.scrobbled = 0;
this.countReport = document.getElementById("autoScrobblerTracksScrobbled");
this.evtInit(["addLatest", "loadThenAdd", "start", "stop"]);
this.listen("addLatest", this.reportScrobble);


/** Hashing function for event listener naming. Similar implementation to
*   Java's hashCode function. Hash collisions are possible.
*   @param toHash The entity to hash (the function will attempt to convert
*                 any variable type to a string before hashing)
*   @return A number up to 11 digits long identifying the entity.
AutoScrobbler.prototype.hasher = function(toHash) {

var hash = 0;
toHash = "" + toHash;
for (var i = 0; i < toHash.length; i++)

hash = ((hash << 5) - hash) + hash.charCodeAt(i);


/** Custom event initiator for events in AutoScrobbler.
*   @param eventName The name of the event. This may be an array of names.
AutoScrobbler.prototype.evtInit = function(eventName) {

//Initialise the evtLstnrs object and the event register if it doesn't exist.
if (typeof this.evtLstnrs == "undefined")

this.evtLstnrs = {"_EVTLST_reg": {}};

if (typeof eventName == "object") {

for (var i = 0; i < eventName.length; i++) {

var event = eventName[i];
this.evtLstnrs[""+event] = [];


} else

this.evtLstnrs[""+eventName] = [];


/** Custom event listener for events in AutoScrobbler.
*   @param toWhat A string specifying which event to listen to.
*   @param fcn A function to call when the event happens.
*   @return A boolean value, true if the listener was successfully set. False
*           otherwise.
AutoScrobbler.prototype.listen = function(toWhat, fcn) {

if (this.evtLstnrs.hasOwnProperty(toWhat)) {

//Naming the function so we can remove it if required. Uses hasher.
var fcnName = this.hasher(fcn);

//Add the function to the list.
var event = this.evtLstnrs[toWhat];
event[event.length] = fcn;
this.evtLstnrs._EVTLST_reg[toWhat+"->"+fcnName] = event.length;
return true;

} else

return false;


/** Custom event listener trigger for events in AutoScrobbler
*   @param what Which event has happened.
AutoScrobbler.prototype.trigger = function (what) {

if (this.evtLstnrs.hasOwnProperty(what)) {

var event = this.evtLstnrs[what];
for (var i = 0; i < event.length; i++)




/** Custom event listener removal for events in AutoScrobbler
*   @param toWhat A string to specify which event to stop listening to.
*   @param fcn The function which should no longer be called.
*   @return A boolean value, true if removal was successful, false otherwise.
AutoScrobbler.prototype.unlisten = function(toWhat, fcn) {

var fcnName = this.hasher(fcn);
if (this.evtLstnrs._EVTLST_reg[toWhat+"->"+fcnName) {

var event = this.evtLstnrs[toWhat];
var fcnPos = this.evtLstnrs._EVTLST_reg[toWhat+"->"+fcnName];
event[fcnPos] = void(0);
delete this.evtLstnrs._EVTLST_reg[toWhat+"->"+fcnName];

return true;


return false;


/** Starts the auto-scrobbler, scrobbles immediately and schedules an update
*   every 5 minutes.
AutoScrobbler.prototype.start = function() {

autoScrobbler.loopUID = setInterval(this.loadThenAdd, 300000);
autoScrobbler.start.disabled = true;
autoScrobbler.stop.disabled = false;


/** Stops the auto-scrobbler, ends the recurring update and zeros the required
*   variables.
AutoScrobbler.prototype.stop = function() {

this.lastTrackUID = undefined;
this.loopUID = -1;
this.stop.disabled = true;
this.start.disabled = false;


/** Loads the new track list using Universal Scrobbler and schedules a scrobble
*   of the latest tracks 30 seconds afterwards.
AutoScrobbler.prototype.loadThenAdd = function() {

setTimeout(this.addLatest, 30000);


/** Selects all the tracks which have not been seen before and scrobbles them
*   using Universal Scrobbler.
AutoScrobbler.prototype.addLatest = function() {

var tracks = document.querySelectorAll(".userSong");
this.lastTrackUID = (typeof this.lastTrackUID == "undefined") ? tracks[1].querySelector("input").value : this.lastTrackUID;

//Check every checkbox until the last seen track is recognised.
for (var i = 0; i < tracks.length; i++) {

var item = tracks[i];
if (item.querySelector("input").value == this.lastTrackUID) {

i = tracks.length;
this.lastTrackUID = tracks[0].querySelector("input").value;

} else {

item.querySelector("input").checked = true;




/** Updates the user interfaces to reflect new scrobbles.
AutoScrobbler.prototype.reportScrobble = function() {

this.countReport.innerHTML = this.scrobbled;


// Create a new instance of the AutoScrobbler.
autoScrobbler = new AutoScrobbler();


There’s quite a few changes to the structure of the code here. You’ll notice that the code now reads in the same way as the code would be used. When you run the bookmarklet initially, you’d run the constructor code, which then calls start(), which in turn calls loadAndAdd() and so on.

The only exception to this is the stop() command, starting and finishing methods like these are often grouped at the top of the code, to make it clear to the programmer what the basic functions are. As stop() doesn’t use any method which hasn’t been seen further up the code, it also won’t confuse the programmer, as it’s clear how the method works, without knowing anymore about the code.


You’ll also notice that each method has a comment to go with it, and in certain places, helpful hints have been added in the code. This can be very important for your code, as your way of thinking about the architecture of the code is very rarely the same as someone else’s way of thinking. Comments communicate in natural language, what the function or method’s overall goal is, they should generally not dissect the code and describe each operation (this can be done within the code if really required).

The way that I’ve written them is one of several loose standards for commenting. This style is similar to the way comments are written in Java. It is often useful to write comments in a common standard, as developers will be able to understand them more immediately, but other programs may also be able to understand them. There are many programs which have been written to interpret comments like these into formatted technical documentation (The type that you’d see on the MDN, or Oracle Docs), saving you a great deal of time and effort and being a neighbourly developer to anyone that wants to look, use or edit your code.

I’ve used a few of these programs myself, I’ve had most success with Natural Docs (for Javascript, though it has support for other languages), PHP Documentor (A bit of a fiddle, but made great PHP documentation) and Oracle’s own Javadoc (For Java of course).


You will also notice that I’ve added three new methods, two of which help me and other developers extend the code in a documented way. The alternative to this would be for other developers to rely on the variables and functions to remain the same in name and the action they perform, this is a bad idea. The two new functions (listen() and trigger()), allow any other functions to hook on to the function/event that they want to know about and have a function called when the function/event happens.

By using this system, events can be named independently of the internal workings of the code, which means more stability for us and other developers.

The third function is a demonstration of this implemented extensibility, in the constructor method, I start “listening” for the “addLatest” event with reportScrobble(), which for now just signifies that the addLatest() method has run and calls reportScrobble() to update any user interface. The event gets triggered simply by calling this.trigger(“addLatest”).

I haven’t included a “removeListen” function here, as it gets quite complicated in pure Javascript, but this would also be possible, and would allow code to stop listening to our events.

Note (07/02/2013)

I have since added a remove and a evtInit function to the event listener mechanism (which I have added above in the code). It has slightly complicated the implementation and you’ll notice a new hasher function which is used to name functions in a way that they can be identified later. I decided that it’d be better to have a custom implementation which was at least complete (often an issue with custom implementations). The rest of the code here is unchanged (except for the initiation of events), I have in fact refactored my own code, making sure that all existing functionality remains.

I have further researched native implementations for custom events, which I haven’t used because of issues with compatibility (the final article in the series will have more on this). If you are interested in using these native methods, Sitepoint has a pretty good article for the basics.

In terms of refactoring our Javascript, we’re just about done. We now have a far cleaner implementation, which is now a lot more developer friendly. But for all the code, there’s a user interface, and everything about it has been neglected thus far, so next we’ll be looking at Cleaning Up Your Messy User Controls.


Javascript: addEventListener and element.event

A quick post on the differences between addEventListener and element.event.

addEventListener and element.event, on first appearances, do the same thing. They allow a custom function or action to be linked to a javascript event, which are usually triggered by user-input.

But in more detail they do in fact have differences and for this reason, they should be used and treated differently. Each case where either is required should be assessed to make sure that it’s the best option of the two.

Using both methods

To use an addEventListener for the mousedown event, for example, we would do this.

var mouseInput = function(e) {
//filter the mouse inputs you're looking for
//do something based on the mouse input

document.addEventListener('mousedown', mouseInput, true);

The alternative element.event method would look like this.

var mouseInput = function(e) {
//filter the mouse inputs you're looking for
//do something based on the mouse input

document.onmousedown = mouseInput;

That’s it, and in most cases, both those will do the same thing…

The Exception to Explain the Difference

However, the methods are actually doing different things behind the things.


addEventListener assigns an additional function or action to carry out when the event happens. The clue is in the name really, addEventListener suggests that it won’t overwrite or override any other actions. This is generally a good thing, scripts, extensions and the browser itself will have assigned things to certain events and it may make the User Experience (UX) poorer if those normal functions and actions don’t happen when those specific events happen.

However, it also means that if you are purposely trying to change default actions to in fact improve the UX, this method won’t work “out-of-the-box”. An example of this is if you’re writing a web app and you want to force your own right click context menu*. You won’t surpress the context menu by default.

*A note on this idea: This is something you should carefully think about, it can certainly be useful, but many core browser operations can be found in the default right-click menu, and many extensions place a menu item to enable them to work effectively. It’s best to only use it on certain parts of the web page/app if you really are going to do this. It is very inadvisable to use this technique to stop people copying your content, there are other ways to take your content and it will often detriment genuine, non-malicious users.


element.event is an absolute assignment, it scraps and effectively destroys anything that was meant to happen when that event occurred and instead replaces it with yours. When put like that, it’s a pretty selfish way of coding as, again, it is likely to detriment the overal UX. Posts about making element.event assignments less problematic were commonly seen around a decade ago. But as addEventListener has become more supported over the years (it has been in the w3 spec for years and had support in Netscape 6(!), but only gained full support across the browsers when IE9 came in), and full featured libraries such as jQuery it has been less of an issue.

However, element.event has it’s place. It has much wider support. It’s likely that you’ll be able to support browser versions you’ve not even considered because this method of registering event listeners has been around since the beginning of Javascript (it’s fully supported in Netscape 2 as part of DOM 0…). So if compatibility is a real issue further than it is for most projects, and you can’t use a library (which will more than likely have this support built in), then this is your method to go for.

It also is quicker, and in some respects, a little easier to read, so if you’re writing a proof of concept or a quick script, where you know you won’t be interfering with anything unintentionally. Then this is also the method to choose.

The Best of Both

It is now accepted that using addEventListener over element.event is generally a better way to do things. You won’t be mucking about with the pre-assigned functions and actions for that event, which means that you’ll provide a better, more consistent UX overall.

However, if you actually are looking to surpress default actions using addEventListener, it is possible. This final example demonstrates how.

var mouseInput = function(e) {
//filter the mouse inputs you're looking for
//do something based on the mouse input

document.addEventListener('mousedown', mouseInput, true);

It should be noted that this example is actually a very bad idea if implemented, as it will stop any pre-assigned click functions. If you were trying to prevent the default context menu from appearing on right click, you should replace mousedown with contextmenu to do this correctly. See my (*) about this above.

Further Reading

 StackOverflow thread that brought these differences to my attention
 Further information on element.event registration
 Article from 2004 on making a cross-browser addEventListener (for the onload event) function for full browser support
 Article from 2001 about how difficult it was to code cross-browser event handlers (not much has changed sadly…)
 Mozilla Developer Network page on both event registration methods
 QuirksMode article on DOM Level 0 (the first version of DOM, used in the first iterations of Javascript)