Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Tuesday, October 30, 2007

Alternative Module Pattern

Okay, I still haven’t finished my event package yet, but I do have short article about a variation on the module pattern. I learned this from the conference this Saturday and Douglas Crockford’s talk. In my last article I showed the ‘begetObject’ method; the technique today will user a similar structure.

var myObject = function() { var F = function() {}, that = null; F.prototype = { somePublicVariable: true, somePublicFucntion: function() { return that.somePublicVariable; } }; that = new F(); return that; }();

In this case, inside the Function ‘myObject’ a new Function ‘F’ is created. ‘that’ is initialized to null, but is accessible to all methods inside this closure, including anything you attach to the prototype of ‘F’. This is handy because maintaining the scope of the ‘this’ object can be complex or down-right impossible in certain cases. ‘that’ will always have the proper scope and anything inside the ‘myObject’ closure will have access to it. In the past, I have always used an initialization Function to properly set the ‘that’ (or in previous examples I used ’self’) variable to the newly created object. By using this technique, you do not need to use an initialization Function.

Like other module pattern Functions, this method does not effectively leverage the prototype Object. Each time you call ‘myObject’ a new memory footprint will be created for Functions and variables attached to the prototype of ‘F’, instead of a pointer to the Function defined on the prototype. I believe I will be adopting this technique when writing methods that I previously would have used the old module pattern for: static packages and/or objects initialized once (or just a few times).

posted by Matt Snider at 11:32 pm  

Saturday, October 27, 2007

JavaScript - The Good Parts

I attended a talk by Douglas Crockford today, entitled “JavaScript - The Good Parts”, where he outlined the good and bad parts of JavaScript. His slides are located on his site, at http://www.crockford.com/codecamp/.

This article will highlight some of the key points made during the presentation and as there was a lot of extra material, not reflected in the notes, I will try to capture that as well.

Influences

These are the languages that influenced the development of JavaScript and in parenthesis are the elements that were inherited.

  • Java (syntact, conventions)
  • Self (prototypal inheritance, dynamic objects)
  • Scheme (lambda, loose typing)

Lambda

Lambda is perhaps one of the most important features as it allows for the functional programming that we all know and love. This includes declaring functions inside of functions, closures, and returning/passing functions as parameters.

The Bad Parts

  • Global Variables - always bad practice in all languages
  • ‘+’ adds and concatenates
  • Semicolon insertion - produces poor and unexpected behavior
  • typeof - doesn’t work as expected (array and null are objects)
  • with and eval - frequently misused
  • phony arrays
  • ‘==’ and ‘!=’ - strings and numbers shouldn’t be equal
  • falsy: false, null, undefined, NaN, 0

Bad Heritage

The methods and syntax inherited from older languages that frequently cause issues or are just plain poor.

  • blockless statements - poor practice and often causes unexpected issues
  • expression statements
  • floating point arithmetic based on IEEE standards - (0.1 + 0.2 !== 0.3)
  • ‘++’ and ‘–’ - leads to complex code blocks
  • switch pass-throughs - causes frequent unexpected behavior

Good Parts

  • Lambdas
  • Dynamic Objects
  • Loose Typing
  • Prototypal Inheritance

Crockford also suggests to avoid using the new operator as it can lead to issues in the global namespace. Here is his method

Object.prototype.begetObject = function() { Function F() {}; F.prototype = this; return new F(); }

In this case ‘begetObject’ returns the ‘new’ instance of the Object, basically inheriting from itself. A better implementation could be to pass another Object into the constructor, then you can have objects inheriting from each other. I will discuss this more in a feature article.

Use ‘that’ instead of ’self’

He also pointed out that my technique of using ’self’ variable name to reference the closure safe version of the ‘this’ Object is a bad practice because some browsers reference the ‘window’ as ’self’, which could cause errors. Instead he recommends using a variable named ‘that’.

Garbage Collection - The IE6 Memory Leak

Different browsers use different garbage collection techniques, which sometimes cause issue (IE6 memory leak). IE6 uses a technique known as, Reference Counting. This is a very simple technique that counts references to objects and deletes objects with no references. In JavaScript, this does not work well, because of event listeners. If an element is removed from the DOM that still has an event listener, then the element is not removed from memory. And this causes the IE6 memory leak.

Use K & R style braces

