Finding Element Positions
Date : 2007 07 25 Category : Design & UsabilityJavascript developers commonly need to find out the position of an element in relation to other elements. For example, if there is a list of elements that need to be reordered, the position of the element being reordered needs to be found and updated. It turns out there are quite a few ways to go about doing this, especially when using a library with a ton of helper functions. Let’s take a look at 3 common ways of approaching this with prototype.js to see how they compare in ease of use and speed of execution.
The ExperimentThe markup is setup as a standard unordered list, and within the list is a target node. For this test, the target node is identified as a class name. In real usage examples, this could be a class name, id, innerHTML property, and so on.
<ul id="lis"> <li>1</li> <li>2</li> <li id="me" class="target">3</li> </ul>The experiment example is constructed of 800 list elements, and the results should be the position of the target list in relation to the other list elements. For example, the code above should result in 3 being returned.
Standard LoopThe first approach that comes to mind is to loop through all child nodes until a child with a class name of target is found. Once found, returning the index of the loop will represent the position of the element.
var el = $('lis').immediateDescendants(); var count = el.length; var ret = -1; for(var i = 0; i < count; i++) { if(el[i].hasClassName('target')) ret = i; } The Find MethodSimilar to the standard approach, this approach utilizes the convenience of the prototype find method.
var el = $('lis'); var ret = -1; el.immediateDescendants().find(function(num, index) { if(num.hasClassName('target')) ret = index; });Arguably, this approach is easier to read, and will most likely be more consistent with the rest of your code if your a fan of prototype.
Previous SiblingsBy counting the amount of previous siblings an element has, the relative position is revealed. If anyone else out there has a thought process similar to mine, this approach is not the first approach taken. But it turns out the code is quite clean and efficient.
var el = $('me'); var ret = el.previousSiblings().length;It is important to note that the use of this method is dependent on having a handle on the object. This can be done be accessing the this object on an event triggered by the element, or by targeting it by its id or a similar property. I have found that I often do have a handle on the element, which is why I have included this approach in the tests.
The ResultsEach of the tests above were run in Safari 3, Firefox 2.0.0.5, IE6 and IE7. The speed results are below (in seconds):
Safari 3 Firefox 2.0.0.5 IE6 IE7 Loop0.0640.1641.5221.105 Find0.0680.1781.5321.131 Siblings0.0180.0761.4020.995So what can we take from this other than the fact that Safari 3 is super fast and IE6 is super slow? Well, the previous siblings approach is the best approach in my opinion. Obviously, it is rare to have 800 elements in a list, but the clean code and the potentially recognizable speed benefits are worth making this approach the one to default to. Between looping and finding, I would go with finding just for code consistency. Even though it is slower, the speed difference will never be noticed by a user.
