Behavioral Action Model - Part Three
In part two, the conceptual underpinnings of the behavioral action model were discussed. We took a simple function from part one and coaxed it into a class then encapsulated that class into an Object. A loader function on the body tag’s “onload” method would then do real work of binding elements to their respective functionality.
In this tutorial, we are going to gloss over that actual mechanics of how the loader operates except to say that it is keyed to CSS selectors. They are effectively the glue between the element, its action and the supporting functionality.
Let’ start by resurrecting the simple page from part one:
<html>
<head>
<title>My Test</title>
<script type="text/javascript" src="link.js"></script>
<link rel="stylesheet" type="text/css" href="goLink.css"/>
</head>
<body onload="link();">
<button class="goLink" id="mybutton">My Page</button>
</body>
</html>
First, we need to modify this page slightly. Instead of loading the Link class directly, we are going to use a loader class, namely “Behaviour“. This class has a few methods available, of which, we are most interested in the “apply” and “register” methods. The “register” method simply informs the Behaviour script of an Object that contains an action; while the “apply” method performs the necessary parsing and binding between our “bam_classes” Object and the page’s DOM.
Since these file are external files, includes are necessary which brings up a couple of important issues, nomenclature and file organization. Regardless of your preferences, be very consistent and well organized. In this tutorial, I will place scripts and css into their own directories. With that in mind, we are ready to make some modifications. First, the web page code needs to include the “Behaviour” class and our “bam_classes” class which can be found in the “bam.js” file.
<script type="text/javascript" src="scripts/js/lib/behaviour.js"></script> <script type="text/javascript" src="scripts/js/actions/bam.js"></script>
An important browser note here, be sure to use the full tag notation for these types of includes instead of the self-closing notation. Some, not mentioned here, browsers continue to have quirky support for this notation when applied to includes.
Next we need to use the “apply” method of the Behaviour class as our loader in the body’s “onload” method:
<body onload="Behaviour.apply();">
While we’re making modification to the page, let’s move the css into its own directory as well. With all of this finished, the original page now looks like the following:
<html>
<head>
<title>My Test</title>
<script type="text/javascript" src="scripts/js/lib/behaviour.js"></script>
<script type="text/javascript" src="scripts/js/actions/bam.js"></script>
<link rel="stylesheet" type="text/css" href="css/bam.css"/>
</head>
<body onload="Behaviour.apply();
<button class="goLink" id="mybutton">My Page</button>
</body>
</html>
Our next modification is the creation of the “bam.js” which is essentially our “bam_classes” Object from part two saved as a file. However, there is an important addition, the inclusion of a call to the “register” method from the “Behaviour” class. This call makes our object known to any calls to the “apply” method.
For brevity sake, here is the “bam.js” file as it now stands.
var bam_classes =
{
'button.goLink #mybutton' : function(element)
{
element.onclick = function() { };
element.onmouseover = function() { };
element.onmouseout = function() { };
}
};
Behaviour.register(bam_classes);
Before you cry foul and tell me that it’s not exactly like part two’s “bam_classes”, let me agree. I have good reasons for stripping out the code from the onclick method and space padding the id token used in the key. In its current state, the “Behaviour” class has a parser flaw that is unable to resolve an id delimiter (#) that has no preceding space which is part of a larger css selector; namely “tag.class#id’ needs to be ‘tag.class #id’ instead). A minor inconvenience, but one that needs some attention.
Digressing briefly, I need to discuss the “bam_class“, or more specifically - action files. Action files contain the “binding action to our functionality”. Therefore, the container Object should be as clean and simple as possible. It is very tempting to pour all the actual functionality code into the various methods within a class. However, it is not required to have the code reside here and in my experience a mistake to do so. Consider the Object as a holder for prototypes of functions defined elsewhere. This helps clear the clutter and confusion of mixing control code with execution code.
So where do I place the code? You have two choices really, in the same file as the Object (but not in the Object) or as an external file or files. For simple applications where the functionality will only be used with the action file, then in the same file is really convenient. If you plan to have a library of functions that exists and are usable independent of the behavior actions, then store the code outside the action file. Again, this is purely dependent upon you personal preferences of course.
For these considerations, I stripped out the code from within the methods in preparation of placing it into an outside
declaration, in effect “stubbing” the prototype. Using the onlick method from our Link class, we now have:
element.onclick = function() { do_click(this); };
Fundamentally, the “do_click(this)” is just a function that takes an argument. The function can be placed anywhere really, we’ll place in the action file. There is now an argument “this” which is a Javascript reserved word. It is similar to other object oriented languages and provides a self contained reference to the current Object, in our case, the Object reference that created the event to which our action is attached. My digression has ended for the moment and I should demonstrate what the action file “bam.js” contains.
var bam_classes =
{
'button.goLink #mybutton' : function(element)
{
element.onclick = function() { do_click(this); };
element.onmouseover = function() { do_mouseOver(this); };
element.onmouseout = function() { do_mouseOut(this); };
}
};
Behaviour.register(bam_classes);
/** Supporting function definition **/
function do_click(reference)
{
alert('The button was pressed");
}
function do_mouseOver(reference)
{
// nothing yet
}
function do_mouseOut(reference)
{
// nothing yet
}
With the actual method code pulled out into their own definitions, we are free to code up whatever we need without entangling the control logic. You have virtually complete freedom to manipulate the DOM, do client-side validations, make Ajax calls, or whatever needs to be accomplished.