Posts Tagged ‘JavaScript’

The Roll Your Own Series From JSMag

I finally put together a page to host my articles published in JSMag for those of you who might have missed them. The articles, titled “Roll Your Own” and dealing with the proposition of creating a custom JavaScript library from scratch, will run about a month or so behind the magazine’s releases so if you like the columns go pick up the current issue here.

The ‘Roll Your Own’ columns.

Added Crockford’s JSLint to the ‘javascript-tools’ Textmate Bundle

The other day I forked the excellent javascript-tools Textmate bundle in order to add Douglas Crockford’s JSLint as an option to go along with JavaScriptLint and the Closure Compiler. You can find it here. I want to look at using Safari’s compiler in order to alleviate the Rhino dependancy, ad possibly include the newest jslint.js with a config file but, unfortunately, don’t have time at the moment. I will soon, but until then this early version at least fits in functionally and aesthetically with the rest of the bundle. Rhino is expected to be installed, as well as jslint,js to be located in ‘~/Library/JSLint’.

The enterprising can just open the bundle contents and hack away at the dclint.rb file to change any paths or dependencies. Hey look, screenshots!

Lint Menu

Added entry into the lint options menu

Jslint output

Fits into the bundle aesthetics

RML.post(#2)

Quick example of a header section of a web page that contains a few form elements done with RML:

  markup: {
    header: function() {
      var $header = RML.div({
        id:'header',
        content: RML.h1('The Chat') +
        RML.hr() +
        RML.div({
          class:'message-form',
          content: RML.textarea({
          name:'txt-message',
          id:'txt-message',
          rows:'4',
          cols:'60'
        }) + RML.input({
          type:'button',
          id:'btn-send-message',
          name:'btn-send-message',
          value:'Send Message',
        }) + RML.input({
          type:'button',
          id:'btn-clear-message',
          name:'btn-clear-message',
          value:'Clear Message',
        }) + RML.input({
          type:'button',
          id:'btn-logout',
          name:'btn-logout',
          value:'Log Out',
        })
      })
    });
    return $header;
    } //end header
  }

I’m using it here in a larger object literal method which can be called with a refinement like so:

HTML rendered by the RML methods
The rendered HTML

Get the code at github or here on the RML page. I’ll post more later this week…

Generate Markup with JavaScript

I am going to blog more in depth this weekend about this little markup generating JavaScript that I’m working on. I know there are others, but what fun is that? Basically, RML will generate tag-based markup like HTML and XML from JavaScript. I have been using it for templating alone and within a larger Ext.js application with pleasing results. I just need to add more tag shortcuts to the object, but it is very usable now. Get it here or at my github repo.

JSLint, Vim, and Windows XP

Just a quick post about integrating JSLint with Vim on a Windows XP machine. I had posted awhile back about integrating JavascriptLint with Vim on XP, but while re-reading The Good Parts I got the urge to revisit JSLint just to revel in the Crockfordness of it all…

First off, get Jesse Hallett’s excellent jslint.vim plugin here. I installed this on my Fedora box without a hitch (well, the only hitch was figuring out the name of the Spidermonkey package for Fedora 12, ‘js-devel’ I believe it was) but my work XP machine would spit this out when attempting a :jslint command:

Error detected while processing function <SNR>17_JSLint:
line  43:
E484: Con't open file C:\...\...\...\...\VIo1F3B.tmp

It’s obvious that some paths (literally, figuratively) were getting crossed somewhere (*Note you may also want to attempt to use cscript from the command line just to make sure it is, in fact, installed and working. I did, and mine was*.)

In my experience the first place to check for errors with Vim plugins is the PATH so I opened up Jesse’s plugin and focused on this section (lines 41-59 on his github repo version):

  let s:plugin_path = '"' . expand("~/") . '"'
  if has("win32")
    let s:cmd = 'cscript /NoLogo '
    let s:plugin_path = s:plugin_path . "vimfiles"
    let s:runjslint_ext = 'wsf'
  else
    if has("gui_macvim") && filereadable('/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc')
      let s:cmd = '/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc'
    else
      let s:cmd = 'js'
    endif
    let s:plugin_path = s:plugin_path . ".vim"
    let s:runjslint_ext = 'js'
  endif
  let s:plugin_path = s:plugin_path . "/plugin/jslint/"
  let s:cmd = "cd " . s:plugin_path . " && " . s:cmd . " " . s:plugin_path
               \ . "runjslint." . s:runjslint_ext
  let s:jslintrc_file = expand('~/.jslintrc')

Here’s the version I ended up with after experimenting for awhile:

let s:plugin_path = '"' . expand("C:/Program Files/Vim/vimfiles") . '"'
  let s:cmd = 'cscript /NoLogo '
  let s:runjslint_ext = 'wsf'
let s:plugin_path = s:plugin_path . "/plugin/jslint/"
let s:cmd = "cd " . s:plugin_path . " && " . s:cmd . " " . s:plugin_path
       \ . "runjslint." . s:runjslint_ext
let s:jslintrc_file = expand('C:/Program Files/Vim/_jslintrc')

So, I ended up changing 3 things:

  • Remove tests for which OS is being used
  • Explicitly state the plugin path
  • Changed the name and path of the jslintrc file

First, since we know this is for XP, the tests for Win32, Mac and ‘Nix are not needed. Second, there are more compact and portable ways to specify this path, but posting it this way makes it easy to adapt to your system path. Third, On my XP system my .vimrc is actually _vimrc and I keep it here in the vimfiles directory so it made sense to me to do the same with a jslintrc.

Speaking of the _jslintrc, it looks like the ‘fulljslint.js’ file is updated quite a bit on the jslint.com website, and allows for more options in the jslintrc file. I need to experiment with that next

X JavaScript: Detecting, Adding and Removing Class Names

I want to post a quick bit here about three methods I use in the X library which make use of element classnames. First a method to detect if an element has a given class:

X.hasClass = function(element, $class) {
    var pattern = new RegExp("(^| )" + $class + "( |$)");
    //ternary to choose
    return pattern.test(element.className) ? true : false;
};

Breaking that down, you have the function taking two arguments. One, the element to check for the presence of the class name and two, the class name itself. Next, a RegEx is defined which will attempt to match the class name you passed in. The RexEx itself is made so that a portion of a string can be matched because of the way that classnames are applied to elements on the DOM, i.e. “spam eggs vikings”. Last, the ternary (return pattern.test…) executes depending on what the test() method returns. Remember, test() is a function of the RegEx object which returns a bool depending on a positive(or lack of) match. Test() is going to return a ‘true’ of the class name is found, ‘false’ if not.If you are unfamiliar with ternary operators, let’s explain by looking at what an if/else doing the same thing would look like.

if (pattern.test(element.className)) {
    return true;
}
else {
    return false;
}

That works, but it’s not the lean and mean js machine we are going for here. Now, the one line of the ternary:

return pattern.test(element.className) ? true : false;

And it works like this:
an action -something to evaluate- ? do if true : do if false

So, in practice you could check if an element had a class name by calling:

X.hasClass(myElement, 'myClass');

hasClass is going to return a Bool though, so usually you will use it as part of an expression:

if(X.hasClass(myElement, 'myClass') {
    do stuff if true
}

I rarely use hasClass outright, but the next function does.

To add a class to a single element or a collection of elements we have this:

X.addClass = function(element, $class) {
    var i;
    //is the element array-like?
    if(element.length) {
        for (i = 0; i < element.length; i++) {
            if (!X.hasClass(element[i], $class)) {
                element[i].className += element[i].className === "" ?
                $class : " "+$class;
            }
        }
    }
    else { //not array-like
        if (!X.hasClass(element, $class)) {
            element.className += element.className === "" ?
                $class : " "+$class;
        }
    }
    return element;
};

In Summary, addClass takes 2 arguments, an element and a class name, checks to see if the element passed in is a single element or a collection of elements, then checks the element(s) to see if they have the passed in class name (via hasClass). If not, element is then checked for the presence of any other classes. Why? If there is another class name already assigned to the element(s) you need to insert a space in front of the new class being appended. Finally the class name you passed in is appended (as long as it’s not already there) and the element is returned. Let’s combine the get method with this in a couple of practical examples:

Say you have an HTML page with these four divs contained in a fifth div:

<div id="stripes">
    <div id="div1"></div>
    <div id="div2"></div>
    <div id="div3"></div>
    <div id="div4"></div>
</div>

And maybe some styles to go with them:

#div1, #div2, #div3, #div4 {
    height: 50px;
    margin-top:10px;
    border-bottom: 1px solid #000;
}
.green {
    background-color: #7f9e7b;
}
.slate {
    background-color: #424242;
}
.grey {
    background-color: #888;
}

So, on a goofy little test page you might have this:

Classless divs

Classless divs


Since the get method returns either a single element or a collection of them, and since our new addClass function can handle either scenario you could grab all those divs and set their background to say, green, in one pass:

X.addClass(X.get('div', X.get('#stripes')), 'green');

Giving you this:

Now with some class (green to be exact)

Now with some class (green to be exact)

Breaking that expression down, recall that the get method will allow you to pass in a ‘context’ to restrain searching. Because I only wanted the divs inside of the div with the id of “stripes” I passed it (‘#stripes’) in along with the search for the generic ‘div’ tagname:

var divs = X.get('div', X.get('#stripes'));

You can break this up into 2 expressions if you want:

var stripes = X.get('#stripes');
var divs = X.get('div', stripes);

After getting the elements, I sent them to addClass. If you used the more verbose code above you would have:

var stripes = X.get('#stripes');
var divs = X.get('div', stripes);
X.addclass(divs, 'green');

In these examples the fact that addClass returns the element passed in is of no consequence. JavaScript functions always return a value, even if one is not specified (undefined in that case), so specifying element as the return value could just be ignored, but we are going to use it next to combine with removeClass.

Here is that removeClass function:

X.removeClass = function(element, $class) {
    var pattern = new RegExp("(^| )" + $class + "( |$)"),
    i;
    //is element array-like?
    if(element.length) {
        for (i = 0; i < element.length; i++) {
            element[i].className = element[i].className.replace(pattern, "$1");
            element[i].className = element[i].className.replace(/ $/, "");            
        }
    }
    else { //nope
        element.className = element.className.replace(pattern, "$1");
        element.className = element.className.replace(/ $/, "");
    }
    return element;
};

Before doing something pedestrian like just removing the ‘green’ classnames from the divs inside of ‘stripes’ let’s swing for the fence and remove the classnames, then add new ones all in one go. weeee!:

X.removeClass(X.addClass(X.get('div', X.get('#stripes')), 'slate'), 'green');

Now that test page would look like:

Class changed to 'slate'

Class changed to 'slate'

This works because of the fact that addClass is returning the element list containing all of the divs we want removeClass to operate on. If you look close you’ll notice that all you needed to do was to take the previous expression we used for adding the green classes at once:

X.addClass(X.get('div', X.get('#stripes')), 'green');

and wrapping it with the removeClass call:

X.removeClass([--previous expression--], 'slate');

There’s more to breakdown, but not too much. The removeClass function just uses the same methodology that addClass does for checking if element is an array-like object (has a length?) then looks for the passed in classname with a RegExp which will replace a match with an empty string using the ‘$1′ backreference (the second call to replace makes sure no whitespace is left over). Finally the element is returned, just like addClass. And, yes, you could invert the order to addClass(removeClass()), it doesn’t matter

All of these examples could have, obviously, been used on single elements. X.get, X.addClass and X.removeClass will take care of that for you. So if you wanted to change the second div back to green you would go:

X.removeClass(X.addClass(X.get('#div2'), 'green'), 'slate');

Notice there is no need to specify a context when using an id (it should be unique).

QueryString Manipulation With The X JavaScript Library

I’ve spent a couple of days lately coding against the Microsoft Bing maps api at work. I just put together a little method this morning to mimic .Net’s ‘Request.Querystring’ functionality because I am needing to grab either a zip code or a latitude/longitude from the URL. The function, named requestQueryString(), expects a string to be passed in which should be a ‘key’ part in one of the key:value pairs located in the url of the page being viewed. In other words, if the URL is

www.apage.aspx?zip=85226&lat=33.372154&lon=-111.754322

the ‘keys’ are ‘zip’, ‘lat’ and ‘lon’, while the values are ’85226′, ’33.372154′ and ‘-111.754322′. To fetch the value associated with zip you would do:

var zipcode = X.requestQueryString('zip');

This will return ’85226′ given the previous URL. For ‘lat’ or ‘lon’ it would be pretty much the same:

var latitude = X.requestQueryString('lat'),
longitude = X.requestQueryString('lat');

Here is the method itself. I’ll update the x.js page with the new code as well.

X.requestQueryString = function(key) {
    //get the whole qs minus the ?
    var qs = window.location.search.substring(1),
    //split at the '&'
    splitArr = qs.split('&'),
    //to hold what we really want
    keys = [],
    values = [],
    //the key passed in to get the value for
    patt = new RegExp("(^| )" + key + "( |$)");
    //remove any whitespace TODO do a bool to see if the qs has any %20's
    for (var i = 0; i < splitArr.length; i++) {
        var trimArr = splitArr[i].split('%');
        //dont want the '%20s'
        splitArr[i] = trimArr[0];
    }
    //split the arr into key & value arrays
    for (var j = 0; j < splitArr.length; j++) {
        keys[j] = splitArr[j].slice(0,splitArr[j].indexOf("="));
        values[j] = splitArr[j].slice(splitArr[j].indexOf("=")+1);
    }
    //look for a match for passed in key, if so...
    for (var k = 0; k < keys.length; k++) {
        //if key is matched, return its value
        if (patt.test(keys[k])) {
            return values[k];
        }
    }
};

It’s an early implementation and a couple of things are worth noting. I had an issue with whitespace because I get the lat/lon numbers from a webservice maintained elsewhere in my company, and elsewhere is not properly constraining the input. You could remove this:

for (var i = 0; i < splitArr.length; i++) {
        var trimArr = splitArr[i].split('%');
        //dont want the '%20s'
        splitArr[i] = trimArr[0];
}

if you know that whitespace will not be an issue. Leaving it in place doesn’t add much of any overhead as the split command will just do nothing as it won’t see any ‘%’.

X: Pt4. Object Literals, Convenience Methods, And Go!

Now that we have a shiny new object created called ‘X’, Let’s use it. I like to organize all of my JavaScripts by incorporating object literals (see Rebecca Murphey’s talk on the closely related object lateral [that's a joke btw, it was a misprint at jconf], the excellent book Simply Javascript, and of course The Good Parts ). Here’s a ‘stub’ for the objects we could use:

    var ObjectName = {
   
        cache: {},
        config: {},
        init: function() {},
        otherThing: function() {}
    }

I will typically use the ObjectName.cache{} to hold a snapshot of the page, or areas of the page that I don’t want to have to re-query the DOM for. If my page is not going to change via AJAX I can store fetched results there for further use/manipulation. The ObjectName.config{} has the same functionality as .cache{}, it’s just my way of seperating out more important info than the generic .cache{}. You could use one, both, or neither, really just a matter of taste.

For this to be of use we need a way to load it once the dom is ready. Let’s add a ‘ready’ type event to the X library then:

X.go = function(obj) {
    X.addEventHandler(window,"load",obj.init);
};

You’ll pass your object literal to the .go() method which will, after the page has loaded, call the init() method of the object you passed to it:

    X.go(ObjectName);

Where this goes depends on how you are constructing your site. For now, we’ll put this line just below each object literal created. Later we’ll look at a site-wide answer to automating the loading of scripts, see Paul Irish’s blogs for more on that…

With a way to bind event handlers, object literals to hold the handlers, and a method to load the scripts, now we can just write some really basic JavaScript to utilize this stuff. So maybe there’s a button that lives on a page:

    <html>
        <head>
            <script src='x.js' type='text/javascript'></script>
        </head>
        <body>
            <input type='button' id='say-hi'>Click Me</input>
            <!--your external .js file linked to this page-->
            <script src="objectName.js" type="text/javascript"></script>
        </body>
    </html>

We need to set an event handler on that button to fire when the user clicks it. In the init() function of objectName do something like:

init: function() {
    var button = X.get("#say-hi");
    X.addEventHandler(button, "click", ObjectName.doClick);
}

I’ll get to the ‘doClick’ function in a moment. First, I want to write myself a convenience method to shorten the verbosity of adding event handlers. This is optional, of course.

    X.$click = function(element, handler) {
        X.addEventHandler(element, 'click', handler);

Now, with the convenience method added to the X library we can revisit what we wrote earlier:

var button = X.get('#say-hi');
X.$click(button, objectName.doClick);

That’s getting better, but we can combine those 2 statements down to:

X.$click(X.get("#say-hi"), objectName.doClick);

I am using a ‘$’ as a sigil in front of the name of the convenience methods throughout ‘X’. I do this to keep any conflicts with keywords like ‘submit’, ‘blur’ or ‘focus’ from happening. Plus all the cool kids are using $…

OK, so about the doClick handler. Remember that X.addEventHandler takes a target element, an event, and a function to call when that event happens on that element. The doClick function call is in object literal notation here, it is an attribute of the object you have made. I’ve been using ObjectName as the object so far (the observant among you may have noticed that the file is objectName.js. I do this in the development phase of a project as I will usually have many separate objects, all of them their own individual .js files. So objectName.js will have one object defined in it, ObjectName. At deploy time, I’ll combine them into one .js file and minify it). ObjectName will have this named function as an attribute:

doClick: function(event) {
    alert("Hi!");
}

The event handler calls ObjectName.doClick() because you bound it to that element. Here’s the whole object then:

var ObjectName = {

    init: function() {
        X.$click(X.get("#say-hi"), objectName.doClick);
    },

    doClick: function(event) {
        alert("Hi!");
    }
};

X.go(ObjectName);

Don’t forget to include the call to the go() method, outside of the variable which passes in ObjectName and tells the browser to call ObjectName’s init() method when the DOM fires the ‘load’ event. The cool thing about an object like this is re-usability. If your site was littered with buttons which needed to say “Hi” when pressed (it could happen) you would only need to attach this object to a master page and it would attach the event to any button with an id of “say-hi”. Now, it is an id so there should only be one per page. If there are going to be multiple instances per page, use a class instead, and iterate over the nodelist that get returns with a for loop to attach events. Assuming there are multiple buttons on the page with a class=”say-hi” attribute you would do:

var ObjectName = {

    config: {
        say: "Hi!"
    },

    init: function() {
        var buttons = X.get(".say-hi");
        for (var i = 0;  i > buttons.length; i++) {
            X.$click(buttons[i], ObjectName.doClick);
        }
    },

    doClick: function(event) {
        alert(ObjectName.config.say);
    }
};

with this you wouldn’t have to worry about the number of elements with a class of “say-hi”, as all of them would be fitted with a click event handler that will call ObjectName.doClick(). Note the use of the config object which is an attribute of ObjectName. You can manipulate this attribute if you wanted, changing it during the page lifecycle, like say adding a toggle-type command in the doClick() function:

doClick: function(event) {
        alert(ObjectName.config.say);
        ObjectName.config.say === "Hi!" ? ObjectName.config.say = "Hi Again!" : ObjectName.config.say = "Hi!";
    }

Now the alert will alternate between “Hi!” and “Hi Again!”. Silly, but you get the idea.

Next, we’ll do a continuation of the object literal using more of the config and cache functionality, I need to get away from this keyboard now!

X: Part Three. The Get() Method.

Before moving into the construction of object literals, which is my preferred method of organizing my scripts, I want to take a post to discuss the get() method in the X library. The functionality the get() method provides is as follows:

  • return an element by its id
  • return an element or elements by class name
  • return an element or elements by tag name
  • given a context, return only elements within it via the above methods

Pretty straightforward yes? To save on the typing, let’s construct the method so that a search for an element use the css selectors ‘#’ for id, and ‘.’(a period) for class like so:

    X.get('#spam');

will return an element with id=”spam”, and to fetch all elements with class=”eggs” you would use:

    X.get('.eggs');

Finally a tag name search would just require the name of the tag:

    X.get('div');

This would return a node list of all div tags on the page. The use of a context argument:

    var divs = X.get('div'),
    //get only a tags in the 3rd div
    var anchors = X.get('a', divs[2]);

You could just pass the function as well:

    //get only a li elements in a certain ul
    var lis = X.get('li', X.get('#certain_ul'));

You get the idea…

Here’s the code for the method:

X.get = function(what, where) {
 
    //context is document by default or self if included
    where = where || document;

    //remove where from an array-like structure if needed
    //this is necessary with recursive use of get()
    if (where.length) {
        where = where[0];
    }

    var selector = what.substr(0, 1),
    name = what.slice(1),i,
    getArray = [], gotArray = [],
    pattern = new RegExp("(^| )" + name + "( |$)");
    //have queryselector or queryselectorall?
    switch (selector) {

        case '#': //is a get by id
            return where.querySelctor ? where.querySelctor(what) :
                where.getElementById(name);
            break;

        case '.': //is a get by class name
            //ie browsers...
            if (typeof document.all != "undefined") {
                //is there queryselectorall?
                if (where.querySelectorAll) {
                    return where.querySelectorAll(what);
                }
                else {
                    getArray = where.all;
                } //no ternary here because of unrelated results  
            }
            else {
                //Is there querySelectorAll()?
                if (where.querySelectorAll) {
                    return where.querySelectorAll(what);
                }
                else { //if not queryselectorall support
                    getArray = where.getElementsByTagName("*");
                } //same as above as far as ternary goes...
            }

            for (i = 0; i < getArray.length; i++) {
                if (pattern.test(getArray[i].className)) {
                    gotArray[gotArray.length] = getArray[i];
                }
            }
            return gotArray;
            break;

        default: //is a get by tag name
            return where.querySelectorAll ? where.querySelectorAll(what) :
                where.getElementsByTagName(what);
    }
};

A selector is gotten by slicing off the first ‘letter’ of the passed in identifier. The switch statement looks at that selector to decide what query to pursue. The root of the query is assigned as document by default (resulting in document.getElement….) or reassigned as the passed in context if one was passed. The getElementById query (‘#…) checks first for the newer w3 querySelector() support which is present in the newest versions of most browsers (including IE8 to some degree), then falls back to the old standard if false. The search by class name is slightly more complex only because there was no standard DOM method to query by class until querySelectorAll() (which, again, is only in the newest of browser versions). When this segment of the case is entered a quick IE vs standards check is conducted (IE will use document.all) and if no querySelectorAll() support is present the 2 arrays created and the RegExp are used to:

  • getArray – Holds all page elements
  • pattern – Test each element for the passed class name
  • gotArray – Hold all elements which pattern.test() returns true

gotArray is then returned. The last piece of the switch, fetch by tag name, is as simple as ID querying because of the presence of the native DOM method getElementsByTagName(). Again used as a fall back here in the case of no querySelectorAll().

I think thats a good enough explanation of this method moving into the next post…

X Project, Pt2: More on Events, Leaks, and IE

Picking up where I left off in ‘Hacking together’, which we’ll now call Pt.1, Let’s look at the entirety of the addEvents section of the library in browsers which aren’t named IE…

//check for standards support first
if (document.addEventListener) {
    //w3 standard events model
    X.addEventHandler = function(element,type,handler){
        element.addEventListener(type,handler,false);
    };

    X.removeEventHandler = function(element,type,handler){
        element.removeEventListener(type,handler,false);
    };

    X.stop = function(event) {
        event.stopPropagation();
    };

    X.prevent = function(event) {
        event.preventDefault();
    };    
} //end

Now to address the IE events model and the (hopefully) soon-to-be-a-thing-of-the-past memory leak issue. If we stroll back in time to 2005 there was a contest held over at Quirksmode for a JavaScript solution to the addEvent challenge. The winner was (to no great suprise) John Resig. That entire post saga is required reading imho. One of the judges of the contest, Dean Edwards wrote this solution, from which I am using a slightly modified version.

    else {
    //IE or other non standards event model-------------
    X.addEventHandler = function(element,type,handler) {
        //check for guid, assign one if not present
        if(!handler.$guid) handler.$guid = X.addEventHandler.guid++;
        //create a hash for the element's event types
        if(!element.events) element.events = {};
        //create a hash of event handlers for each element/event pair
        var handlers = element.events[type];
        if(!handlers) {
            handlers = element.events[type] = {};
            //store the existing handler if exists
            handler[0] = element["on" + type];
        }
        //store the event handler in the hash
        handlers[handler.$guid] = handler;
        element["on" + type] = X._handleEvent; //called when event fires
    };

    X.addEventHandler.guid = 1; //counter to generate the guids

    X.removeEventHandler = function(element,type,handler) {
        //delete a single handler from the hash table
        if(element.events && element.events[type]) {
            delete element.events[type][handler.$guid];
        }
    };

    X.prevent = function(event) {
        event.returnValue = false;
    };

    X.stop = function(event) {
        event.cancelBubble = true;
    };
   
    X._handleEvent = function(event) {
        //need a return val
        var returnVal = true;
        //IE uses a global event object
        event = event || window.event;
        //get a ref to the hash of event handlers
        var handlers = this.events[event.type];
        //execute each one
        for(var i in handlers) {
            this.$handleEvent = handlers[i];
            if(this.$handleEvent(event) === false) {
                returnVal = false;
            }
        }
        return returnVal;
    };
}

IE has a serious flaw if not corrected: When your attached function gets fired, the ‘this’ reference refers to the worthless ‘window’, when, in fact, it should refer to the parent object (I think I’m paraphrasing J.Resig there). This code addresses that by creating a hash table of event handlers with unique ids bound to your target element, assigning a ‘handleEvent’ method to be called when your event fires. This passes the object correctly and maintains scope (this). You should walk through this series of methods with IE’s developer tools JavaScript debugger, which is the best way to see how this works in real-time. Then there is a removeEventHandler to allow assigned handlers to be removed (more on that in a moment), as well as IE methods for preventing default behavior and stopping event propagation.

So, back to removeEvent and the IE memory leak possibility. This code does not use attachEvent() to handle events, which unless I am mistaken was the biggest problem with IE’s memory manager understanding which event handlers could be reclaimed by the garbage collector. See Douglas Crockford’s post here. Admittedly, this little portion of a discussion on the IE memory leak issue is a tiny drop in one seriously big-assed ocean. There is so much material out there on this subject alone you could get lost in it. I don’t really want to go down that path any further here. We’ll bind events without attachEvent, we’ll set a removeEvent handler on the events we bind and we’ll move on. A real solution would be don’t use IE, but I digress…

I have made a seperate page on the blog called ‘x.js’ for the code so far. Next I want to go into using object literals as a way to organize your code which will use the X library and some syntactic sugar to make for less verbose script, until then…

Return top