Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Wednesday, April 30, 2008

On Vacation In Maui

No articles this week. I tried to prepare something, but didn’t have enough battery life for the plane ride. Now, I’m in Maui and have a really full week. I doubt I will have time to write an article. So, I’m not going to make any promises. However, I will have another article for you all by next Tuesday ^_^.

posted by Matt Snider at 12:33 pm  

Sunday, April 27, 2008

removeNode and appendNode Methods

First, for everyone following the JavaScript Game Engine project, I am putting it on hold for now. Building an engine to support dynamic games requires a lot of work… a lot more more time than I currently have to devote to the project, and I do not want to release code of questionable quality. I will, however, revisit it from time to time and complete the article series as I am able.

For todays article, I would like to introduce two of my favorite DOM extension methods, “removeNode” and “appendNode”. They are basically, simple wrappers for “removeChild” and “appendChilde” with the added functionality to animate the addition/removal of a DOM nodes. I find these methods to be very useful in providing feedback to a user when they are taking an action that modifies the DOM. Most of the time (and the default animation of this method) an opacity fade does the trick, making the addition/removal of a DOM node tangible to the user, but sometimes you need a different animation, so the methods accept animation arguments as optional parameters. I build these methods on top of YUI animator and either add them to the “YAHOO.util.DOM” or Document Object.

Example 1: AppendNode

Document.appendNode = function(root, elem, ap, fn) { var node = $(elem), parent = $(root); if (parent && node) { parent.appendChild(node); var animParams = (isType(ap, ‘object’)) ? ap : {opacity: {from: 0.25, to: 1}}, anim = new YAHOO.util.Anim(node, animParams, 0.5, YAHOO.util.Easing.easeIn); anim.onComplete.subscribe(function() { if (fn && isType(fn, ‘function’)) {fn(node);} }); anim.animate(); } return node; };

The “appendNode” method has two required parameters: the ‘root’ element to append to, and the ‘node’ to append with. As usual, I have written the “YAHOO.util.Dom.get” method as the ‘$’ shortcut method. By default the method will animate with a fade in from 0.25 opacity to 1 opacity, but you can override it by passing in your own arguments for YUI animation. If you want an event to occur after the animation, then provide a Function as the last argument, however it is also not required; this Function will be passed the newly appended ‘node’ as its only parameter.

Example 2: RemoveNode

Document.removeNode = function(elem, ap, fn, isRemoveListeners) { var node = $(elem), parent = node.parentNode; if (parent) { var animParams = (isType(ap, ‘object’)) ? ap : {opacity: {from: 1, to: 0.25}}, anim = new YAHOO.util.Anim(node, animParams, 0.5, YAHOO.util.Easing.easeOut); if (isRemoveListeners) {Event.purgeElement(node, true);} anim.onComplete.subscribe(function() { parent.removeChild(node); if (fn && isType(fn, ‘function’)) {fn();} }); anim.animate(); } };

The “removeNode” method is very similar to the “appendNode” method, with the noted exception that the animation must occur before the actual removal of the child-node. You do not need to pass in the ‘root’ Element as we can extract that directly from the provided node. Lastly, if you have attached any listeners to the node or its children, you should probably remove them. I leveraged YUIs “YAHOO.util.Event.purgeElement” (shortcut name of “Event.purgeElement”) method for this, which will recursively remove the events attached to your node and its children, when you pass true as the last parameters (isRemoveListeners), otherwise no events will be removed.

Again, these methods are simple, but very handy. I have found it is almost always useful to provide feedback to users when they are adding/removing DOM nodes, and these methods abstract the logic away, so that I can do just about anything I need. If you’d like to give them a try, I have provided a test page.

posted by Matt Snider at 12:34 pm  

Wednesday, April 23, 2008

News: Ext Gotcha and YUI Hidden Gems

Unfortunately, I am too busy this week to finish the game project that I have been working on. Not wanting to leave you all hanging, I found some noteworthy news:

Ext JS 2.1 Now GPL (was LGPL)

Basically, this means that if you modify the Ext Framework and use it on your project, then your project is also under the GPL license. For more information, check out what Dietrich Kappe wrote on Agile Ajax, Ext JS 2.1 Now GPL (was LGPL).

Hidden Gems in the YAHOO Object

There is a lot of useful functionality attached directly to the YAHOO Object that has not been well noted. Find out more on the YUI Blog, Hidden Gems in the YAHOO Object.

