Showing posts with label prototype. Show all posts
Showing posts with label prototype. Show all posts

Sunday, 4 October 2009

Build your own framework

Creating your own framework

Lets build our own mini framework. Not to replace the many brilliant frameworks already out there but to educate ourselves in how we would go about such a task. Many of you maybe using JQuery, Prototype. YUI or MooTools but have no idea how that code actually works and looking at the source code may just put you off wanting to know. Therefore this article is going to show you how we can create our own basic framework which we could extend and use ourselves if we so wished or we could build it just for fun as a way to improve our JavaScript programming knowledge as well as helping us understand why frameworks are built the way they are.

If you want to jump straight in and have a look at the full source code first then you can download the latest release of the code from here: http://www.strictly-software.com/scripts/downloads/getme-1.0.4.js


Aims of our example framework.

1. Picking an eye catching name

All the current major frameworks have great names that are easy to remember such as JQuery, Dojo and MooTools.

I am going to call this framework Getme for the simple reason it Gets me what I want. I already use a function in a lot of my code including Strictly.js called getEl which has a short name of G so Getme seems like a logical progression.

Also I am currently in the process of building an AJAX based add-on that is based on accessing remote content and reformatting it for use on websites in various ways so the name Getme.js seems to be in keeping with the aims of this add-on which will no doubt implement this code or a variation of it when complete.

One of the things I tend to do when creating a codebase or function libaray is give all my important functions two names. The first name is the more descriptive one such as Getme and the second one is usually a single capital letter which I will use to reference the function once the code has been compressed with my compressor tool e.g

G = getEl = function(id){ return document.getElementById(id); }

2. Main Functionality

The codebase is a very cut down version of a framework designed to show how you would go about creating your own as well as helping us understand what's going on when we look through the source code of other popular frameworks therefore the functionality will be very minimal. However it would be nice if we could do the following:

1. Return DOM nodes in a variety of manners using the same function. Similar to how JQuery lets you pass in CSS-Selectors to the $ function.

My function will do without the onDOMLoad function that JQuery supports with its main constructor when an anonymous function is passed in but it will offer the ability to return nodes in a variety of ways including by passing the constructor an #ID, .className, <tag>, a P:first-child > SPAN CSS Selector or an HTML string which can be converted into a node list.

2. Enable chaining so that the result of a previous DOM manipulation can be passed to subsequent functions without having to pass the previous functions result as a parameter into the next function e.g

// instead of having to do this
Getme.setHTML(Getme('DIV > P.myClass'),"<p>New HTML</p>");

//we could do this
Getme('DIV > P.myClass').setHTML("<p>New HTML</p>");

Which would set the innerHTML of any Paragraphs with a className of myClass that are descendants of DIV tags to have the value <New HTML>

3. Make use of the CSS 3 Selectors that all the major frameworks use to return DOM elements e.g

DIV#myID > P.myClass:nth-child(3)

4. Make use of internal loops so that a method applied to the result of a selector acts on all the nodes returned for example:

// set the class to "blue" and set some style properties for all elements that match the selector e.g all P tags that are descendants of DIV's
Getme('DIV > P').setAtts({class:"blue",style:"color:red;font-weight:bold"});

5. The ability to extend our object very easily by passing another object to a method that will add any properties and functions from one object to the other.

That's all I want this example code to do which if you think about it is pretty powerful stuff anyway. The code format is loosely based on JQuery and if you have ever looked at JQueries source code and wondered what the hell is going on then this example might help you get your head round it.


Starting to build our Getme.js framework - The Wrapper function

Now you may have seen in JQuery and other frameworks or functions that the entire code is wrapped in a self calling anonymous function like so:

(function(){

// set some shortcuts to optimise references to global vars
var window = this, // pointing window to this (which refers to the global object) speeds up references to window

undefined, // this creates an undefined variable we can use to test other undefined variables against

query = !!document.querySelectorAll;

// rest of code e.g object definition, functions and methods
})()

