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";
}
2 Answers 2
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'))
-
\$\begingroup\$ Nice, I would have suggested
reduce
for determining the max height. \$\endgroup\$konijn– konijn2014年11月10日 18:11:06 +00:00Commented Nov 10, 2014 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– Blake Walsh2014年11月10日 21:07:31 +00:00Commented Nov 10, 2014 at 21:07
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";
}