posted by Matt Snider at 4:55 pm  

Sunday, April 20, 2008

Game Menu

For the game project, I felt it would be prudent to have a menu that handles the control of the game engine. For this task, I have decided to use YUI Menu, because I know YUI well, and it is powerful, extendable, and easy. One of the great things about the YUI is the plethora of examples about how to use it, including the menu widget. You can also reference the JavaScript directly from the YUI site, so there is no need to maintain them yourself, or worry about versions.

Example 1: Files Needed For YUI Menu

<link rel=”stylesheet” type=”text/css” href=”http://yui.yahooapis.com/2.5.1/build/menu/assets/skins/sam/menu.css”/> <script type=”text/javascript” src=”http://yui.yahooapis.com/2.5.1/build/yahoo-dom-event/yahoo-dom-event.js”></script> <script type=”text/javascript” src=”http://yui.yahooapis.com/2.5.1/build/container/container_core-min.js”></script> <script type=”text/javascript” src=”http://yui.yahooapis.com/2.5.1/build/menu/menu-min.js”></script>

Since, YUI does such a good job documenting how to use their widgets, there isn’t a need to say much about them. The Yahoo developer site has examples showing how to create menus from markup, JavaScript, or some combination of both. I choose the last option, using markup to design the root menu, and JavaScript to build the submenus. You need to apply the class “yui-skin-sam” to the body tag and various other classes to your menu widget, depending on what type of menu you are making. I choose the “Application Menubar” styles, positioning the widget statically at the top left of the page. Give my menu a try:

Game Menu

One important thing to note, is that if you dynamically updating the menu items, as I do when checking and disabling menu items, make sure that lazy-loading is set to false, otherwise, you will find that parts of your menu are undefined.

My menu is still very basic, but does most of what I expect I will want for the game engine. You can start a new game or restart an existing one, change the resolution, or exit the game. Starting a new game will bring up an empty game board, sized 640 X 480, while restart will bring up a new, empty game board that is the same size as the existing window. I am leveraging the ‘disabled’ flag for the menu, so that certain options, such as resolution and restart are not available until the game has started. I also use the ‘checked’ flag, so that you the resolution menu shows which resolution is selected. The view -> resolution menu item allows you to resize the gameboard to the various supported sizes for the game (right now I threw in some test sizes). The exit game doesn’t do anything for now. I’m not sure what it really will do, unless I implement some type of login feature. Lastly, you can add help text to the menus, which I have used to indicate what possible shortcut keys trigger the menu items, although I haven’t wired them up yet.

posted by Matt Snider at 12:49 pm  

Thursday, April 17, 2008

News: Collection of JavaScript Tools And More

I was impressed to see that Smashing Magazine, an online designer magazine, choose to feature a collection of JavaScript resources this week. Some of them are really useful:

60 More AJAX- and JavaScript Solutions For Professional Coding

Douglas Crockford revisited global variables and decided to change his definition of best practices. The gist of which is, instead of using “var foo = ‘value’;” in the global space, you should use:

/*global foo*/ foo = {};

Find out more: Global Domination, Part Two

posted by Matt Snider at 4:42 pm  

Tuesday, April 15, 2008

Document CreateTag Method

As I worked on the GameEngine improvements, I realized that it would be nice to have a menu and several other DOM elements. Since the engine will be standalone, it is necessary to dynamically create the DOM elements required to run the game. For this task, I have use a method “document.createTag()”, which I often refactor for various projects. In its most basic form, it looks like the following:

Example 1: document.createTag

