Web Developer?… writing and application?… want the front end to be badass?… welcome to the club.

I’ve been working with JavaScript for roughly 4 or 5 years at this point. My first apps, if you can even call them that were randomized ads being server from an array on my buddies web site once his traffic picked up. It was not only my first attempt at JS, but also one of the early programming lessons of my life. With that said I’m old school, I can write an AJAX request by memory, I can tell you about the days of old using invisible iframes and having inter page communication between objects to do essentially what XMLHttpRequest does now (yes I know, using them is still a key to many apps). With that said doing a good ole getElementById(‘x’) never phased me in the least. The idea of building a DOM structure with document.createElement and document.appendChild…


var heart = document.createElement('heart');
chest.appendChild(heart);

With that said, I could continue down my route and stick to my ways, but a man that stands still in his knowledge is a man that gets left behind. So I’ve started researching JS frameworks and the pros and cons of these now necessary evils/saints.

I personally fell for jQuery. Clean syntax, ease of use, functionality… all of these things put it leaps and bounds ahead of it’s competitors in my book. Looking over Prototype.js markup usually makes me walk away from the computer grumbling about a nose bleed and makes me smoke a cigarette. With that said I’m not your typical JavaScript programmer from my experiences working with others in this field. I feel that a good JS program much like any other should consist of “Objects” since the language is considered OOP, or atleast some what I tend to take advantage of that. For more complex applications I’ll write a series of objects with self contained functionality to control multiple facets of an application. An example of this would be a web based jabber client I wrote about a year ago.

The interface and most every functionality was controlled via a series of objects. I’ll give a short example of what I mean.


function Chat()
{
this.connected = this.establishConnection();
etc.
}

Chat.prototype.handleMessage = function(ejabberdPacketObject)
{
var chatInstance = this.activeChats[ejabberdPacketObject['username']];
if(!chatInstance)
{
this.activeChats[ejabberdPacketObject['username']] = new Conversation(ejabberdPacketObject);
etc.
}
}

function Conversation(incomingConversation)
{
this.username = incomingConversation['username'];
this.appendChatLog(incomingConversation['message']);
}

Conversation.prototype.appendChatLog = function(msgText)
{
msgText = this.username+" : "+msgText;
this.chatLog += msgText;
etc...
}

For all of the fans of Object Literal Notation, I know I’m a sinner, but I really do believe prototyping objects is the way to go because of readability and memory management. I’ll make it up to you and write the next piece in OLN.

So long story short I’m working on a dynamic Pedigree builder, it’s rather simple really, it needs to build out the HTML DOM dynamically adding generations to a pedigree. But I figured I would use jQuery to ease some of the extra markup overhead of appendChild and getElementById calls plus get all the fancy fancy shiny flashy effects as well. I ran into an interesting issue that gave me a headache for a few hours so I wanted to present it to whoever may be running into the same issue and propose my fix.

See when I do development I like to store references to my DOM elements in Object properties to prevent looking over the DOM again plus it just makes it easier to grab DOM references in context.


this.myDiv = $('#gen1');
this.addGenBtn = $('#gen1 input.add_generation');

Using that with jQuery would fetch the input with a class name of ‘add_generation’ inside of the DIV with the ID ‘gen1’. Which is great, we get the correct element back and all the extra functionality supplied by jQuery including the ‘click()’ functionality… I know I know I could use ‘onclick’ but as my Grandpa says “If you’re gonna get wet, might as well go swimming”… basically what I’m saying here is if you’re using the framework, might as well implement it wherever you can. So now that we have the reference in place we can do stuff like this.


Pedigree.generations[5].myDiv.append('stuff');
Pedigree.generations[5].addGenBtn;

Another habit of mine is also passing the reference of the parent object to the DOM node itself for cross reference purposes. This way the DOM node can “phone home” easily and react to user information and interaction accordingly. Such as this.


this.addGenBtn.Parent = this;

Now this may seem kind of clumsy but I’ll show you why I do it personally.


function Generation()
{
this.myDiv = $('#gen1');
this.addGenBtn = $('#gen1 input.add_generation');
this.addGenBtn.Parent = this;
this.addGenBtn.click(this.addGeneration);
}
Generation.prototype = {
addGeneration:function()
{
this.Parent.myDiv.append(this.Parent.blankGenTemplate);
}
};

