Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Tuesday, June 19, 2007

Scalable vs. Extendable

Working with a client this last week I realized that there was a difference between scalable applications and extendable applications, and that many people do not see the distinction. When designing a widget, I told the client that it would be scalable, meaning the widget could have as many instances and permutations (in a specific format) as they needed. However, I did not designed it in a way that allowed easy extending of the widget (addition of more features). The client believes that extendability is part of scalability. Allowing this difference of opinion was an oversight on my part, because scalability is a nebulous concept and could be seen to include extendability. However, in practice scalability and extendability can both be very time consuming elements of your project and will effect the bottom-line of a project. Therefore, if you think like me and believe they are different (and consequently quote prices accordingly), make sure your client’s understand the difference.

In my case, I went ahead and gave the widget limited extendability on my own time (to meet the client’s basic needs) and told them if they needed anything more we would need to re-negotiate the terms.

posted by Matt Snider at 3:36 pm  

Friday, June 15, 2007

Introducing Core JavaScript File and Namespace

Today’s post is going to introduce the file that provides the core functionality for my personal JavaScript Framework. I have already introduced several parts of the file, such as type detection and cookie management. You can view the code @ http://mattsnider.com/corejs.

The first thing you’ll probably notice is that I comment everything. This is the most important part of programming, and often overlooked in Web Applications. You almost can not have enough comments in any given file. In my case, I write so much code and work on so many different projects on a daily basis that without comments, I would be lost. But beyond that, if I wrote a file (of any reasonable length) 4 months ago and revisit it now, I am probably going to have to reacquaint myself with what it is doing (and most likely improve it a little). Figuring out what is going is exponentially faster when you comment your code. It’s best to comment: all functions, large blocks of code, anything that might be confusing, and all hacks.

Now, back to ‘Core.js’. What is does this all do? First, you will see a section that sets up some W3C standard constants, which should be attached to the ‘document’. These constants will be used anytime you want to compare the ‘nodeType’ of a DOMElement. Older browsers and IE do not define these by default, so this block of code fixes that. Note that I do not use browser detection to figure this out, but instead see if the variables are defined in the ‘document’ namespace. Always try not to use browser detection if you can.

/**
 *  W3C DOM Level 2 standard node types; for older browsers and IE
 */
if (null == document.ELEMENT_NODE) {
	document.ELEMENT_NODE                   = 1;
	document.ATTRIBUTE_NODE                 = 2;
	document.TEXT_NODE                      = 3;
	document.CDATA_SECTION_NODE             = 4;
	document.ENTITY_REFERENCE_NODE          = 5;
	document.ENTITY_NODE                    = 6;
	document.PROCESSING_INSTRUCTION_NODE    = 7;
	document.COMMENT_NODE                   = 8;
	document.DOCUMENT_NODE                  = 9;
	document.DOCUMENT_TYPE_NODE             = 10;
	document.DOCUMENT_FRAGMENT_NODE         = 11;
	document.NOTATION_NODE                  = 12;
}

An example of how you might use this is:

var isTextNode = function(node) {
	return document.TEXT_NODE == node.nodeType;
}

So, if you pass a DOMElement into the Function ‘isTextNode’, it will return ‘true’ if the node is a text node.

The next block of code defines the ‘Core’ Object and namespace. I say namespace, because the Core Object will be the global Object that the Framework will be attached to. I use the ‘module’ pattern as coined by Douglas Crockford to create my Core Object. This gives me the freedom to create ‘private’-like variables: ‘debugLevel’ and ‘clientInitFunc’. Debug level is a variable I use to differentiate between production and development messaging. ‘clientInitFunc’ also uses the ‘module’ pattern, creating the template that will evaluate the user’s browser and OS. This is also a logical place to attach cookie management Functions. Here are some references for these methods:

Again, let me reiterate, “DO NOT USE BROWSER DETECTION, UNLESS ABSOLUTELY NECESSARY”. That said, the two browsers you might have to detect most often are IE and Opera, so I have two shortcut methods: ‘isIE’ and ‘isOpera’ that compares against the Core.browser variable.

The next statement returns the constants and methods attached directly to the Core namespace. These include additional namespace variables: Biz, Debug, Client, Widget, and Util. Each of these have been commented to explain what to use them for. I frequently use Core.Client and therefore have attached the Client Object created during the initialization of the Core Object to Core.Client for ease of access.

The next two Functions are helper methods that did not belong in the global namespace, nor any other namespace, so I have attached them to Core. ‘deepCopy’ copies an Object or Array and all of its member Objects and Arrays until all children have been copied. I need this from time-to-time to prevent Objects from being passed by reference, when I do not want a Function to modify the passed element. ‘getImage’ is a short-cut method to create an ‘Image’ instance and then attach the URI src to the image Object. This allows pre-caching of images and simplifies code.

