5
\$\begingroup\$

I'm pushing myself to learn pure JavaScript, so this morning I wrote an equal heights script in jQuery and then converted it to JavaScript. It works, but I'm using two for loops. Is there a better or more efficient way to write this code, maybe by finding a way to remove the second for loop?

I've created a jsfiddle to make is a little easier to test.

var  maxHeight = -1
,    elems = document.getElementsByClassName("features");

for( var i = 0; i < elems.length; i++ )
{
    maxHeight = maxHeight > elems[i].offsetHeight ? maxHeight : elems[i].offsetHeight;
}

for( var i = 0; i < elems.length; i++ )
{
    elems[i].style.height = maxHeight + "px";
}
\$\endgroup\$
2
\$\begingroup\$

It would be cleaner to write the maximum value comparison like this:

    maxHeight = Math.max(maxHeight, elems[i].offsetHeight)

In this case, using the 'max' function follows the DRY principle.

It is common in javascript to not use for loops and instead use the functional-style .forEach (.each in jQuery)

The return type of getElementsByClassName is not Array (it is an array like object), but it can be converted to a real array using the slice array method and then the normal Array methods can be used:

var maxHeight = -1,
    elems = document.getElementsByClassName("features");
elems = [].slice.call(elems) // Now a true Array

elems.forEach(function(e){
    maxHeight = Math.max(maxHeight, e.offsetHeight);
});

elems.forEach(function(e){
    e.style.height = maxHeight + "px";
});

In this case the functional style is a bit more concise even if it looks a little weird coming from some other mainstream languages - it's more concise mainly because it completely eliminates the index variable and hands you the object directly. This kind of idiom is extremely common in the javascript world and you will find it used extensively with libraries like jQuery, and once you get used to it you will probably almost never use traditional for loops. In this case there might not seem to be a compelling reason why it's better but it's certainly the conventional way to code in javascript.

Advanced methods for calculating the maximum

Math.max can take any number of arguments, for example max(5,3,2,1). You can call a function using an array for arguments by using the apply method on a function. We can use the map array method to get an array of heights suitable for calling max with.

maxHeight = Math.max.apply(null, elems.map(function(e){return e.offsetHeight}))

It is also possible to use the reduce array method to accomplish the same task:

maxHeight = elems.reduce(function(value, e){return Math.max(value, e.offsetHeight)}, -Infinity)

The question pertains to 'pure javascript' so to talk about libraries is a little off-scope, but the above examples perhaps show why no-one really uses 'pure javascript', you need to do some seriously weird stuff to accomplish things which should be a simple function call. If I were to do this for real, I'd use the underscore library which adds a bunch of functions which would normally exist as builtins or in the standard library of a language. With underscore, the max is simple and concise:

maxHeight = _.max(_.pluck(elems, 'offsetHeight')) 
\$\endgroup\$
  • \$\begingroup\$ Nice, I would have suggested reduce for determining the max height. \$\endgroup\$ – konijn Nov 10 '14 at 18:11
  • \$\begingroup\$ @konijn personally I'd use Math.max.apply with an arguments array. I've added a section on some 'advanced' methods for determining the max including your suggestion. \$\endgroup\$ – Blake Walsh Nov 10 '14 at 21:07
3
\$\begingroup\$

The two loops are necessary: to know the max value, you need to make one complete pass. There's no way around it. Only after you know the max value, after checking all items, you need a second pass to set the height to the max.

In terms of writing style, there isn't really a standard, and it may be a matter of taste, but in any case I prefer this way:

var maxHeight = -1;
var elems = document.getElementsByClassName("features");

for (var i = 0; i < elems.length; i++) {
    maxHeight = maxHeight > elems[i].offsetHeight ? maxHeight : elems[i].offsetHeight;
}

for (var i = 0; i < elems.length; i++) {
    elems[i].style.height = maxHeight + "px";
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.