Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Saturday, July 28, 2007

Specificity

You are probably asking yourself, what is specificity and how does it apply to JavaScript?

Specificity is the measure of importance associated with a CSS declaration. This is one of the technologies that makes cascading stylesheets so powerful. If you find yourself attaching classes and not seeing the styles applied, then it is probably a specificity issue. Anyway, Smashing Magazine wrote a really good explanation. Check it out:

CSS Specificity: Things You Should Know

posted by Matt Snider at 11:56 am  

Thursday, July 26, 2007

Detect when DOM Element is in viewport

I just read a good article by Dustin Diaz (formerly of YAHOO) about detecting where elements are in regards to scrolling and the viewport. It is simple and very powerful, so I thought I would share it with you. Also, I am overworked right now and have not had time to write any material of my own :(.

element-scroll-into-view

posted by Matt Snider at 3:49 pm  

Tuesday, July 24, 2007

More on Module Pattern

I posted a link, about a month ago, to a presentation by Douglas Crockford explaining the “Module Pattern” of JavaScript programming. Here is more explanation by Christian Heilmann @ Wait ’till I Come.

Show love to the Module Pattern

posted by Matt Snider at 2:19 pm  

Friday, July 13, 2007

Pagination Algorithm in JavaScript

I recently added pagination on two sites and in the process developed a solid pagination algorithm, which I wanted to share with you. To start, I read this article from KuraFire Network:

Pagination 101

My take away from that article was the following pagination spec:

  1. should be functional, simple, and elegant
  2. should include prev/next and first/last as needed
  3. visible only when applicable, hidden otherwise
  4. don’t show prev when on the first page
  5. don’t show next when on the last page
  6. don’t show page numbers when only two pages (only show prev/next)
  7. don’t show ‘first’ when page ‘1′ is visible in pagination
  8. don’t show ‘last’ when the last page is visible in pagination
  9. clearly show the currently selected page (with the exception of the 2 page simplified view)
  10. when there are more pages than the maximum page #s shown, then the currently selected page should be intelligently centered

Pagination Spec Explained:

Probably, the only issue I deviated from the Pagination 101 article is that I do not show page numbers when there are only 2 pages. I found they added extra clutter and confusion, so I removed it. You really only have 2 options anyway and you are not adding (improving?) functionality by including the page numbers. ‘Prev’ and ‘Next’ are included because many users move directionally through pagination. ‘First’ and ‘Last’ are included (as needed), because in large result sets you need a way to get back to the beginning or end easily. Everything else is to improve simplicity and visibility of the selected page.

Below is an example of my algorithm. Keep in mind that ‘0′ actually refers to page ‘1′ and the last page is always ‘numpages - 1′; Most likely you will want to use this server-side, however, I rewrote it in JavaScript, because of copyright issues and this blog is about JavaScript. To sum up, if you follow the instructions in the method JavaDoc, you will create a string containing an unorder list containing the appropriate pagination to match what was passed in. Some additional explanation included below.

/**
 *  Create a pagination DOM or String as needed
 *  @param  count {Integer} the number of results
 *  @param  resultsPerPage {Integer} the maximum per page
 *  @param  uri {String} the uri for the pagination
 *  @param  offset {String} OPTIONAL: the offset of the current page
 *  @param  sClass {String} OPTIONAL: the selected page class
 *  @return {String} the DOM string or empty string when DOM object is passed
 */
function buildPagination(count, resultsPerPage, uri, offset, sClass) {
	if (! offset || 0 > offset) {offset=0;}
	if (! isString(sClass)) {sClass = '';}
	//
	var str = [],
		maxPages = 10,
		halfMax = maxPages / 2,
		numpages = Math.ceil(count / resultsPerPage),
		index = Math.ceil(offset / resultsPerPage),
		findex = index - halfMax,
		lindex = index + halfMax;
//
	// validate index
	if (numpages - 1 < lindex) {lindex = numpages - 1;}
	else if (lindex < maxPages) {lindex = maxPages - 1;}
	if (0 > findex || index < maxPages - 1) {findex = 0;}
	else {findex = numpages - maxPages;}
	if (0 > index) {index = findex;}
	if (numpages < index) {index = lindex;}
//
	str.push('<ul>');
//
	// if the first index is not 0 then the pagination view does not include page 1, so display first
	if (findex) {
		// logic to create the 'first' link
		str.push('<li><a href="');
		str.push(uri);
		str.push('" rel="0">first</a></li>');
	}
//
	// if the index is not 0 then we are not on the first page, so display previous
	if (index) {
		// logic to create the 'previous' link
		str.push('<li><a href="');
		str.push(uri);
		str.push('" rel="');
		str.push(offset - resultsPerPage);
		str.push('">first</a></li>');
	}
//
	// if there are more than 2 pages, then show page numbers, otherwise, just next/prev for simplicity
	if (2 < numpages) {
		for (var i=findex; i<=lindex; i++) {
			// logic to create the page '#' link
			if (i == index) {
				str.push('<li><a href="');
				str.push(uri);
				str.push('" rel="');
				str.push(offset);
				str.push('">');
				str.push(i);
				str.push('</a></li>');
			}
			else {
				str.push('<li class="');
				str.push(sClass);
				str.push('">');
				str.push(i);
				str.push('</li>');
			}
		}
	}
//
	// if the index is equal to the last index then we are not on the last page, so display next
	if (lindex != index) {
		// logic to create the 'next' link
		str.push('<li><a href="');
		str.push(uri);
		str.push('" rel="');
		str.push(offset + resultsPerPage);
		str.push('">next</a></li>');
	}
//
	// if the last index is equals to (maxPages - 1) then the pagination view does not include the last page, so display last
	if (lindex == maxPages-1) {
		// logic to create the 'last' link
		str.push('<li><a href="');
		str.push(uri);
		str.push('" rel="');
		str.push((numpages - 1) * resultsPerPage);
		str.push('">last</a></li>');
	}
//
	return str.join('');
}

More to come. In the process of writing a spreadsheet that steps through the algorithm with many different values so that you can see it manipulated.

posted by Matt Snider at 6:29 pm  

Sunday, July 8, 2007

How to improve YUI hasClass method

On my project, Mint, I have reached a point, where the code is mostly stable and decided to start looking for ways to improve the performance. I checked out various JavaScript profiling techniques, deciding that the Firebug profiler was probably the most accurate. I then started profiling the different pages and realized (to my surprise) that a lot of time spent in the ‘YAHOO.util.Dom.hasClass’ method. I use ‘getElementsByClass’ method a lot, so this method is hit frequently. Therefore, since ‘hasClass’ was used the most, any performance improvement to that method would generally improve the performance of Mint.

Here is the YUI method found in dom.js:

         /**
         * Determines whether an HTMLElement has the given className.
         * @method hasClass
         * @param {String | HTMLElement | Array} el The element or collection to test
         * @param {String} className the class name to search for
         * @return {Boolean | Array} A boolean value or array of boolean values
         */
        hasClass: function(el, className) {
            var re = new RegExp('(?:^|\s+)' + className + '(?:\s+|$)');

            var f = function(el) {
                return re.test(el.className);
            };

            return Y.Dom.batch(el, f, Y.Dom, true);
        },

So what is taking a long time here? creating and executing the regular expression.

How do I improve this? replace the regex with something faster or find a way to create something that is already instantiated before this method is hit

I first choose to replace the regular expression (regex) with a call to ’split’ method, of String, on the DOMElements ‘className’. I noticed about a ‘0.5′ millisecond (ms) response time improvement to the execution time of the ‘hasClass’ method and almost a ‘0.1′ ms improvement to the whole function stack. The ‘0.05′ ms improvement is because converting the ‘className’ into an array using ’split’ is that faster than creating a new regex. Also, the execution of ‘batch’ and ‘f’ Functions are improved a combined ‘0.05′ ms, because executing the ‘indexOf’ method on a small array is faster than ‘test’ method of the regex. So, replacing the regex with an array does improve the execution time by a reasonable amount. It will be hard to notice the improved performance on any data set < 1000, however, it is always faster, so you are not hurting by using this change with smaller data sets.

Here is what I came up with:

 /**
		 *  Determines whether an DOMElement has the given className
		 *  @param  el {String/DOMElement/Array}        The element or collection to test
		 *  @param  className {String}                  The class name to search for
		 *  @return {Boolean/Array}                     A boolean value or array of boolean values
		 */
		hasClass: function(el, className) {
			var re = el.className.split(' ');
			var f = function(el) {
				return -1 != re.indexOf(className);
			};
			return Y.Dom.batch(el, f, Y.Dom, true);
		},

I compiled all my tests into Table 1A. The table shows the ms of ‘owned time’ (time the execution thread was actually in that method) that each function had. I setup a simple page with 3 div elements, one without a class, one with a single class, and one with several classes. I then grabbed one element at a time and executed ‘hasClass’ on it 1000 times. I did this 5 times, then took the average of those runs and recorded it as a trial. So each trial consists of 5000 calls to ‘hasClass’. I then modified the ‘hasClass’ method and repeated. ‘hasClass’ calls ‘batch’, which calls the ‘f’ Function, so I have included their ‘owned time’ as well. I did not throw out outliers as I was only proving that the Array method is faster than the Regex method and not trying to accurately estimate how much faster.

Table 1A:
http://spreadsheets0.google.com/ccc?key=pJL0oH5TVaRjjog0qePS2bQ&hl=en_US

Something this data shows that warrants further investigation is: why does ‘indexOf’ method execute marginally faster on an array with a single element versus an Array with no elements? If you look at the average runtime of the ‘f’ Function using the new method, the runtime of ‘f’ is slightly higher when there is no class than when there is one. I produced two more sets of data for each case and got comparable results.

I would have liked to try an approach that moves the instantiation completely out of the method, but I couldn’t think of a good technique. If you have any ideas how to further improve this, let me know.

posted by Matt Snider at 3:56 pm  

Wednesday, July 4, 2007

Faster Loops With JavaScript

Sorry, that I have not posted about any Frameworks in the past two weeks; I am overwhelmed with my work. However, tonight I cannot sleep, so I thought I would write something. I was writing an event handler function and realized that I was coding loops in a way that is not usually possible in other languages. Let’s discuss this technique, which can help improve the run-time of certain iterative for loops.

Suppose you have an event callback function with multiple triggers and a collection of something that you need to iterate through each time the callback function is made (like rows in a table or list items in a list). You then need to do something different to the collection depending on what trigger hit the callback function. In most languages you would have a for loop containing a switch statement with N cases (or if statements if you absolutely cannot use a switch). Here is an example that executes a function on each list item depending on the trigger id:

function callback(e) {
	var items = document.body.getElementsByTagName('li');
	var trigger = Event.getTarget(e); // this is a Framework function that is really useful
	var id = trigger.id;
	for (var i=0, j=items.length; i<j; i++) {
		switch (id) {
			case 'id1':
				someFunctionForId1(items[i]);
				break;
			case 'id2':
				someFunctionForId2(items[i]);
				break;
			case 'id3':
				someFunctionForId3(items[i]);
				break;
			case 'id4':
				someFunctionForId4(items[i]);
				break;
			default: // any other id
				someFunctionForId5(items[i]);
				break;
		}
	}
}

This is perfectly valid code and will work great on small sets, because the switch statement has a very small run-time cost (switches are very quick seek structures in JavaScript), and on a small set, you will not execute the statement many times. The runtime as an equation would be:


k1 * n = r
where k1 = the constant cost of the switch
n = the number of values in the array
and r = run-time

There is a better way to write this function, where the select statement is only executed once:

function callback(e) {
	var items = document.body.getElementsByTagName('li');
	var trigger = Event.getTarget(e); // this is a Framework function that is really useful
	var id = trigger.id;
	var fx = null;
	switch (id) {
		case 'id1':
			fx = someFunctionForId1;
			break;
		case 'id2':
			fx = someFunctionForId2;
			break;
		case 'id3':
			fx = someFunctionForId3;
			break;
		case 'id4':
			fx = someFunctionForId4;
			break;
		default: // any other id
			fx = someFunctionForId5;
			break;
	}
	for (var i=0, j=items.length; i<j; i++) {
		fx(item[i]);
	}
}

For this callback the runtime is:


(k2 + k1) + (c * n) = r
where k1 = the constant cost of the switch
k2 = the constant cost of assigning the function to variable fx
c = the cost of seeking and executing the function fx
n = the number of values in the array
and r = run-time

I have not done the actually runtime analysis, so I do not have the exact numbers, but you should not have to stretch your imagination far to assume that the cost of c < k1, so as long as the number of iterations (n) are overwhelm the constant (k1 + k2), then the latter algorithm is faster. The exact equation for this comparison is:


(k1 + k2) + (c * n) = k1 * n
where k1 = the constant cost of the switch
where k2 = the constant cost of assigning the function to variable fx
c = the cost of seeking and executing the function fx
n = the number of values in the array

computing for n, you get:

n = (k1 + k2) / (k1 - c)

If you have time, try profiling some loop code to figure the exact numbers. My assumption is that n is between 10 & 20, so anytime I iterate on a list greater than 10 and can use this pattern, I do.

posted by Matt Snider at 3:07 pm  

Monday, July 2, 2007

jQuery, iFirebug, and iPhone Programming

Some important events happened in the JavaScript community this past week: new jQuery release, Joe Hewitt wrote an iPhone extension for Firebug, and developing for the iPhone is getting a lot of hype. Here are some articles that do a great job covering this:

posted by Matt Snider at 2:31 pm  

Powered by WordPress