YAHOO.lang.augmentObject(document, { /** * Creates and returns an html element and adds attributes from the hash. * * @method createTag * @param tagName {String} Required. Tag name to create. * @param h {Object} Optional. The hashtable of attributes, styles, and classes; defaults is empty object. * @return {Element} The newly created element; returns null otherwise. * @static */ createTag: function(tagName, h) { var Dom = YAHOO.util.Dom, node = document.createElement(tagName), hash = isType(h, ‘object’) ? h : {}; // iterate through the possible attributes for (var k in hash) { var v = hash[k]; k = k.toLowerCase(); if (isType(v, ’string’) || (’style’ === k && isType(v, ‘object’))) { switch (k) { case ‘classname’: case ‘klass’: case ‘class’: case ‘cls’: Dom.addClass(node, v); break; case ‘cellpadding’: node.cellPadding = v; break; case ‘cellspacing’: node.cellSpacing = v; break; case ‘colspan’: node.colSpan = v; break; case ‘checked’: case ‘disabled’: node[k] = v; break; case ‘rowspan’: node.rowSpan = v; break; case ’style’: // iterate through the style object for (var t in v) { var s = v[t]; if (isType(s, ’string’) || isType(s, ‘number’)) { Dom.setStyle(node, t, s); } }; break; case ‘innerhtml’: case ‘text’: if (isType(v, ’string’) && ! v.match(/< .*?>/) && ! v.match(/&.*?;/)) { node.appendChild(document.createTextNode(v)); } else { node.innerHTML = v; } break; default: node.setAttribute(k, v); break; } } }; return node || null; } });

To use this method, simply include it in your JavaScript source files, then call “document.createTag” passing in two parameters: 1st parameter is the tag name you wish to create, and the 2nd is an Object where the keys are tag attribute names and the values are the attribute values. Using a ‘for … in’ loop we iterate on each key in the Object, evaluating only those values that are strings and numbers, or an object in the case of the style attribute. The switch statement then, normalizes the keys, so the key ‘checked’ does the same thing as ‘cHeCkEd’, because capitalization does matter.

Over the years I have added each case of the switch statement based on issues experienced in various browsers. Most attributes seem to be applied just fine, when you use the ’setAttribute’ method, so this is the default case. Some attributes such as ‘checked’ and ‘disabled’ do not work in all browsers with ’setAttribute’, so they are applied only with dot notation. Some attributes such as ‘cellPadding’ and ‘cellSpacing’ are case-sensitive in certain browsers (and capitalizing them doesn’t break the browsers that lower-case worked in), and/or can only be applied via dot notation, so we special-case each of them. The word ‘class’ is a reserved word in at least IE, so you cannot do “object.class”, instead you need to use “object.className”, and this seems to be supported by all browsers; I also support various ways of storing the classname as a key, because different projects have different naming conventions. Style is a special-case that must contain an object, where the keys are style names, and the values are what you want to set the style to (strings and integers are acceptable). Lastly, the ‘text’ or ‘innerhtml’ key will attempt to append the provided value as a text node, unless the value: isn’t a string, contains HTML elements, or contains HTML special characters; in those cases it uses innerHTML, because it will render HTML and special characters correctly, while a text node will simply print the text as is.

Now, this method probably isn’t perfect, but it works with all the browsers that I have ever needed to support. Feel free to chime in, if you know of any other special-case situations or browsers that you believe this method won’t work with.

This solution is homegrown, and there may exist better DOM node creation methods, with more exposure and support. I know the Framework Ext.js has a DOM node creation method, and I wouldn’t be surprised if Dojo or jQuery have a plugin solution as well.

posted by Matt Snider at 9:46 pm  

Monday, April 14, 2008

Sliding Puzzle Game Using Movable

So, as promised, I will be showcasing a game today that makes use of the Movable Object from last Tuesdays article. I had hoped to get this article out earlier, but my old game engine was too sloppy to use. Instead, I hacked together a new engine, but it is ‘hacked’ together and will currently only work with this game. I intend to experiment with the engine more this next week to see how abstract I can make it, or at least make each element extendable, and fire and listen for Custom Events.

The game is called Sliding Puzzle, this particular variant is called The Fifteen Puzzle. The game objective is to move 15 randomly placed tiles into and out of the one open space on a 4×4, 16-space board (there is 1 open space), until you have arranged them in order. I originally played this game on the 8-bit Nintendo Console, as The Fifteen Puzzle was a hidden, side-game in Final Fantasy One.

To make the game work, I wrote 3 new classes: GameEngine, GameBoard, and GameTile. The GameTile Object will manage the tiles, which wraps the Movable Object and needs to know its position on the board. The GameBoard manages all the tiles, handling tile movement and test for game completeness (win or lose states). The GameEngine should handle pre/post game user interaction, and initialize and manage the other objects. I also improved the Movable Object so that it can be limited to move about a DOM node or the viewport.

Unfortunately, much of the code is still hacked together and the Objects know too much about each other. I intend to work on cleaning the code up this week, but until then, here is a Fifteen Puzzle Game Demo.

posted by Matt Snider at 12:16 am  

Tuesday, April 8, 2008

Movable DOM Node Widget

One of my favorite pastimes is playing and collecting video games, which combined with another of my passions, JavaScript, drives me to figure out ways to port simple gaming engines into JavaScript. JavaScript is not the best language for writing such engines (at least not in the browser), so I do not spend much time on this. However, I had a little time and revisited an old project, replacing the ancient coding with some simple YUI calls. When I wrote this, I was thinking of the original Dragon Warrior game, where you had a character that moved around the screen.

Think of the black rectangle as the character. Using YUI animation utility and a custom Object ‘Movable’, I listen to arrow keydown events and trigger animations on Movable. Because YUI manages duplicate animations on the same style of an Element (preventing it, until the previous animation completes), this was much simpler than code I had previously written. Also, the YUI animation code allows for diagonal movement if you press two perpendicular arrow keys within the half-second animation period. Try moving the black box around the screen on the Movable Object Test Page. Here is the code for the Movable Object:

Example 1: Movable Class

Core.Widget.Movable = function(id, useAnim) { // Local Variables var F = function() {}, node = $(id), that = null, anim = null; // shortcut for viewport size var getWSize = Core.Client.getViewportSize; // Public Variables F.prototype = { /** * Move the object down. * * @method down * @public */ down: function() { var r = Dom.getRegion(node), w = r.bottom - r.top, o = r.bottom, s = getWSize(); // prevent exceeding viewport height if (s.y < o + w) {o = s.y - w;} if (useAnim) { anim = new YAHOO.util.Anim(node, {top: {to: o}}, 1, YAHOO.util.Easing.easeOut); anim.animate(); } else { Dom.setStyle(node, 'top', o + 'px'); } }, /** * Move the object left. * * @method left * @public */ left: function() { var r = Dom.getRegion(node), w = r.right - r.left, o = r.left - w; // prevent exceeding viewport width if (0 > o - w) {o = 0;} if (useAnim) { anim = new YAHOO.util.Anim(node, {left: {to: o}}, 1, YAHOO.util.Easing.easeOut); anim.animate(); } else { Dom.setStyle(node, ‘left’, o + ‘px’); } }, /** * Move the object right. * * @method right * @public */ right: function() { var r = Dom.getRegion(node), w = r.right - r.left, o = r.right, s = getWSize(); // prevent exceeding viewport width if (s.x < o + w) {o = s.x - w;} if (useAnim) { anim = new YAHOO.util.Anim(node, {left: {to: o}}, 1, YAHOO.util.Easing.easeOut); anim.animate(); } else { Dom.setStyle(node, 'left', o + 'px'); } }, /** * Move the object up. * * @method up * @public */ up: function() { var r = Dom.getRegion(node), w = r.bottom - r.top, o = r.top - w; // prevent exceeding viewport height if (0 > o - w) {o = 0;} if (useAnim) { anim = new YAHOO.util.Anim(node, {top: {to: o}}, 1, YAHOO.util.Easing.easeOut); anim.animate(); } else { Dom.setStyle(node, ‘top’, o + ‘px’); } } }; // Scoped Operations that = new F(); return that; };

The ‘Movable’ class is designed to work with any DOM node that is absolutely positioned and smaller than the viewport. Simply pass the DOM node as the first parameter and whether you want the movements to be animated as the second. There is any number of reasons that you may need an Element on a page that can move up and down, based on its size, so you needn’t only think of it as part of a gaming engine. Keep in mind that I have used shorthand notation for ‘YAHOO.util.Dom’ (Dom), and ‘YAHOO.util.Dom.get’ ($). This widget also requires that you include ‘core.js‘.

Lastly, if you want to have it animate on the page, like in my example, you will need to use YUI Event package to listen to all keydown events on the ‘document’. Here is the code that I used:

Example 2: Document Keydown Listener

Event.addListener(document, ‘keydown’, function(e) { var test = Core.Widget.Movable(obj, true); switch (Event.getCharCode(e)) { case 37: // left arrow test.left(); break; case 38: // up arrow test.up(); break; case 39: // right arrow test.right(); break; case 40: // down arrow test.down(); break; } });

Again, I have shorthanded the ‘YAHOO.util.Event’ package to just ‘Event’. This code simply listens for all keydown events, ignoring all but the arrow keys. When any key is fired, it calls the appropriate method on ‘Movable’, which then handles the movement.

Now, the next step of the engine is to have the Object move over something, such as a map or a game board, firing custom events that the engine listens to. My next article will show how the ‘Movable’ object can be used to build a simple game.

posted by Matt Snider at 10:45 pm  

Saturday, April 5, 2008

Catching All JavaScript Errors in All Browsers

I recently wrote an article about the JavaScript “onerror” event. The “onerror” event is really powerful in that you can prevent all JavaScript errors from ever being shown to the user, as well giving you the ability to capture each error and send it to the server for logging. The major downside is that only the major browsers (IE and FireFox) support ‘onerror’, because of this you miss all the goodness in Safari, Opera, and other smaller browsers. I spent some time over the last week and devised a solution that will allow you to catch all errors in all browsers. However, it is rather onerous, so I present it here, but hope that some of you can suggest ways to help improve it.

The sure-fire way to capture all errors in all browsers, is to put a try/catch statement inside each Function. Obviously, doing so would be ridiculous, even though you are trying to solve a huge problem: preventing and capturing JavaScript errors. However, you can instead create a proxy Function that handles the try/catch wrap for you. Instead of calling a Function/Method directly, you will use the proxy Function throughout your code. Inside the proxy, we put a try/catch statement to capture exceptions. The catch statement will send an AJAX message to log the error server-side, and return NULL. Otherwise the proxy Function will return the same value that would have normally been returned by executing the passed method. While, still onerous, this is probably a do-able solution:

Example 1: The Call_User_Method_Array Function

/** * Calls the method, scoped to the passed object, with the set of parameters. * * @method call_user_method_array * @param method_name {String} Required. The method name on ‘obj’. * @param obj {Object} Required. The object owning the method, that the method will also be scoped as. * @param paramarr {Array} Required. The collection of parameters. * @static */ var call_user_method_array = function(method_name, obj, paramarr) { try { return obj[method_name].apply(obj, paramarr || []); } catch (e) { window.onerror(e.message, window.location, ‘-1′); // use -1 for exceptions return null; } };

I modeled the method after a similar method used by PHP. The method should be placed early in global namespace, and I would recommend abbreviating the name to no more than 3 characters as it will be called many, many times. Below are a few examples of how to use this method:

Example 2: How to Use Call_User_Method_Array

// calling a method on window var testFunc = function() {/* do something */ }; call_user_method_array(’testFunc’, window); // calling a method on a string var testString = ‘This is a string’; var index = call_user_method_array(’indexOf’, testString, [’string’]); // calling a nested method var bool = call_user_method_array(’isIE’, Core.Client);
posted by Matt Snider at 3:26 pm  

Wednesday, April 2, 2008

JavaScript Gif-Like Animation

First, I like to say how much I hate spammers. I’d even start believing in the beyond again, if God would only reserve a special place in hell for spammers. 35,000 spam comments and counting… Because of this, I’d like to apologize in advanced if I accidentally delete one of your comments. Also, the first time you comment, it goes into an approval pool, so if you don’t see it right away, please give me a day or two to approve it.

I have been pretty busy prepping for a big announcement that we will be making at Mint.com this week, so I did not have as much time to prepare a blog article as I would have liked. So, I decided to have a little fun today and created a JavaScript driven GIF-Like animation tool.

The basic idea is that you take each frame that you would use in an animated GIF and instead create a separate GIF for each. The next step is to put an IMG tag on a page and set its source to the first image. Then at the end of the webpage inside a SCRIPT tag, instantiate GifAnimator, passing in the ID of the IMG tag as the first parameter, and an Array of image sources (your frames) as the second image. Then, go ahead and call the ’start’ method of your new GifAnimator Object to begin animation.

Example 1: Instantiating GifAnimator

<script type=”text/javascript”> var anim = new Core.Widget.GifAnimator(’animator’, [ ‘assets/images/animator/A.gif’, ‘assets/images/animator/B.gif’, ‘assets/images/animator/C.gif’, ‘assets/images/animator/D.gif’, ‘assets/images/animator/E.gif’, ‘assets/images/animator/F.gif’ ]); setTimeout(function() { anim.start(); }, 1000); </script>

Download GifAnimator.js and include it into your project to make Example 1 work. I have created a test page as well.

Hopefully, you will remember that I started this article by saying that I was playing. There is little reason to replace GIF animation with a JavaScript solution, as JavaScript timeouts are fairly inaccurate, and they have a tendency to pause every few seconds as the processor catches up with the browser.

posted by Matt Snider at 10:52 am  

Powered by WordPress