Namespaces, duplicate functions and more IE nightmaresI was working on some code the other night which belonged to a site similar to my own in which numerous scripts and
"semi frameworks" were being included. By
semi framework I mean an
add-on script that includes numerous functions that will almost certainly be replicated elsewhere in the site
(DOM manipulation, event handlers etc).
The code involved using an iframe and then
running an onload event as soon as the content had loaded so that I could
resize the iframe to the correct dimensions for the content within. I had a bit of code like this:
addEvent(document.getElementById('myIframe'),"load",resizeIframe);
// resize iframe content onload
function resizeIframe(){
var dc = S.getIframeDoc(this); // return iframe document
var h = dc.body.scrollHeight; // get height of document within iframe
this.height = h+30+"px"; // set height of iframe+30 to ensure no scrollbars
}
As you can see the function
resizeIframe which is called when the iframe loads uses the
this keyword to reference itself rather than getting a reference to itself using
getElementById which is fine as long as you are using a proper browser that supports the
DOM 2 event model. However as
IE does not support this model it has a problem with the
this keyword in that it references the
global window object instead of the iframe.
However this is a well known problem and many a solution has been created to get round this issue in
Internet Explorer. On the site in question I use the following function which as you can see handles
DOM 2, IE's event model and the older DOM 0 event model. It also correctly handles the
this keyword problem by storing a reference in the DOM to the function. Read up on
PPKs event handler content for more details.addEvent = function( obj, type, fn, cp )
{
if(obj){
if(obj.addEventListener){
cp = cp || false;
obj.addEventListener( type, fn, cp );
}else if ( obj.attachEvent ) {
obj[type+fn] = function(){fn.call(obj,window.event);}
obj.attachEvent( 'on'+type, obj[type+fn] );
}else{
var ev='on'+type;
var oldevent = obj[ev];
if (typeof oldevent != 'function'){
obj[ev]=fn;
}else{
obj[ev] = function(){ oldevent();fn();}
}
}
}
}
However when testing the site in
Internet Explorer I was getting errors when trying to reference the
contentWindow.document in my resizeIframe function saying its
null or not an object.The reason being that the
this keyword was referencing the window object and there is no such object property on the
global object.I spent some time scratching my head and looking over the function at hand and then when I put some
debug code into the
addEvent function and noticed it not appearing it at all the problem suddenly made sense. After a quick search through the
other JS files being included on the page in question I came across the following function in a script called
sorttable.js used for sorting table contents.
function addEvent(elm, evType, fn, useCapture)
{
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
}else if (elm.attachEvent){
var r = elm.attachEvent("on"+evType, fn);
return r;
} else {
var o = elm[evType];
if(typeof(o)=="function"){
elm[evType] = function(){o();fn();}
}else{
elm[evType] = function(){fn();};
}
}
}
As you can see its another
addEvent function and one that doesn't handle the
this keyword problem in IE.As this script was being included after my own file it was
overwriting the previous addEvent function and therefore was the cause of the error.
Now this self taught lesson reminded me of a recent post I wrote about the amount of
duplicate frameworks and semi frameworks being included on websites at the moment. I reckon that on this page in question there was at least
5 different places where a function to add an event listener was being declared:
-My own
addEvent function in my standard library
strictly.js-The
addEvent function in the
sorttable.js script
-
JQueries own methods to bind events - different names but doing the same action.
-
GooglesAJAX library would surely have its own event handlers.
-
AddThis add-on also had a similar event handler.
Therefore 5 scripts all doing the same thing. I explain it more in my earlier post:
http://blog.strictly-software.com/2009/08/large-number-of-duplicate-frameworks.htmlwhere I discuss the possibility of a
standard global API for implementing these sort of functions that are nearly always replicated in
add-on scripts. This would allow developers to choose the
framework to handle this type of work and
add-on developers wouldn't have to worry about implementing duplicate code.
Another solution would be if the
add-ons would separate their code out into 2 files. The first file would contain the
core functionality and then the other file would contain the functions that
could/should be handled by the main framework used by the site.
This would allow the site developer to
reduce the size of their codebase as they could choose to remove the 2nd file (if they wished) and allow their
primary framework to handle the work that a lot of
add-ons duplicate such as
DOM manipulation and event handling.Obviously this works only if the
add-on is using the same naming convention as the many frameworks and if the
frameworks don't even share the same
naming convention then this would seem hard to achieve. However we could always use
wrapper functions or aliases to
point the add-on functions at the desired framework objects e.g for our
addEvent function:// addEvent used by this script
// params supplied
// obj = object, type = event, fn = function
// set alias so that add-on can use sites primary framework e.g jQuery/Prototype
A = addEvent = function(obj,type,fn){
$(obj).bind(type,fn);
}
Which allows the add-on to
add event listeners with a call to
A or addEvent with either call just being a
pointer to the frameworks bind method. The add-on provider could either provide these alias wrappers themselves or just
outline the interface required and allow the
developer to implement the code.And of course if the
user of the add-on didn't use a framework then they could choose to utilise the 2nd files functions as is even if they were duplicated in 5 other places.
This is all just ideas at the moment but I am
developing an add-on at the moment that is going to try this approach.
Why Namespaces are such a good ideaAnother solution to the problem of
duplicated functions is to always use
namespaces to define your functions and objects so that there is little if no chance of two functions having the same name. The writer of the
sorttable.js file didn't use a namespace to define his functions but if I had of defined my own
addEvent function like so instead:
Strictly.addEvent = function(obj, type, fn){ ...
// call like so
Strictly.addEvent(document.getElementById('myIframe'),"load",Strictly.resizeIframe);
I would not have had any problem working out why my function wasn't working as expected unless one of the other
add-ons also had used the
namespace Strictly!
Obviously
using namepaces may solve the problem of functions being overwritten but it doesn't solve the issue of
duplicated functionality.Object Comparisons in Internet ExplorerNow whilst I was scratching my head in relation to the this problem I looked into creating a little test function that would tell me whether
this equated to the global object or not as I was going a bit offtrack and of course a simple
this === window should do the trick. However whilst looking into the different methods of
object comparision I came across some of the differences between
browsers of how
object comparison is carried out. I don't know why I thought
IE wouldn't be the odd one out as it always is but you might find the results interesting or you may not.
Make sure you view the test page in
IE and a standards compliant browser like Firefox for comparison so that you see what I mean:
www.strictly-software.com/thistest.htmwindow === window.top is
true in Firefox and
false in IE.
window == document is
false in Firefox and
true in IE.
var _w=window;
_w === window.top is
true in Firefox and
false in IE.
When
this relates the the
global window object:
this === window.top is
true in Firefox and
false in IE
this === self is
true in Firefox and
false in IE
This may all be old news as far as
JS developers are concerned but it was news to me so I thought I would post the link anyway.