Now this self calling anonymous function ensures that any objects defined within it are created and returned when the function runs. The open and close () brackets at the end of the anonymous function ensure this. Plus any variables declared within the function are only accessible inside the anonymous function which means we are not polluting the global namespace. To learn more about this modular method please read the following article:

http://yuiblog.com/blog/2007/06/12/module-pattern/

As you can see there are a few variables declared within this function that to the beginner may see pretty odd e.g
var window = this,

undefined,
How can you set window to be equal to this?

Well the this keyword is a special keyword that refers to the context object and defaults to the global context (window) when used outside an object. If I create an instance of my Getme object and use the this keyword this inside a Getme method then it refers to the current instance of the Getme object. Used outside any object it will refer to the window object.

If you don't know much about the this keyword I suggest reading up about it as there is a well known problem in Internet Explorer where the this keyword refers to the Window object in certain cases when it shouldn't such as when event listeners are added using IE's attachEvent function. A recent blog of mine detailed one such problem that explains this issue in detail: http://blog.strictly-software.com/2009/09/trouble-with-this-keyword-and.html

If you define a function outside a namespace then you would also be able to reference it by prefixing it with window e.g a function called myFunc could be called as window.myFunc. Outside objects and functions where scope has been passed with a call or apply method the this keyword refers to the global namespace therefore setting window = this is a way of accessing the global namespace quicker than referring to it through the keyword window.

The other variable that is declared but not set to anything is undefined. Because the variable hasn't had a value set it's basically undefined and this means any comparison tests with other variables can be speeded up by comparing the variable with this undefined variable rather than having to do an equality test such as:
var myvar;
if(myvar == "undefined") alert("undefined");
if(myvar === undefined) alert("undefined");
The first test has to convert myVar to a string whereas the second test compares one undefined value against the other which is much quicker.


Now to the Getme.js source code

As most DOM manipulation involves the returning of one or more nodes from the DOM the aim of this object is to enable quick retrieval of DOM nodes by various methods. The main object will be called Getme and its primary property will be an array called this.nodes which will hold the current set of nodes. For functions that only return one node element such as getElementById then this node array will contain one item located at the first index e.g

this.nodes[0];

To enable the chaining of our object methods together each method will return a reference to the Getme object after any DOM manipulation has been carried out.

Lets add some code to instantiate our Getme object which we will use as our primary method of returning DOM nodes. We will also give it a short cut name of G (for Getme) so we can access DOM elements in a similar way that JQuery and the others use $ instead of jQuery.

// the G function is a short cut to instantiating with Getme object
G = function(sel, context){

return new Getme(sel, context);
}