‘getDebugLevel’ is used to retrieve the debugLevel variable. I have designed it this way, because I want to ensure that I (or a hacker) can never update the debug level directly once the script is loaded into the page. Lastly, the ‘init’ Function initializes some internal ‘Debug’ variables and initializes the Core.Client Object.

Now, the ‘emptyFunction’ Function and ‘Class’ Object are copies of similar elements in the ‘Prototype’ Framework. They probably should not be in the global namespace, but i have not had a good enough reason to move them yet. ‘emptyFunction’ is handy, because it allows you to supply a generic do-nothing method. The ‘Class’ Object, can be used to create ‘Prototype’ Objects, but is something I try to stay away from. I have only one instance in the Form Validator that benefited from this design pattern, and so it remains. Also, the amount of code is negligible and some people may prefer to build ‘Prototype’ Objects.

The file closes with all the type detection functions. These are described in detail for the Type Detection article.

Although, short and sweet, this file has some complex ideas and may take a little getting used to. Please let me know your thoughts and any possible improvements, especially with the ‘deepCopy’ method.

posted by Matt Snider at 8:57 am  

Thursday, June 14, 2007

More on Frameworks

A List Apart Magazine recently posted an article on Frameworks. This mostly talks about CSS Frameworks, but does a great job describing Frameworks as a concept.

frameworksfordesigners

posted by Matt Snider at 10:50 am  

Wednesday, June 6, 2007

Cookies

Cookie Monster

Well, unfortunately, not the kind you can eat (sorry, cookie monster). I was explaining cookies to someone in a forum today and thought I would share my thoughts.

Cookies are a good way to store temporary information about a user on their computer as long as they have them enabled. Fortunately, most people who have JavaScript enabled also have cookies enabled, so we will not worry about accessibility too much. Cookies have the advantage of being stored client-side in the user’s temporary internet files, so they are computationally cheap to set and retrieve. However, you should never store any private data in cookies or use them to access any private systems, as cookies are hackable and easy to forge. That said, many login systems rely on cookies to store a temporary session key; cookies are a well established technology and can be powerful if used right.

I often use cookies with tabs or toggles so that when a user switches pages and returns to the page with my widget the last state is remembered (this will be discussed at some point in the future). For the sake of today’s article, here are three simple function that I use as part of my Core package.

var createCookie = function(name, value, days) {
	var expires=null;
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires="+date.toGMTString();
	}
	else {expires = "";}
	document.cookie = name+"="+value+expires+"; path=/";
};
//
var eraseCookie = function(name) {
	Core.getClient().createCookie(name, '', -1);
};
//
var readCookie = function(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') {c = c.substring(1,c.length);}
		if (c.indexOf(nameEQ) == 0) {return c.substring(nameEQ.length, c.length);}
	}
	return null;
};

I do not remember where I originally found them from, so I cannot take credit, but I have used these functions for a long time.

posted by Matt Snider at 10:44 pm  

Monday, June 4, 2007

Prototype vs. YUI Round 2: I love $

Probably the most commonly needed Function for a Web Application Developer (WAD), is document.getElementById. As you will most certainly need to retrieve HTMLElements from the DOM by the element’s ID. Using ‘getElementById’ is perfectly valid, but has potential drawbacks such as:

  • ‘document.getElementById’ is verbose
  • multiple elements cannot be retrieved with one simple call
  • Some browsers, especially old browsers, do not support this call.
  • The more IDs you use in your document, the longer the seek time of this method.

As the title says, I am in love with the ‘$’ (I am also in love with money, in case you were wondering). The ‘$’ function was first developed by Prototype (and IMHO is Prototype’s greatest contribution to the JavaScript community). My love for the ‘$’ function is two-fold:

  • It is elegant, simple, and concise
  • Using the $ symbol as a shortcut, should be safe and most people did not know this was possible (so it is cool)

But beyond those points, let us really consider what an element retrieval function should and should not do:

Dos

  • Process lists of elements as well (both arguments list and arrays)
  • Handle all x-browser issues and old browsers, or degrade nicely
  • Caching. It is faster to maintain an array for all previously retrieved IDs, than to retrieve them each time
  • Do nothing when passing an HTMLElement (it should be acceptable to use ‘foo= $(foo)’, just to ensure that is an HTMLElement

Do Nots

  1. Attach a bunch of variables and/or functions to the HTMLElement*
  2. DO NOT do item 1

* This is my single biggest gripe with pretty much all JavaScript Frameworks. Why do each of my HTMLElements need a slew of helper functions that are already available to me elsewhere? I am not that lazy and frankly it is expensive to add Function references to every HTMLElement that I retrieve. Plus: you increase your chances of circular references causing memory leaks, different Frameworks and Toolkits may conflict with each other, and some future version of JavaScript may define that same function.

That said, let us move on to comparing YUI ‘YAHOO.util.Dom.get’ vs. Prototype ‘$’. I have created an simple rating system that I will be using for this and future discussion. I will walk through each Function and rate them using a point system, where you get plus points for each ‘Dos’ (ex. +1) and minus points for each (ex. -3) ‘Do not’ used. Here is the newest prototype ‘$’ function from version 1.5.1:

Prototype ‘$’ Function

function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
{elements.push($(arguments[i]));}
return elements;
}
if (typeof element == 'string')
{element = document.getElementById(element);}
return Element.extend(element);
}

I am happy in this release that they allow you to pass a list of arguments and retrieve multiple HTMLElements at the same time (+1). I am going to give the Function (+1) for using the ‘$’ name. However, it does not allow you to pass in an array as an argument, which is important because sometimes you will have gotten your lists of IDs from a method that created an array of Strings (+0) and will be required to use a work around to pass these Strings into this Function. The Function does retrieve arguments that are Strings and returns the non-string Objects (hopefully HTMLElements) (+1). Unfortunately, the method attaches needless and dangerous methods to the HTMLElement that you pass or retrieve (-3). It does not work with old browsers or degrade well (+0) and has no built in caching system (+0). This gives Prototype an overall rating of (0), which is pretty poor.

YUI ‘YAHOO.util.Dom.get’ Function

get = function(el) {
if (!el) { return null; } // nothing to work with
//
if (typeof el != 'string' && !(el instanceof Array) ) { // assuming HTMLElement or HTMLCollection, so pass back as is
return el;
}
//
if (typeof el == 'string') { // ID
return document.getElementById(el);
}
else { // array of ID's and/or elements
var collection = [];
for (var i = 0, len = el.length; i < len; ++i) {
collection[collection.length] = Y.Dom.get(el[i]);
}
//
return collection;
}
//
return null; // safety, should never happen
}

As with Prototype you are allowed to pass multiple arguments at the same time (+1) and although you cannot pass in an array of arguments, you can pass an argument that is an array, which is more powerful (+1). There is no ‘$’ function short-cut (+0), but I always map this method to the ‘$’ symbol anyway, so it is really not a big deal. The function does retrieve arguments that are Strings and returns the non-string Objects (hopefully HTMLElements) (+1). It does not attach needless/dangerous methods to the HTMLElement (-0). Unfortunately, it does not work with old browsers (+0) (it does degrade well, by return ‘null for unexpected values) and has no built in caching system (+0). This gives YUI ‘get’ Function an overall rating of (+3), which is average, however, if you map the ‘$’ symbol to the ‘get’ Function, then it would bump the rating up one, making YUI ‘get’ Function slightly above average.

Therefore, using my metrics, YUI’s ‘get’ Function is better than Prototype’s ‘$’ Function. I recommend using the YUI one and mapping it to a function named ‘$’ in the global space, emulating Prototype. Not only will you be able to use the concise ‘$’ symbol to retrieve HTMLElements, but doing this will allow you to use YUI with some of Prototype-based Toolkits and Frameworks, without the down-side of using Prototype. Obviously, there is a little room for improvement, as there is no caching yet and there is not a work around for browsers that do not support the ‘document.getElementById’ method. However, 95%+ of internet users (with JavaScript enabled) will have a browser that supports ‘document.getElementById’ (and this number increases every year), and caching gives a marginal improvement only when repeatedly retrieving the same element using the ‘get’ Function (and may be detrimental to performance if you use your own caching system).

Lastly, I want to leave you with a slightly modified YUI ‘get’ function that I use. I use YUI’s namespace approach and attach everything to an Object named ‘Core’, which I later shortcut key methods (such as ‘get’) as part of my JavaScript build process. Here is my method (it will be made available as part of my Core.Util.Dom package after I have further discussed DOM manipulation:

Core.Util.Dom.get: function() {
var multiArgs = (1 < arguments.length); // arguments is not an Array
var elem = multiArgs? arguments: arguments[0];
//
// nothing to work with
if (! elem) {
return null;
}
//
// assume is a DOMElement, but could be any Object
if (! isString(elem) && ! isArray(elem) && ! multiArgs) {
return elem;
}
//
// ID
if (isString(elem)) {
return document.getElementById(elem);
}
// array of ID's and/or elements
else {
var collection = [];
for (var i = 0, j=elem.length; i collection[i] = Core.Util.Dom.get(elem[i]);
}
return collection;
}
}

posted by Matt Snider at 3:08 pm  

Powered by WordPress