See now the functionality from an event also has a reference to it’s parent properties and methods. This can be very very handy in the long run. And this is where the problem set in. When the ‘click’ event handler fired, yes, it passed back the associated DOM element as expected. Meaning if I placed a console.log() call and logged ‘this’ to firebug I was seeing the DOM reference I expected. However, any properties and methods I added to that DOM object were neutered.


...
addGeneration:function()
{
console.log(this); // returned input DOM node...
console.log(this.Parent); // returned 'undefined'
}
...

Now me being an old school developer I’m used to saying “getElementById” and getting back exactly what I expected, any new methods or properties I add to that element will reside there until otherwise noted. So this threw me for a loop, but a simple loop that I was looking at from the wrong angle. See jQuery hands back an Object of it’s own when you “query” the DOM. This is what leads to all the fancy added functionality, so although it appears you’re getting back just your DOM node with some extra methods slapped on the end you’re really receiving and jQuery object with your DOM reference tucked neatly inside.

Long story short should someone run into something along the lines of this and require a cigarette much as I did when there is a very simple, very quick, and very easy fix… I almost don’t feel like even publishing this article because I know some people are going to go “well no shit dumbass” but I feel like there might be someone out there that could use this information. SO here it goes.

When you “query” the DOM as mentioned above it returns a custom jQuery object so appending methods/properties to that object does indeed append them, but it does so in the jQuery Object scope which could indeed be useful in some cases. However when using the event controls such as ‘click()’ the scope returns the event reporter. This is why in the example above logging ‘this’ outputs the input element, but it doesn’t see a .Parent reference. It doesn’t see this reference because we technically never put it in place. The trick to fixing this is damn simple though.

See when a jQuery object is returned it’s much like a prototyped/extended array. If you check the object out you’ll see it has a .length property and THIS is where it stores the raw DOM references. So basically using it like an array is the trick.


this.addGenBtn = $('#gen1 input.add_generation');
this.addGenBtn[0].Parent = this;
this.addGenBtn.click(this.addGeneration);

Now in that example we grabbed the first node of the array which is the raw DOM element to create our .Parent property. Now we can use the idea’s stated above without skipping a beat. Also note that if your “query” returns more than one item you can easily loop over the object just like an array and achieve the same idea like so.

this.divControls = $('div.collapsible');
// loop over DOM elements and reference parent object
for(var a=0,z=this.divControls.length; a(lt*)z ; a++)
{
this.divControls[a].Parent = this;
}
this.divControls.click(this.handleCascade);

*(lt) = word press won’t allow a less than because of template munge.. so yea, sorry.

So, with everything we’ve discussed here, I now give you an example (non functional) of the kind of functionality I was trying to achieve.


// pedigree class
function Pedigree()
{
this.init();
}
Pedigree.prototype = {
generations:[],
init:function()
{
// hook up DOM and such
}
},

// generation class
function Generation(parentGen)
{
this.init(parentGen)
}
Generation.prototype = {
init:function(parentGen)
{
this.parentGen = parentGen;
this.masterPedigree = this.parentGen.masterPedigree;
this.generation = this.parentGen.generation+1;
this.buildDOM();
this.hookupDOM();
},
buildDOM:function()
{
// create interface, skipping these
},
hookupDOM()
{
var divName = '#gen'+this.parentGen.generation+'-'+this.generation;
this.myDiv = $(divName);
this.addBtn = $(divName+' input.add_generation');
this.addBtn[0].Parent = this;
this.addBtn.click(this.addGeneration);
},
addGeneration()
{
this.Parent.masterPedigree.push(new Generation(this.Parent));
}
}

Hope this helps anyone else that runs into scoping/object confusion using jQuery later on. If anyone has any questions or comments I’d love to hear them. To the JS dev. community… I know my ways might seem a bit unorthodox so I’m sure I’ll get nice and crucified.

If anyone is going to the Ajaxian conference in Boston next week (Sept. 29th – Oct. 1st) and would like to hang out and shoot the shit feel free to drop your email here and I’ll get in touch.

For those interested in learning more about jQuery jQuery homepage.

Once again, hope this helps, and sorry it’s so damn long 😉

Advertisements