2
\$\begingroup\$

I'm running a jQuery function to equalize heights. I want to equalize the heights of several pairs of elements, and can't think of a way to do so without repeating the whole function.

How can I combine the separate functions below into one function that equalizes the heights of each pair of <div>s without forcing all three pairs to be the same height as each other?

//Equalize heights with jQuery
var maxHeight = 0;
$(".example-1").each(function(){
   if ($(this).height() > maxHeight) { maxHeight = $(this).height(); }
});
$(".example-1").height(maxHeight);

var maxHeight = 0;
$(".example-2").each(function(){
   if ($(this).height() > maxHeight) { maxHeight = $(this).height(); }
});
$(".example-2").height(maxHeight);

var maxHeight = 0;
$(".example-3").each(function(){
   if ($(this).height() > maxHeight) { maxHeight = $(this).height(); }
});
$(".example-3").height(maxHeight);
/* CSS borders for illustration purposes */
div { border-width: 1px; border-style: solid; }
div.example-1 { border-color: red; }
div.example-2 { border-color: green; }
div.example-3 { border-color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- HTML – 3 x separate pairs of <div> elements -->
<div class="example-1">
    // div content here
</div>
<div class="example-1">
    // div content here
</div>

<div class="example-2">
    // div content here<br>
    // a second line
</div>
<div class="example-2">
    // div content here
</div>

<div class="example-3">
    // div content here
</div>
<div class="example-3">
    // div content here<br>
    // a second line<br>
    // and a third line
</div>

The jQuery is the code to be reviewed in this question; the CSS and HTML are for illustration.

\$\endgroup\$
  • \$\begingroup\$ I've added the HTML and simplified the example code. The CSS isn't affecting the example, so I've left it out. Hopefully the context is clearer now? \$\endgroup\$ – jasonbradberry Mar 27 '15 at 15:09
  • \$\begingroup\$ I think can be solved only with css. Also, you can add a working example, with jsfiddle.net. (jsfiddle.net/pjbh0n2s) \$\endgroup\$ – vmariano Mar 27 '15 at 16:01
  • 1
    \$\begingroup\$ Are the divs really laid out vertically as in your example? If each pair is laid out side by side, please post your real HTML and CSS, because there may be completely different techniques to implement that layout. \$\endgroup\$ – 200_success Mar 27 '15 at 16:36
  • \$\begingroup\$ @200_success What difference could vertical vs side by side possibly make?? Are they still not pairs one way or the other? \$\endgroup\$ – Misunderstood Mar 28 '15 at 6:56
  • \$\begingroup\$ The review I'm looking for is how to combine the jQuery functions into one. The question I'm asking is not about achieving a certain layout, it's about combining the scripts. \$\endgroup\$ – jasonbradberry Mar 28 '15 at 12:13
2
\$\begingroup\$

You can extract the repeated code on the same function:

function setMaxHeight ($element) {
    var maxHeight = 0;
    $element.each(function(){
       var $this = $(this);
       if ($this.height() > maxHeight) { 
           maxHeight = $this.height(); 
       }
    });
    $element.height(maxHeight);
}

setMaxHeight($(".example-1"));
setMaxHeight($(".example-2"));
setMaxHeight($(".example-3"));
| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ You can also use a comma delimited CSS selector to combine that statement for even more brownie points - setMaxHeight($(".example-1, .example-2, .example-3")); \$\endgroup\$ – Dan Pantry Mar 28 '15 at 14:47
  • \$\begingroup\$ @vmariano While scrolling I accidentally (really) had clicked on your down vote. I cannot undo it until you edit your answer. Interesting thing I noticed about jQuery. It has to do with how it adapts the code executed, for Browser compatibility. jQuery will also use more efficient JS on newer Browsers. It is much slower on IE 8 (600%) and some other older Browsers I test with (300-400%). On an old laptop with Win 7 and FireFox, jQuery does the best at 150-200%. I try to use old JS from DOM Level 1 for best compatibility. To me leading edge technology is Bleeding edge. \$\endgroup\$ – Misunderstood Mar 28 '15 at 18:08
2
\$\begingroup\$

There are a few issues with the original approach. It is far better to use the id rather than class to find the div pairs. jQuery has a fair amount of unnecessary work to do to find the classes for the code $(".example-1").each.

It is what you do NOT see, that matters here. jQuery makes it appear simple. It could be simple if the id is used rather than class. But still jQuery does not know what is actually wanted, it has to use generalized computations which compounds the cpu time.

The far better approach would be to use the Math.max to get the greater of the two heights rather than comparisons and inefficient if else structures. This would eliminate a lot of inefficient code.

jQuery has to scan all DOM elements, retrieve the class, then compare the class to "example-x". Too much like work.

Here is a simple JavaScript routine to resize the height of 5 pairs by their id. By class would not be as simple but the second snippet uses a routine to find the pairs then adjusts the height using the method of this first snippet.

This code in both snippets should be compatible with all Browsers going back to IE 5.
Tested with IE 8.0 (2009), opera 12 (2013), Safari 3.2 (2007), FireFox 36, and Chrome 40

pair('d01','d11');
pair('d02','d12');
pair('d03','d13');
pair('d04','d14');
pair('d099','d199');

function pair(d1,d2){
  var div1 = document.getElementById(d1);
  var div2 = document.getElementById(d2);
  h = Math.max(div1.offsetHeight,div2.offsetHeight);
  div1.style.height=h + 'px';
  div2.style.height=h + 'px';
}
div{border:1px solid #f00;}
<div>
<div id="d01"><p>1 of two<br/>2 lines</p>  </div>
<div id="d11"><p>single line height of 2 lines</p>  </div>

<div id="d02"><p></p>single line height of 3 lines </div>
<div id="d12"><p></p>1 of three<br/>2<br/>3 lines  </div>

<div id="d03"><p></p>1 of four<br/>2<br/>3<br/>4 lines  </div>
<div id="d13"><p></p>single line height of 4 lines  </div>
<div id="d04"><p></p>single line  height of 5 lines </div>
<div id="d14"><p></p>1 of five<br/>2<br/>3<br/>4<br/>5 lines  </div>
<div id="d05"><p></p>1 of six<br/>2<br/>3<br/>4;<br/>5<br/>6 lines  </div>
</div>

I still prefer using id over class to pair the divs. If the class were absolutely necessary the code would still be similar by retrieving all divs

To get by class Replace this section:

did = divs[div].getAttribute("id");
  if (did != null){
    if (did.substring(0,1) == "d"){

With something like (untested):

 if (divs[div].className.substring(0,8 === "example-") {
    pair = divs[div].className.substring(9,1);
    objs[pair]++;
    obj = objs[pair]

I can get the divs with divs = document.getElementsByTagName("div");, whereas jQuery does not know that the example-x class is only used on divs. jQuery has to get all elements and compare every one of them with (in my opinion) a long string. that is not a huge issue with this small example HTML, but on a real life web page it is a huge issue.

Auto Find Pairs

Rather than manually type in the pairs like this:

pair(d01,d11);
pair(d02,d12);
pair(d03,d13);
pair(d04,d14);
pair(d099,d199);

You could just add pairs to the HTML and this routine will automatically find the pairs of divs. There are just a few rules to the id naming convention.

This javaScript routine works with between 1 and 100 pairs of divs.
It could be expanded to more divs by adding more digits to the id

The pairs must be the only divs where the id starts with "d".
In the pair, one div must start with "d0" and the other "d1".
The following digit or two digits are the pair number.

id="d0x" is paired with id="d1x" where x = 0 through 99.

This snippet uses the same id pair numbers as the previous snippet: d0x and d1x and pair numbers (x) are 1,2,3,4,and 99.

The first part of the JS finds the div pairs
The last part sets the height to the max height of the pair.

var pairs = [];
var divs = document.getElementsByTagName("div");
var d = [[0],[1]];
for (var div=0; div<divs.length; div++){
  did = divs[div].getAttribute("id");
  if (did != null){
    if (did.substring(0,1) == "d"){
      var pair = parseInt(did.substring(2,4));
      var obj = parseInt(did.substring(1,2));
      d[obj][pair] = divs[div];
      pairs.push(pair);
    }
  }
}
divs = null;



var p = 0;
for (var pair=0; pair<pairs.length; pair++){
  p = pairs[pair];
  var h = Math.max(d[0][p].offsetHeight,d[1][p].offsetHeight);
  d[0][p].style.height=h + 'px';
  d[1][p].style.height=h + 'px';
}
d = null;
pairs = null;
div{border:1px solid #f00;}
<div>
<div id="d01"><p>1 of two<br/>2 lines</p>  </div>
<div id="d11"><p>single line height of 2 lines</p>  </div>

<div id="d02"><p></p>single line height of 3 lines </div>
<div id="d12"><p></p>1 of three<br/>2<br/>3 lines  </div>

<div id="d03"><p></p>1 of four<br/>2<br/>3<br/>4 lines  </div>
<div id="d13"><p></p>single line height of 4 lines  </div>
<div id="d04"><p></p>single line  height of 5 lines </div>
<div id="d14"><p></p>1 of five<br/>2<br/>3<br/>4<br/>5 lines  </div>
<div id="d099"><p></p>1 of six<br/>2<br/>3<br/>4;<br/>5<br/>6 lines  </div>
<div id="d199"><p></p>single line height of 6 lines  </div>
</div>

Benchmark

This Benchmark was request by @vmariano who posted a jQuery Answer to this post.

var start = new Date();
for (var loop=0; loop<100; loop++){
function setMaxHeight ($element) {
    var maxHeight = 0;
    $element.each(function(){
       var $this = $(this);
       if ($this.height() > maxHeight) { 
           maxHeight = $this.height(); 
       }
    });
    $element.height(maxHeight);
}


setMaxHeight($(".example-1"));
setMaxHeight($(".example-2"));
setMaxHeight($(".example-3"));
setMaxHeight($(".example-4"));
setMaxHeight($(".example-5"));

}
var end = new Date();
var elapsed1 = (end - start)/1000;
console.log(elapsed1);

start = new Date();
var brk = true;
for (var loop=0; loop<100; loop++){
function pair(d1,d2){
  var div1 = document.getElementById(d1);
  var div2 = document.getElementById(d2);
  h = Math.max(div1.offsetHeight,div2.offsetHeight);
  div1.style.height=h + 'px';
  div2.style.height=h + 'px';
}
pair('d01','d11');
pair('d02','d12');
pair('d03','d13');
pair('d04','d14');
pair('d099','d199');
}
var end = new Date();
var elapsed = (end - start)/1000;
console.log(elapsed);
var pcnt = parseInt(elapsed1/elapsed * 100);
var str = pcnt + '%';
console.log(str);
var msg = document.getElementById('msg');
msg.innerHTML = 'Results: ' + str;
div{border:1px solid #f00;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<div id="msg">Results: </div>
<div id="d01" class="example-1"><p>1 of two<br/>2 lines</p>  </div>
<div id="d11" class="example-1"><p>single line height of 2 lines</p>  </div>
<div id="d02" class="example-2"><p></p>single line height of 3 lines </div>
<div id="d12" class="example-2"><p></p>1 of three<br/>2<br/>3 lines  </div>
<div id="d03" class="example-3"><p></p>1 of four<br/>2<br/>3<br/>4 lines  </div>
<div id="d13" class="example-3"><p></p>single line height of 4 lines  </div>
<div id="d04" class="example-4"><p></p>single line  height of 5 lines </div>
<div id="d14" class="example-4"><p></p>1 of five<br/>2<br/>3<br/>4<br/>5 lines  </div>
<div id="d099" class="example-5"><p></p>1 of six<br/>2<br/>3<br/>4;<br/>5<br/>6 lines  </div>
<div id="d199" class="example-5"><p></p>single line height of 6 lines  </div> </div>
</div>

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Can you provide an example over the original code? this maybe work, but I don't see the relation with the original code. \$\endgroup\$ – vmariano Mar 28 '15 at 8:14
  • 1
    \$\begingroup\$ @vmariano The relation is the pairs of HTML divs. Original code is inefficient jQuery and it uses inefficient if else structures. What you are not seeing in the original code is all the additional jQuery code that has to execute. Rather than the inefficient if this.height > maxHeight, I just get the two heights, with no comparison, get the max value of the two heights, and set the height of both divs to the max value. Very simple, very efficient. And no 30 KB of jQuery. No comparisons, no if else, just very fast computational code. \$\endgroup\$ – Misunderstood Mar 28 '15 at 8:51
  • \$\begingroup\$ Can you provide a benchmark to compare the efficiency? \$\endgroup\$ – vmariano Mar 28 '15 at 12:18
  • 2
    \$\begingroup\$ @vmariano Added third snippet with benchmark code. I looped your code 100x and mine 100x and compared the times. The results are stored at the top of the snippet window. Both routines use the same HTML. Mine is about 265% to 300% faster. The results formula is (yourTime / myTime) * 100 \$\endgroup\$ – Misunderstood Mar 28 '15 at 14:14
  • \$\begingroup\$ Thankyou for taking so much time to share this! I haven't marked this as the correct answer because I was looking for a way of combining functions in jQuery, but I can see that this is a better way of doing things so will be using this approach. \$\endgroup\$ – jasonbradberry Mar 29 '15 at 12:42

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.