I always thought that the bracket structure we used in JavaScript was a stylistic issue and not a functional one. However, do to semi-colon insertion, there can be some serious issues. Therefore, K & R braces should be used over Allman or another style. Here is a famous example:

K & R style:

var foo = function () { return { ok: true }; };

is not the same as

Allman Style:

var foo = function () { return { ok: true }; };

in the Allman example what actually happens is that the browser inserts a semi-colon at the end of the ‘return’ line and the inner object simple becomes an unreachable and useless statement. JavaScript won’t through any errors as it is syntactically correct, but it will not do what you need it to. Therefore, you should use K & R braces so as to be safe.

Well, that about wraps up what I learned today. Hopefully, this will help you as well. I will also be writing an article about Prototypal Inheritance in November, when I have some time to put together some tests.

posted by Matt Snider at 2:47 pm  

Friday, October 26, 2007

News: More on RSH

No article today. Sorry all, I forgot I was getting my wisdom teeth pulled yesterday and I have not been able to focus.

However, Brian Dillard has posted an update on this Real Simple History beta. Check it out:

DOM methods, document.write and the art of library design

posted by Matt Snider at 2:31 pm  

Thursday, October 25, 2007

News: Tripoli Beta In Development

Tripoli is one of the best and lightest CSS Framework. And it has been/is still being improved! Learn more:

Tripoli Beta In Development

My CSS Rest is mostly based on Tripoli, except that I do not use the * {} style. I have found it slows down rendering in some browsers. For more detailed explanation:

CSS Frameworks And Removing X-browser Variations

posted by Matt Snider at 8:56 am  

Wednesday, October 24, 2007

News: Really Simple History

The really simple history project is being taken over by Brian Dillard. He is revamping it to use the Module Pattern and compliant methods wherever possible. For more information see my article:

More on Really Simple History

And Brians:

Coming Soon: Really Simple History 0.6 Beta

posted by Matt Snider at 2:08 pm  

Wednesday, October 24, 2007

Event Package Part 2

In my last article (Event Package Part 1), I covered some of the necessary parts of an event package and mentioned some “optional” advanced features. I originally planned to discuss them all in this article, but there is too much material to cover, so I will be breaking it up into two articles. Some of the advanced features (such as, removing all listeners and scope control) require an internal caching mechanism and I will be covering this system in Friday’s article. Todays article will cover the other features, including two more additions to the Core package.

These new core methods are “getBody” and “getScrollOffset”:

/** * X-browser method to find the BODY tag; lazy definition to improve repeat performance * * @method getBody * @return {HTMLElement} the BODY tag * @static */ var getBody = function() { var bd = window.document.body || window.document.childNodes[0].childNodes[1] || window.document.getElementsByTagName(”body”)[0] || window.document; Mint.Client.getBody = function() {return bd;} return Mint.Client.getBody(); };

This method tries 3 ways to get the BODY tag, then defaults to the document. The “bd” variable will then hold the reference to the BODY tag. We then use the lazy initialization technique to create a closure that will improve the performance of any subsequent requests to this method.

/** * X-browser method to find the scroll offset; lazy definition to improve repeat performance * * @method getScrollOffset * @return {Object} the scroll offset object {x, y} * @static */ var getScrollOffset = function() { var de = document.documentElement, db = getBody(); if (de && (de.scrollTop || de.scrollLeft)) { Mint.Client.getScrollOffset = function() {return {x: dd.scrollLeft, y: dd.scrollTop};} } else if (db) { Mint.Client.getScrollOffset = function() {return {x: db.scrollLeft, y: db.scrollTop};} } else { Mint.Client.getScrollOffset = function() {return {x: 0, y: 0};} } return Mint.Client.getScrollOffset(); };

This method returns the scrolled offset of the viewport, in both the x and y directions. We first create the “de” and “db” variables, then evaluate which of 3 methods to lazily define “getScrollOffset” as. The first if statement is true for most compliant browser, the second is true for IE, and the 3rd is a failsafe for older browsers (this way, calling this method won’t throw an exception). Again, by using lazy function definition pattern, we will improve the runtime of subsequent requests.

I also added the following methods as private Functions of the Event package:

