Javascript getFirstDescendantBy()


Update – Jan 22, 2009
A new native YUI function is in the works, and does this job better


Recently I was working on optimizing some javascript, and found a slow area that was trying to find the first focusable input in a certain area of the page, and it was taking anywhere from 100 – 500 milliseconds, depending on the size of the DOM tree in that element. After digging into it, I noticed it was using the YAHOO.util.dom.getElementsBy() method, which basically had to walk through the whole DOM tree in this case, testing each node against the boolean method passed in. After calling that, it would then return the first, if any, element that getElementsBy returned. Obviously this was a bad approach, as after you find the first match, there is no need to go further.

I did a little research, and saw that this had come up in a thread on the YUI group. I ended up writing a small method to fill in this functionality I wanted out of YUI, called getFirstDescendantBy(rootEl, method). Below is the code, the function takes a root element, or string id of that element, and then a function to test each element against that has the element being tested as the only input. This function passed in should return a boolean, and is similar to the way the YUI dom function getElementsBy works. Hopefully this will help out some people in similar situations.

function getFirstDescendantBy(rootEl, method) {
	var root = typeof rootEl === 'string' ? document.getElementById(rootEl) : rootEl;
	var firstDescendant = null;
	var children = root.childNodes;
	for(var idx in children) {
		var child = children[idx];
		if(child.nodeType === 1) {
			if(method(child)) {
				firstDescendant = child;
				break;
			}else if(child.childNodes.length > 0) {
				var recursiveResult = getFirstDescendantBy(child, method);
				if(recursiveResult !== null) {
					firstDescendant = recursiveResult;
					break;
				}
			}
		}
	}
	return firstDescendant;
}
  • del.icio.us
  • Digg
Tagged , | 4 Comments

Back to Firefox 2

If you’re like me, you use Firebug a lot, like, all day long. Long gone are the days of resorting to alerts. The worst I’ll fall back to is using the YUI logger for IE.

I’ve recently been having a lot of issues using Firefox 3, and Firebug. Firefox 3 has been pretty consistent at crashing a few times throughout the day. Firebug 1.2 has recently been causing me more and more troubles, to the point where I can’t get it to not stop at breakpoints that I’ve removed, or it stops at my breakpoint, but doesn’t let me play it through or debug, or remove the breakpoint, because it doesn’t show up as being registered.

I’m sure these are hiccups with the new version of Firefox 3, but it is pretty disruptive to me as I’m developing. I reverted back to Firefox 2 and an older version of Firebug until they get this sorted out, which I’m sure will be soon. For anyone looking, you can find old versions of Firefox on FileHippo, and past versions of Firebug from the normal download site.

  • del.icio.us
  • Digg
Tagged , | 1 Comment

Complex Javascript Event Handling: EventMediator

A large enterprise sized project I work on uses YUI library extensively, and events are a huge part of the rich front end we’re developing. What you start to learn quickly about UI events, is that dependencies between different events start to get very complicated very fast. When A depends on B, but B needs to wait for C and D and E to finish, but E needs to wait for F to finish, you have a complex situation on your hands. Up until recently we were able to rely mostly on just using CustomEvents in YUI to handle this for us.

Where that starts to break down is when you have one event, A, that is dependent on multiple other events, B, C, D. You now have to manually keep track of what has fired, and make sure you don’t fire that A until B, C and D have all fired. With very little code, here is a simple class that can be used in conjunction with YIU CustomEvents. It will handle the ‘book-keeping’ of firing what you want when all the events you have designated fire.

EventMediator = {

	addActivationRecord : function(record) {
		record = record || {};
		var that = this;
		for(var idx in record.events) {
			var eventRecord = record.events[idx];
			if(eventRecord.event !== null) {
				eventRecord.fired = false;
				eventRecord.event.subscribe(function(scopedEvent) {
					return function(e) {
						scopedEvent.fired = true;
						that.fireActivation(record);
					}
				}(eventRecord));
			}
		}
	},

	fireActivation : function(record) {
		var fired = true;
		for(var idx in record.events) {
			if(record.events[idx].fired === false) {
				fired = false;
				break;
			}
		}
		if(fired === true) {
			record.activate.call(record.scope);
		}
	}

};

Using the EventMediator would look something like the following:

EventMediator.addActivationRecord({
	events : [
		{ event : myObj.someYUICusomEvent },
		{ event : myObj.anotherYUICusomEvent }
	],
	activate : myObj1.myActivationCallback,
	scope : myObj1
});

It’s pretty straightforward I think. You call addActivationRecord, passing in an array of objects, whose ‘event’ property points to a YUI CustomEvent. You also provide an activate callback method, and give it a scope in which to call the method. For my purposes so far this has worked pretty well, although I’m sure it could be built up to be much more robust. Hope it helps someone out!

  • del.icio.us
  • Digg
Tagged , | 4 Comments

Subscribing to an image loaded event

I recently was working on something where I needed to swap the src of an image, but then perform some specific javascript AFTER the new image was loaded, namely resize the dimensions a little. This may seem pretty simple, but took me a little research, along with trial and error to get a working solution, so hopefully it helps someone else. I often use jQuery on my smaller projects, so I’ll show how I accomplished this with that toolkit.

  1. First, I would need to listen to the load event of the image I was replacing the src of.
  2. Next, in order to trigger the handler, we need to actually change the src of the image
$('#my-image').load(function() {
    //Logic for w/e here, resizing, etc.
}).attr('src', myNewImageSrc);

The code itself is really simple, but it took me a few to realize that jQuery solved the problem by simply letting us listen to the load event of the image, and count on it being fired when the src changes, and finishes loading.

  • del.icio.us
  • Digg
Tagged , | Leave a comment

slickifying Slikcalc to version 1.0

A new version of Slikcalc (1.0) was released. This release simplified some code, mainly the slikcalc.formatCurrency() method. There was also a bug where a calculator could be initialized more than once depending on timing that was fixed. As always, feel free to leave feedback on the library. Enjoy.

Download Slikcalc 1.0

  • del.icio.us
  • Digg
Tagged , | Leave a comment