How To Improve YUI Dom hasClass


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:

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:

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.