/** * Some browsers (Safari) will sometimes return a text node inside the targeted element. This normalizes the return value for getEl and getRelatedEl. * * @method resolveTextNode * @param node {HTMLElement} the node to resolve * @return {HTMLElement} the normized node * @private */ var resolveTextNode = function(node) { return (node && document.TEXT_NODE == node.nodeType)? node.parentNode: node }; /** * Some browsers (IE) do not actually return the event, but store it in a global object. We will try to normalize this in our event callback, but just in case, we resolve this each time we expect an event. * * @method resolveEvent * @param e {Event} The trigger event * @return {Event} the IE event * @private */ var resolveEvent = function(e) { return e || window.event; }

“reseolveTextNode” was modeled after YUI’s method, but is more compact and leverages the ‘TEXT_NODE’ constant. Sometime Safari or Safari-like browsers return the textnode inside of the target element, instead of the target element. This method normalizes these situation and returns the appropriate target element. The “resolveEvent” is used to normalize an Event anytime it is expected as a paremeter. IE attaches the event to a global constant instead of returning it to the Event handler. And although, I plan on passing an already normalized event back to the callback Function, it is possible that another event system was used to get the Event without normalizing it.

Now armed with these methods, we can see how to implement the following Event package methods:

  • getRelatedEl
  • getTime
  • getX
  • getY
  • getXY
/** * X-browser method to return the event related target; tries to find relatedTarget, then toElement (mouseover), then fromElement (mouseout). * Some browsers (Safari) sometimes provide text nodes, so we automatically resolve them. * * @method getRelatedEl * @param e {Event} the trigger event * @return {HTMLElement} the related target element * @static */ getRelatedEl: function(e) { e = resolveEvent(e); return resolveTextNode(e.relatedTarget || e.toElement || e.fromElement); }, /** * Returns the time of the event or set the event to the current time. * * @method getTime * @param e {Event} the trigger event * @return {Date} the time of the event * @static */ getTime: function(e) { e = resolveEvent(e); if (! e.time) { var t = new Date().getTime(); try { e.time = t; } catch(ex) { return t; } } return e.time; }, /** * X-browser method to return the x position of the event * * @method getX * @param e {Event} The trigger event * @return {int} the x coordinate of the event * @static */ getX: function(e) { e = resolveEvent(e); var x = e.pageX; if (! x && 0 !== x) { x = e.clientX || 0; if ( Core.Client.isIE() ) { x += Core.Client.getScrollOffset().x; } } return x; }, /** * X-browser method to return the y position of the event * * @method getY * @param e {Event} The trigger event * @return {int} the y coordinate of the event * @static */ getY: function(e) { e = resolveEvent(e); var y = e.pageY; if (! y && 0 !== y) { y = e.clientY || 0; if ( Core.Client.isIE() ) { y += Core.Client.getScrollOffset().y; } } return y; }, /** * Returns the pageX and pageY properties as an indexed array. * * @method getXY * @param e {Event} The trigger event * @return {Object} the coordinate object {x: xpos, y: ypos} * @static */ getXY: function(e) { e = resolveEvent(e); return {x: self.getX(e), y: this.getY(e)}; },

The JAVADOC comments describing the Functions do a pretty good job of explaining their use. Let me know if you have any questions. On Friday I will be uploading the entire Event package JavaScript, which will probably help clear up confusion.

posted by Matt Snider at 1:52 pm  

Sunday, October 21, 2007

Event Package Postponed

I was hoping with the insane hours I worked last week, I would find some time to finish testing and debugging the Event package, but for every bug fixed, two more took its place. On the upside there will be a major new release for Mint this week. On the down-side I did not finish the article. So have to postpone it until Tuesday.

Thanks,
-matt

posted by Matt Snider at 10:03 pm  

Thursday, October 18, 2007

Event Package Part 1

For this article, I will be using the “attachFunc” and “detachFunc” methods outlined in my X-Browser Event Handling article.

Again, sorry about this late post. I am still working 14 hour days this week.

Today I will be introducing a simple event handler package. First, we will discuss the methods you might want to have in an Event Package and whether they are: required, frequently used, or nice to have.

Necessary Functions:

  • Event.add - same as “attachFunc”
  • Event.remove - same as “detachFunc”

Those two functions will allow you to add and remove any event from any element. This is all you need for event management, however, you may frequently wish to know about or change an event (such as triggering element or key code, and preventing default behavior). Here is a list of methods and variables that I frequently use:

Frequently Used Functions and Static Variables:

  • Hash map of key codes - such as {KEY_BACKSPACE: 8}
  • Hash map of event names - such as {CLICK: ‘click’}
  • getEl - returns the element that owns this event
  • getCode - returns the key code of the event
  • stopDefault - stops the capture phase of the event
  • stopProgation - stops the bubbling phase of an event
  • stop - stops both the bubbling and capture phase of an event

Having static key codes is helpful, as you won’t need to look them up everytime you are listening for an “enter” key. I map the event Names because if you try to attach a ‘clck’ event, instead of a ‘click’ event, the browser does not throw an exception. However, your code is not going to work, but if you instead mispell your variable name, then the browser will throw an exception. Here are the function mentioned so far:

var Event = function() { // Private namespace var self = null; // Public namespace return { /** * Event name constants */ BLUR: ‘blur’, CLICK: ‘click’, DOUBLE_CLICK: ‘dblclick’, KEY_DOWN: ‘keydown’, KEY_PRESS: ‘keypress’, KEY_UP: ‘keyup’, LOAD: ‘load’, MOUSE_DOWN: ‘mousedown’, MOUSE_MOVE: ‘mousemove’, MOUSE_OVER: ‘mouseover’, MOUSE_OUT: ‘mouseout’, MOUSE_UP: ‘mouseup’, SUBMIT: ’submit’, UNLOAD: ‘unload’, /** * Common event keycodes */ KEY_BACKSPACE: 8, KEY_TAB: 9, KEY_RETURN: 13, KEY_ESC: 27, KEY_LEFT: 37, KEY_UP: 38, KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, KEY_HOME: 36, KEY_END: 35, KEY_PAGEUP: 33, KEY_PAGEDOWN: 34, KEY_SPACE: 32, /** * Attach a listener from an element; uses lazy loading to setup the add and remove functions * * @method add * @param el {HTMLElement} the element to bind the handler to * @param sType {string} the type of event handler * @param fn {function} the callback to invoke * @param fl {boolean} OPTIONAL: capture or bubble phase * @static */ add: function(el, eType, fn, capture) { self = Event; if (window.addEventListener) { self.add = function(el, eType, fn, capture) { el.addEventListener(eType, fn, capture); }; self.remove = function (el, eType, fn, capture) { el.removeEventListener(eType, fn, capture); }; } else if (window.attachEvent) { self.add = function(el, eType, fn, capture) { el.attachEvent(’on’ + eType, fn, capture); }; self.remove = function (el, eType, fn, capture) { el.detachEvent(’on’ + eType, fn, capture); }; } else { self.add = function() {}; } }, /** * Extend the Event Object with x-browser retrieval of keycode from event * * @method getCode * @param e {event} The triggering event * @static */ getCode: function(e) { var code = null; if (! e) {e = window.event;} if (e.keyCode) {code = e.keyCode;} else if (e.which) {code = e.which;} return code; }, /** * X-browser event target retrieval * * @param e {event} The triggering event * @param tagName {string} OPTIONAL: HTML tag name * @param className {string} OPTIONAL: HTML tag attribute class name * @static */ getEl: function(e, tagName, className) { if (! e) {e = window.event;} var targ = e.target || e.srcElement; if ($D.TEXT_NODE == targ.nodeType) {targ = targ.parentNode;} // defeat Safari bug if (tagName || className) {targ = Dom.getParent(targ, tagName, className);} return targ; }, /** * Remove a listener from an element * * @method remove * @param el {HTMLElement} the element to bind the handler to * @param sType {string} the type of event handler * @param fn {function} the callback to invoke * @param fl {boolean} OPTIONAL: capture or bubble phase * @static */ remove: function(el, eType, func, fl) {}, /** * Convenience method for stopPropagation + stopDefault * * @method stop * @param e {event} The triggering event * @static */ stop: function(e) { self.stopPropagation(e); self.preventDefault(e); }, /** * Prevents the default behavior of the event * * @method stopDefault * @param e {event} The triggering event * @static */ stopDefault: function(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } }, /** * Stops event propagation * * @method stopPropagation * @param e {event} The triggering event * @static */ stopPropagation: function(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } }; }();

There is an endless amount of other ’stuff’ that could be added to an Event package. However, most of it is not necessary. Here are a few that I use often enough to include in my package:

Bells and Whistles:

  • getEvents - returns a collection of events on an element
  • getRelatedElem - some mouse events have a related element other than the triggering element
  • getTime - the time the event was triggered
  • getX - the x position of the event
  • getY - the y position of the event
  • getXY - the x and y position of the event
  • removeAll - removing all events from the page
  • removeEventsFromEl - removes all events or a single event type from an element
  • control scope of the callback Function

On Friday, I will look at these features and explain how they can be implemented.

posted by Matt Snider at 2:39 am  

Saturday, October 13, 2007

Using Call And Apply Methods

Sorry, for the late post. Mint is about to launch v1.1 and much of the remaining work is user interaction. I have already worked 60 hours this week and it is only Saturday morning. Anyway, enough bitching…

Today, we are going to examine the native JavaScript Function methods: call and apply. Both methods are used to set the execution scope of the Function you are calling, but with “call” you pass in each parameter directly to the Function, whereas with “apply” you pass in an array whose members are applied to the Functions parameters.

Here is a simple example of each:

var Foo = function(arg1, arg2, arg3) { // do something }; var arr = [’a', ‘b’, ‘c’]; Foo.call(window, 1, 2, 3); Foo.apply(window, arr);

In this example we are creating a Function “Foo” with parameters: arg1, arg2, and arg3; we also create an array with members: ‘a’, ‘b’, and ‘c’. Using the “call” method we set the execution scope of Foo to “window”, then pass in each argument (arg1, arg2, arg3) explicitly. The “apply” method also sets the execution scope of Foo to “window”, but instead passes in an array. The members of the array will be applied to Foo, where the array member at position n, will be applied to the nth parameter of Foo. With both methods if you pass in too few parameters, the remaining parameters will be set to “undefined”; and if you pass it too many, the extra parameters will be ignored.

This example was rather simple and since the execution scope of Foo was “window” already, we have not really accomplished much. So, lets create another Function “Bar” that leverages the native “prototype” Object:

var Bar = function(id) { this.node = document.getElementById(id); }; Bar.prototype = { callFoo: function(arg1, arg2, arg3) { Foo.call(this, arg1, arg2, arg3); }, applyFoo: function() { Foo.apply(this, arguments); } }; var barObj = new Bar(’someElementId’); barObj.callFoo(1, 2, 3); barObj.applyFoo(’a', ‘b’, ‘c’);

In this example, when instantiated, Bar will have members “applyFoo” and “callFoo”, and will contain a DOM reference “node”. Now “callFoo” and “applyFoo” will execute Function Foo with the scope of the “barObj”. Therefore, the code inside of Foo will have access to all member Functions and properties of “barObj”, such as the “node”, on Foo’s “this” Object. Many Frameworks use thse methods in conjunction with their Event and/or AJAX Handler Systems, so that you can specify the scope of the callback Function. In this case, we are not that sophisticated, “callFoo” executes Foo with up to three explicit arguments, and “applyFoo” will pass any number of supplied arguments directly into Foo. This means “applyFoo” acts like a proxy Function of Foo, only changing Foo’s execution scope.

Next week, I will be demonstrating how to create event handler Functions that maintains scope. Then you will see how to use “call” and “apply” in a working system.

posted by Matt Snider at 1:16 pm  

Tuesday, October 9, 2007

Why I Am No Longer Using Mouse Gestures

I have been a long time PC user (mostly because the vast majority of web users are on PCs) and have been in love with mouse gestures since first seeing them on a Linux machine. When the FireFox extension came out I immediately began to use them and life was great. Well, recently I switched to a mac (now that PC emulation is trivial) and of course I installed mouse gestures.

Unfortunately, the standard mac mouse only has 1 button, but you can use OSX to simulate a 2-button mouse. However, it isn’t very accurate and it is very easy to left-click when you mean right and right-click when you mean left. It also turns out that it’s easy to right-click, followed by a quick left-click, which I use as a shortcut for clicking the back button in the browser.

Where am I getting with this? Well, after spending 2 hours writing up a real post for today, I went to publish it and instead of left clicking, my mouse triggered the back button shortcut. And well, the rest is history… there went my blog entry.

What have I learned? Mouse gestures are not worth it on the mac and from now I will be writing my posts in a word processor before posting.

posted by Matt Snider at 10:07 pm  
Next Page »

Powered by WordPress