// the Getme constuctor pass in a selector/context
// can handle multiple forms of selector
// ID - #ID to return element by id e.g G('#myID') 
// CLASS - .myClass to return elements by className
// TAG - <P> or <SPAN> to return all elements of a certain node type
// SELECTOR - DIV P SPAN to return all elements that are SPANS decendants of P who are decendants of DIV
// NodeList - <DIV><SPAN>hello there</SPAN></DIV> will return a nodelist containing the elements specified by the HTML passed in
Getme = function(sel, context){  

this.Getme = "Getme version 1.0.4",

this.nodes = this.nodes || [],

this.context = this.context || document; // default context to document

// main regEx to determine if selector is HTML string, ID or classname
var re_getme = /^<([^> ]+)[^>]*>(?:.|\n)+?<\/\1>$|^(\#([-\w]+)|\.(\w[-\w]+))$/i,

// regEx to match <SPAN> <P> <H1> tags
re_tag = /^<([a-z1-9]+?)>$/i,   

match;

// if no selector passed in default to document
if(!sel) sel = document;

// if we have been passed a string then this could either be
// an id of an element, a class name, a tag or an HTML string
if(typeof(sel)==="string"){

// look for a single HTML tag e.g <P> or <SPAN>
match = re_tag.exec(sel);    

if(match && match[1]){

// return all nodes matching the tag      
this.nodes = this.context.getElementsByTagName(match[1]);
}else{

// run regex to look for ID, class or HTML string
// match[1] = HTML string - matching start and end tag
// match[3] = ID
// match[4] = class name
var match = re_getme.exec(sel)||[];

// if we have an ID with or without a # e.g #myid or myid treated as an ID
if(match[3]){     

// get element by id
this.nodes[0] = document.getElementById(match[3]);

// if we have a class name e.g .myClass
}else if(match[4]){

// get elements by class name
this.nodes = Getme.funcs.getElementsByClassName( match[4],this.context);

// if we have a valid HTML string e.g <P><STRONG>hello</STRONG></P>
}else if(match[1]){

// create nodes from html
var div = document.createElement("DIV");

div.innerHTML = sel;

this.nodes = div.childNodes; 

}else{

// if querySelectorAll is available for modern browsers we can use that e.g
// FF 3.2+, Safari 3.2+, Opera 10, Chrome 3, IE 8 (standards mode)
if(query && this.context === document){       
this.nodes = Getme.funcs.selector(sel);
}else{            
// otherwise revert to Sizzle which makes a good job of handling older browsers
this.nodes = Getme.find(sel,this.context);
}

}
}
}else if(sel.nodeType){

// already got a node add
this.nodes[0] = sel;

}else if(Getme.funcs.isArray(sel)){    

this.nodes = sel;

}else{

this.nodes = Getme.funcs.makeArray(sel);
}

this.length = this.nodes.length;

return this; 
}

This function Getme which can be called with the shortcut G is the key to the whole object. It accepts two parameters sel and context. Sel (which is short for selector) can be a number of parameter types such as:
  • #myID which will return a reference to an element with the ID myID
  • .myClass which will return a node list of all elements with the className myClass
  • <span> or <p> which will return a node list of all elements with the tag specified e.g SPAN or P
  • <div><span>hello</span></div> which will be used to create a node list from the HTML specified.
  • An array of elements can be passed in gained from some other method or an object hash of elements which will be converted into an array.
  • DIV#myID > P:first-child or any other CSS 3 Selector can be passed in and a nodelist will be returned if the browser in question supports document.querySelectorAll. Otherwise if Sizzle has been added to the codebase then it will handle older browsers without the use of XPath.
Also notice the internal properties this.context and this.nodes as these are very important in terms of storing the current context that any selections are being carried out against such as document or a particular node. The nodes property stores the current nodelist that any selector or function has retrieved. For example if you have just returned a list of P tags then the nodes array will hold references to these items. This allows subsequent methods to access the nodelist to do any work such as set styles, clear children etc.

The Getme function uses various different methods to return the required nodelists including document.getElementById, getElementsByTagName, getElementsByClassName and a method to return elements by CSS selectors.

In fact adding complex CSS-Selector functionality is quite a simple process and for those browsers that support it such as FireFox 3.1, Safari 3.1, Opera 10 and IE 8 in standards mode a function can return nodes matching CSS 3 Selectors with only the following few lines of code which I have placed in a method called selector:
// returns elements by selector e.g DIV P SPAN
// supported in later browsers IE 8 (standards mode), FF 3.1+, Safari 3.1+
selector : function(query){

try {
return  document.querySelectorAll( query )
} catch(e){}

return [];
}


Now we have our main function that instansiates our Getme object and returns various node sets in a multitude of ways we can add our other methods which make use of Javascripts prototype inheritance to add methods to any Getme objects instantiated with the G function call.

All of these functions will return a reference to this (or the Getme object) so that further methods can be appended to the end in a chain. An example of three of these functions are below:

// returns the specified element from the current nodeList
get : function(idx){   

 if(this.nodes && this.nodes.length>0){
  return (typeof(idx)=="number") ? this.nodes[idx] : this.nodes[0];
 }else{
  return null;
 }

},

// sets the innerHTML of one or more nodes.
setHtml : function(content){

 if(content){     
  
  // if a node has been passed in we take its innerHTML
  if(content.nodeType){
   html = content.innerHTML; 
  // otherwise use the string passed to us   
  }else if(typeof(content)==="string"){
   html = content;
  }else{
   html = "";
  }

  // remove all existing html from this node as we are re-setting it
  this.each(function(){
   
   if ( this.hasChildNodes() ){
    while ( this.childNodes.length >= 1 ){
     this.removeChild( this.firstChild );       
    } 
   }
   
   this.innerHTML = html;
  })

 }

 return this;
},

// allows Getme object to reference the static foreach function automatically passing the current node list e.g G('SPAN.myclass').each(function(){})
each : function(callback){

this.foreach(this.nodes,callback);

return this;
},

// allows the setting of style values to the current nodelist
setStyle : function(style, val){

if(style){

var self = this,

atts;

// may have passed att=val pair or a object hash for atts
if(val !== undefined){
// create object hash
atts = eval('({'+style+':"'+val+'"})');
}else{
atts = style;
}

this.each(function(){
self.setProperties.call(this.style,atts);
})
}

return this;
}


As you can see these methods are all chainable in that they can be referenced in the following manner:

G('P > SPAN.info').setHTML('Use the help icon for more info.').setStyle({color:red,fontSize:8px})

I also have added another object called Getme.funcs which holds a number of utility functions that can be called statically without a Getme object reference having to be instantiated. For example in the Getme constructor I make a call to the getElementsByClass method in the following manner:

// get elements by class name
this.nodes = Getme.funcs.getElementsByClassName( match[4],this.context);

You can also call these functions yourself without having to first create a Getme object with the G constructor e.g

// loop through an array of items in arrEls calling a function for each node in the array passed to it
Getme.funcs.foreach(arrEls, function(){ somefunc(arrEls[this] );

I also extend my Getme.prototype object with Getme.funcs once both objects have been defined. This is to allow any Getme objects to have the benefit of having these methods added to their prototype.

The .each function is a good example of a method added to the Getme object as it calls the Getme.funcs.foreach method passing in the current node list stored in this.nodes and a callback function e.g:
// allows Getme object to reference the foreach function automatically passing the current node list e.g G('<pan>').each(function(){})
each : function(callback){

this.foreach(this.nodes,callback);

return this;
}

This allows you to easily call a function for each item in a nodelist and for other methods such as setStyle or setAtts the foreach method is called internally to allow any css style values or attributes to be set for each node element currently stored in the internal this.nodes member.

Although not even nearly complete this object serves as a basic example of how a framework like JQuery operates and you should use this as a guide to creating your own DOM manipulation framework. I have added some more methods to the object which you can see from the full source code which you can download here:


You should also note that to handle CSS 3 Selectors for older browsers such as IE 5-7, FF 1-3, Safari 1-3 etc I have used Sizzle.js which is the same selector engine that JQuery uses.

You can download the version of Sizzle that I have hooked into Getme here:


This brilliant CSS selector engine which was created by John Resig makes CSS 3 Selectors available for all those browsers that don't support the querySelectorAll method available in FireFox 3.2, Safari 3.2, IE 8, Opera 10 and Chrome 3.

You will notice in my Getme constructor that I have the following branch:
// if querySelectorAll is available for modern browsers we can use that e.g
// FF 3.2+, Safari 3.2+, Opera 10, Chrome 3, IE 8 (standards mode)
if(query && this.context === document){
this.nodes = Getme.funcs.selector(sel);
}else{
// otherwise revert to Sizzle which makes a good job of handling older browsers
this.nodes = Getme.find(sel,this.context);
}

Which passes off any selectors for browsers with no querySelectorAll support to Getme.find which is a pointer to the Sizzle function. If you scroll to the bottom of the Sizzle code you will see I have hooked in my Getme object in a similar way as JQuery hooks itself into Sizzle e.g

// EXPOSE Sizzle to Getme.js

window.Sizzle = Sizzle;

Getme.find = Sizzle;
Getme.filter = Sizzle.filter;
Getme.expr = Sizzle.selectors;
Getme.expr[":"] = Getme.expr.filters;
If you wanted to use Sizzle in your own framework or DOM manipulator library then its a simple case of referencing it in your code and either calling the Sizzle functions directly or doing what JQuery and Getme does and point your own functions at Sizzles own functions.

Hopefully this article has been helpful in explaining to new developers how a DOM manipulator library could be build with similar features to other frameworks such as JQuery.

This example Getme.js is not a complete framework and has not been built to become one rather its an example of how easy it is to utilise other useful libraries such as Sizzle and add them to your own code without having to worry about all the bloat that many frameworks come with. You can start off with a basic piece of code to manipulate and search for DOM nodes and then extend it as and when you require new functionality.

Writing your own framework

People always ask me why I don't use one of the big frameworks like jQuery, Prototype, Mootools, YUI etc. My answer is that its not that I don't think their code is good although I have found numerous bugs or issues in all of them over time but rather that I prefer to write my own code because that way I get to understand the language and become a better coder.

I don't claim to be a brilliant Javascript programmer and its only within the last few years that I have developed a big interest in it as opposed to back-end coding and database development. However I know that I won't get to the level of the John Resig's, Dean Edwards and Douglas Crockfords if I always rely on others to write my code for me.

For example on a project I worked on I had to create a fading in lightbox WYSIWYG editor that could float around the screen but never leave the boundaries of the viewport. This is no easy job for a novice and I could have easily loaded up Prototype, Scriptalicious and TinyMCE, found a how to article on Google and got the code working with a few hours. However I would have no idea how the code worked and if I had to make customisations to the code I would then have to spend hours trawling through lots of code I didn't understand hacking about until my tweak was complete. Plus I would have to do it in a way that any updates to those libraries didn't overwrite my changes when future updates were rolled out.

Obviously if you have time constraints or just don't care about how things are done but just want them done in the quickest way possible this maybe the way forward for you and I don't blame you for not wanting to commit the time it would take to build a widget such as the one described from scratch. However this is what I chose to do and it involved spending some considerable time reading up about my intended task before even attempting to write a line of code for the job.

This method of development will take you some time to achieve and you will probably spend a lot of time pulling whatever hair you have left out of your head. However along the way you will learn a lot about your craft including browser differences, the history of the DOM, event models and future developments as well increasing your own skills as a developer.

The pain and time spent will be worth it as when it comes to getting a high paid job in development in the future you will have done yourself a big favour by going down the hard route. Plus the widget or application you have just built will be your own creation and you will know your own code inside out. Any bugs that need fixing will not rely on some 3rd party to release an update and you will have pride in completing a job that many others would not do.

Now I am not totally against frameworks and even use a couple on my own sites. However I do feel that people tend to use them without due consideration to what tasks they actually need to perform. If you are writing heavy DOM manipulated AJAX application that utilise the majority of JQueries features then that is the tool for you. However if you are not intending to use the majority of the features and just like the fact you can access an element by id with the $ sign then you have bloated your codebase unnecessarily.

Whether you write your own code or use frameworks you should at least spend the time looking at the code so that you understand what it is your using and how it works. Once you do this fair play but there is nothing worse in my eyes that someone who uses a framework such as Prototype or Dojo but reverts straight to a forum when they get stuck rather than looking at the source code to see what is happening under the hood. If you at least attempt to do that first and then still don't know what's going on then your well within your rights to ask for help. However at least try to figure out what the code is doing as it may take up some of your time but it may also help you understand JavaScript a little bit more.


If you want to see some ideas on how you could go about writing your own JavaScript framework then check out this article of mine which goes over the basic concepts of using CSS selectors to find your nodes, applying functions to those nodes, chaining functions together, and passing the results of the first function to the second in the chain:

http://blog.strictly-software.com/2009/10/build-your-own-framework.html

Saturday, 3 October 2009

Correctly measuring element dimensions

Obtaining an Elements correct width and height

Should be easy shouldn't it? You could check the elements style.width and style.height property but if they haven't been set by an inline style or with Javascript this won't help. You can use the browser specific style functions to return the current value but there are big differences between IE's currentStyle and the standard getComputedStyle functions.

If you want the height and width in pixels because you need the values for further computations such as positioning an element in the window then you will probably go for offsetWidth and offsetHeight and in fact most of the frameworks will resort to this method within their CSS functions if those dimensions are required. This is because even if you have specified a CSS style for the width and height in pt, em, % or any other unit the offsets will return a value in px. This is very useful as its a very easy method to get an accurate measurement that works cross browser.

Now I was working with some absolutely positioned floating DIV's earlier today and I required the dimensions of an inner DIV for use in a positioning calculation. The issue was that the floating DIV contained numerous inner DIV's only one of which was actually visible at one time. To make one of these sections appear the outer DIV was made visible and all of the inner DIVs apart from the one required to be shown were hidden. When the DIV was closed both the outer and inner DIVs were set to display:none;

I won't go into the reasons for this method apart from saying that the DIV's contained links and the reason I did it this way was for SEO so that all the links were available in the DOM server-side for bots to spider. Too many people nowadays create too much content on the fly with Javascript and forget about the fact that anything created this way is hidden from crawlers.


The problem - Measuring a DIV hidden from the flow

If you want to obtain the dimensions of an element that is currently hidden from the flow using offsetWidth and offsetHeight you must first put that element back into the DOM so that a measurement can be made. The usual method which frameworks like jQuery and Prototype use is to store the existing values for visibility, display and position, set those values to hidden, block and absolute, take a measurement and then revert the styles back to their original values e.g from Prototypes getDimensions function:
getDimensions: function(element) {
element = $(element);
var display = $(element).getStyle('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};

// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
Now this is fine if the element you are trying to measure is hiding itself from the flow but if a parent is hiding it or even multiple parents then this won't work.

For example if you have a DIV that is set to display:none that contains another DIV also set to display:none; and then this DIV also contains a DIV set to display:none; which is the one you want to measure you actually have to toggle all 3 into the flow and out again to get an accurate offset measurement.

You can see this in action on the following example page I created: http://www.strictly-software.com/testGetdim.htm

View the source and run the test which will take measurements of the DIV on the left which is always in the flow and then the 3 nested DIVs that are hidden from the flow which will only be made visible once the test has run.

As you can see from the debug JQuery has no problem at all getting the measurements of the visible DIV and the outer most one from the 3 nested DIV's. However the two inner DIV's that require parents to be toggled in and out of the flow for their offsets to be measured do not return an accurate value.


Functions to measure offsets correctly

There are 3 main functions used in this test page that return accurate offset values for the nested DIV elements. The others are just helper functions to return a computed/current/style value and output debug or return an element by id.

// returns an array of elements that need to be made visible to carry out a measurement of an element
function getVisibleObj(elem){
arrEls = []; // holds array of elements we need to make visible to measure X

while(elem && elem!==document){
var es = getStyle(elem,"display"); // method returns current/computed/style value

if(es == 'none'){
arrEls.push(elem);
}

elem = elem.parentNode;
}
return arrEls; //null;
}

// swap styles in and out for an accurate measurment. Taken from jQuery and tweaked by myself to
// handle multiple elements.
function Swap(elem, els, styles, callback){

var obj;

for(var x=0,l=els.length;x<l;x++){
// create hash on element to hold old styles so we can revert later
obj = els[x];
obj.old = {};

// Remember the old values, and insert the new ones
for ( var name in styles ) {
obj.old[ name ] = obj.style[ name ];
obj.style[ name ] = styles[ name ];
}
}

// call the function passing in any element that needs scope
callback.call( elem );


for(var x=0,l=els.length;x<l;x++){
obj = els[x];

// Revert the old values
for ( var name in styles ){
obj.style[ name ] = obj.old[ name ];
}
// delete the hash from the element
try{ delete obj.old; }catch(e){ obj.old=null}
}

}

// offsetWidth/Height is element.width/height +border+padding (standard box model)
// clientWidth/Height is element.width/height +padding (if overflow:scroll then -16px)
function getElementDimensions(el){

el = (typeof(el)=="string")?G(el):el; //return a reference to the element

var w=0,h=0,cw=0,ch=0;

// if element is currently hidden we won't be able to get measurements so we need to find out whether this or
// any other parent objects are hiding this element from the flow
var arrEls = getVisibleObj(el); //returns array of objects we need to show to meaure our element

// create function to do the measuring
function getElDim(){

// get style object
var els = el.style;

// get dimensions
w = el.offsetWidth, h = el.offsetHeight, cw = el.clientWidth, ch = el.clientHeight;

}

// do we need to toggle other objects before getting our dimensions
if(arrEls && arrEls.length>0){
// call function to swap over properties so we can accuratley measure this element
var styles = {visibility: "hidden",display:"block"};
Swap(el, arrEls, styles, getElDim);
}else{
getElDim();
}


// create object
var ret = {
"width":w, //total width (element+border+padding)
"height":h,
"clientWidth":cw, //element+padding
"clientHeight":ch
}

return ret;
}

1. getVisibleObj

This function takes an element as its parameter and then loops up any parent elements storing in an array any that have a style.display set to none. Similar function on the web stop at the first element it comes across that is hidden but if you have multiple parents all hiding their children then only a full list will do.

2. Swap

This function was taken from jQuery and tweaked by myself to handle multiple elements instead of just one. The array of elements from getVisibleObj is passed to this function along with the element that requires measuring. This is then passed to the callback function so that correct scope can be utilised not that I require it here. The first loop stores the current style properties of the elements that require toggling in a hash on the object itself. After the callback is run the element styles are toggled back to their original state and this hash is removed from the element.

3. getElementDimensions

This is the function that actually is called by a user and returns an object with the height and width obtained from the elements offset values after any toggling has been carried out.

The reason I have not toggled position:absolute on elements that need toggling is that it seems to work without me doing this plus on the actual code I was working with earlier it was actually causing an incorrect offsetWidth value to be returned in certain situations. The test page seems to work with or without this property so if you have any issues pass this into the Swap function along with display:block and visibility:hidden.

Saturday, 15 August 2009

The large number of duplicate frameworks

Living with multiple frameworks

I have just been investigating some of the source code that some of the new widgets I have added to my site lately contain. Its always good to delve into the source code of any widget or add-on as it helps you as a developer to see other ways of coding including methodology and style. Even with a compressed and packed source code you can easily reformat it to make it readable by using an unpacker tool like www.strictly-software.com/unpack-javascript.

One of the things I noticed was the large amount of duplication in functionality that my site now has due to using these 3rd party add-ons and tools. There is no doubt that these widgets offer some great functionality with little or no cost and development time and they have enhanced the sites usability. However using Googles AJAX API, jQuery, add-this and snapshots and my own core library strictly.js I now have 5 separate JS files being loaded that handle events, load scripts dynamically and other various DOM manipulation functions.

I don't know what the answer is as its too much to ask 3rd party developers to all develop their add-ons with one framework or to ask them to create multiple versions of their widget for each framework. Plus its very likely that any developer who creates a nice add-on will want to write their own code anyway as any coder worth their pay thinks they know best and in most cases they choose to encapsulate any helper functions into the add-ons core files.

Now you could spend a lot of time creating local copies of these add-ons and rewriting all the code to share as much as possible but this would be futile and take a lot of effort. Plus every time a new version came out you would have to go through the same process or otherwise stay with an old version of the software.

I was looking into a really nice looking lightwindow add-on the other day but this involved the installation of another 4 large Javascript files including Prototype and Scriptaculous. Therefore the dilemma was whether to add to the existing frameworks or rewrite the code to use JQuery instead.

This started me thinking around the possibility of some sort of CDN which any developer could access and upload to that would hold files that met the requirements of a standardised API. Developers of new add-ons instead of recreating the wheel could build their widgets using these central objects. It would then be up to the site owner to decide which version of the object he wished to use but as long as any add-ons used on their site implemented this standard API the code would all work.

For example thinking about the common need for event handling by all widgets. Say I am developing a new widget that shows previews of other sites (like the Snapshot add-on). Instead of writing new code to handle event management (add, remove, onload, DOM ready etc) I implement this standard API that has a specific naming convention. There can be multiple objects written to handle events on this CDN and they all differ in their internal workings but they all support the same interface and therefore it would be very easy for a developer to hook into them or change the core framework. An example would be that for any event object written to support this API to add an event they would follow this convention: element.addEvent(type,function,capture).

The main framework developers would all have to standardise their naming conventions if they wanted to use the API but it would mean any site owner using multiple add-ons could choose one core framework for their sites engine and as long as all the add-ons and widgets were built to use the standard API it wouldn't matter if they changed the core framework from JQuery to Prototype the site would continue to work and without the need for code duplication. 3rd party widget developers could specify in their documentation which framework they felt the widget worked best with but it wouldn't depend on that framework and a site owner would make their choice without worrying about breaking the add-ons.

It just an idea that I have just had and it would obviously need lots more thought and the support of the major framework developers as well as any 3rd party widget developers. However until the time comes when some sort of standardised central codebase is implemented and widely used it looks like code duplication and large files are here to stay. Therefore to mitigate this issue site owners will need to make sure all these files are optimised as much as possible through compression, minification, local hosting or CDNs, caching and other tweaks.

This article is a great resource for covering this in detail: http://websitetips.com/optimization/

Sunday, 26 April 2009

Should I use a framework?

When to use a framework and when to write your own code

I am always asked at work why I don't just use a framework such as jQuery, prototype, YUI, MooTools etc rather than spend time writing my own code and its a fair point. I have spent time looking at the major frameworks and its all good code written by clever people and if you haven't got the time to spend then I would definitely recommend using a library. Then again if John Reisig had thought like that then millions of people would be using YUI instead of jQuery and Microsoft would be packaging another library with Visual Studio to handle selectors instead.

Libraries are good for many reasons they hide browser incompatibilities from the developer and its good for a team of developers to stick to a standard code base rather than all adding their own functions and bloating a site up with several versions of the same addEvent or toggleClass function.

The downside is that most libraries will contain lots of code that is never even used by the developer. If you're not even going to be using selectors to return DOM objects in your JavaScript and are just looking for a shorter version of document.getElementById then using $('#blah') is not the way to go. 

The other good thing about writing your own code is that you get to understand the language of your trade a whole lot better than if you just relied on a library. There is no better way in my opinion for learning anything that being thrown in at the deep end and having to sink or swim so to speak. Yes it takes much longer as you will have to read up about the early browser wars and compatibility issues, learn about objects and their properties and understand event models and script syntax but it will make you a much better programmer and when bugs appear due to a new version of Internet Explorer you won't have to wait for an update to your framework to be released.

As with all things its swings and roundabouts and just because I like to write my own code and know why things work the way they do does not mean I won't use a library. However having spent the time researching the language for my own code has given me invaluable knowledge and it helps being able to step through something like jQuery and actually understand what its doing and why rather than just knowing that it works.