2
\$\begingroup\$

I am using the Bootstrap class .clearfix after every second(or third) <div> to keep each row starting at the same height on all screen sizes even if two divs on same row have different height.

I use a hidden <div> at the top as template for information I will get from an ajax call. I have simulated this event in the demo with a button to click and content "insert N" for simplicity. The counter is only there for the demo. In this snippet I also added <br /> to some news to simulate difference in height.

When I insert a <div> at the top with jquery, I need the .clearfix classes to move accordingly to keep the current layout, and add a .clearfix if there are too many <div> after the last .clearfix.

The layouts are 1 field on top row, and 2(or 3) fields on all other rows on screen 992px and up, if smaller screen just 1 field per row. (Just to be clear, when I talk about "row", I do not mean the bootstrap class .row)

I have managed to get this working with jquery .clone() / .prepend() / .each() / .prev() / .next() / .insertAfter() / .nextUntil() / .nextAll(), but I cant shake the feeling that it is extremely long-winded and hard for next generation coders to maintain. Can anyone offer a cleaner method?

// simulate new content from ajax call
$('#clickit').on('click', function(){
  // demo counter
  var counter = (parseInt($('#output').text())+1);
  $('#output').text(counter); // view

  // clone template before filling up with info
  var template = $('.modalInputHolder_679');
  var templateClone = template.clone();
  
  // remove template-class from old template
  // insert new content in new div(old template) and new template
  template.removeClass('modalInputHolder_679').text('insert  '+counter);
  templateClone.text('hidden');
  $(".row").prepend(templateClone);
  
  // move clearfix divs up one place
  var replaceClass;
  var clearfix = $(".row .clearfix");
  clearfix.each(function(index){
    var that = $(this);
    var prev = that.prev();
    if(index==0) { // if first .clearfix
    	replaceClass = that.next().attr('class'); // get new class for old template
    	prev.attr('class',replaceClass);
    }
    prev.insertAfter(that); // move up
  });
  
  // accomodate for both 1-2 layout and 1-3 layout(wide first, rest split among 2/3 cols)
  var clearfixLength = clearfix.length;
  if(clearfixLength>1){ // if more than two .clearfix
    // find number of divs between clearfixes
    var type = clearfix.eq(0).nextUntil('.clearfix').length;
    var last = clearfix.eq((clearfixLength-1)); // get last .clearfix
    // get number of divs after last
    var trailingFields = last.nextAll();
    var trailingLength = trailingFields.length;
    if(trailingLength>type){ // if difference is more than filled up (2 or 3)
      var clearfixClone = clearfix.eq((type-1)).clone(); // create clone of clearfix
      trailingFields.eq((type-1)).after(clearfixClone); // input new clearfix
    }    
  }
});
.fullWidthColumn { background-color: #FF0000!important; }
.row>.col-xs-12:nth-of-type(odd) { background-color: #00FF00; }
.row>.col-xs-12:nth-of-type(even) { background-color: #0000FF; }
.modalInputHolder_679 { display:none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<button class="btn btn-default" id="clickit">Insert news</button> <span> Counter: </span><span id="output">0</span><br />
<div class="container">
  <div class="row">
    <div class="col-xs-12 fullWidthColumn modalInputHolder_679">hidden</div>
    <div class="col-xs-12 fullWidthColumn">first</div>
    <div class="clearfix"></div>
    <div class="col-xs-12 col-md-6">second<br />...</div>
    <div class="col-xs-12 col-md-6">third</div>
    <div class="clearfix"></div>
    <div class="col-xs-12 col-md-6">fourth</div>
    <div class="col-xs-12 col-md-6">fifth<br />...</div>
    <div class="clearfix"></div>
    <div class="col-xs-12 col-md-6">sixth<br />...</div>
    <div class="col-xs-12 col-md-6">seventh</div>
  </div>
</div>

Here's jsfiddles of both 2 col layout (code/view) and 3 col layout (code/view), they use the same javascript and css, but slightly different html.

\$\endgroup\$
1
\$\begingroup\$

Define counter

I'd define a counter like this:

var counter = 0; //I'd define it in upper scope 
$('#clickit').on('click', function () {
    counter++;
    $('#output').text(counter);

Define template:

You defined a template in html, and access and clone it like this:

var template = $('.modalInputHolder_679');
var templateClone = template.clone();

template.removeClass('modalInputHolder_679').text('insert  '+counter);
templateClone.text('hidden');
$(".row").prepend(templateClone);

It's verbose. I'd define it like this:

var $row = $('<div class="col-xs-12 fullWidthColumn"></div>')
    .text('insert   ' + counter);
$(".row").prepend($row);

Since the template is short, you can just define it in js code. If the real template is long, you can define it at somewhere else to avoid handling modalInputHolder_679 things (use the class name on its wrapper).

Type?

var type = clearfix.eq(0).nextUntil('.clearfix').length;

Maybe colCnt is better. And the .eq(0) can be replaced with .first(). Similarly, .eq((clearfixLength - 1)); can be replaced with .last().

So this section can be change to:

var last = clearfix.last(); // get last .clearfix
var colCnt = last.prevUntil('.clearfix').length;

Better access

Since you do insertAfter on every clearfix, why not just insert them again?

var counter = 0; //I'd define it in upper scope 
var clearfixTemplate = $('<div class="clearfix"></div>');
var rowTemplate = $('<div class="col-xs-12 fullWidthColumn"></div>');
var rowWrapper = $(".row");
$('#clickit').on('click', function () {
    counter++;
    $('#output').text(counter);

    var clearfix = rowWrapper.find(">.clearfix");
    var sections = rowWrapper.find(">.col-xs-12"); //you may need a class name to indicate the `information` element

    sections.eq(0).attr("class", sections.eq(1).attr("class"));

    if (clearfix.length < 2)
        return;

    var last = clearfix.last();
    var colCnt = last.prevUntil('.clearfix').length;

    sections.each(function (index) {
        if (index % colCnt === colCnt - 1) {   
            var clear = clearfix[Math.floor(index / colCnt)]; //reuse them
            if (!clear) { //if not enough
                clear = clearfixTemplate.clone();
            }
            $(this).after(clear);
        }
    });

    var row = rowTemplate.clone()
        .text('insert   ' + counter);
    rowWrapper.prepend(row);

});
.fullWidthColumn {
  background-color: #FF0000!important;
}

.row>.col-xs-12:nth-of-type(odd) {
  background-color: #00FF00;
}

.row>.col-xs-12:nth-of-type(even) {
  background-color: #0000FF;
}

.modalInputHolder_679 {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<button class="btn btn-default" id="clickit">Insert news</button> <span> Counter: </span><span id="output">0</span><br />
<div class="container">
  <div class="row">
    <div class="col-xs-12 fullWidthColumn">first</div>    
    <div class="col-xs-12 col-md-6">second<br />...</div>
    <div class="col-xs-12 col-md-6">third</div>   
    <div class="clearfix"></div>
    <div class="col-xs-12 col-md-6">fourth</div>
    <div class="col-xs-12 col-md-6">fifth<br />...</div>   
    <div class="clearfix"></div>
    <div class="col-xs-12 col-md-6">sixth<br />...</div>
    <div class="col-xs-12 col-md-6">seventh</div>
  </div>
</div>

\$\endgroup\$
  • \$\begingroup\$ Thanks for your input and for taking the time to look at it! :) However I can't accept it as the correct answer(yet?) because it does not work as intended anymore. I will make use of some of it, but your full code does not work on large screens. Error1; .col-md-6 / col-md-4 are ignored in your solution. Error2; .clearfix are added but not moved up. Error3; .clearfix are added at the bottom one step too soon. Error4; template is no longer hidden. I have been trying to debug it, but as it created more bugs than solutions, I cant accept this answer. Sorry, and thanks for good ideas \$\endgroup\$ – turbopipp Apr 2 '17 at 13:15
  • 1
    \$\begingroup\$ @turbopipp Well.. 1: I can't find col-md-4 in your code. 2: It does, please inspect the result, don't just read the code. 4: the template is not in the document at all. Don't just copy my code to your project, I also changed your html. \$\endgroup\$ – blackmiaool Apr 2 '17 at 13:34
  • \$\begingroup\$ @turbopipp You need rel="stylesheet" in your html snippet. \$\endgroup\$ – blackmiaool Apr 2 '17 at 13:36
  • 1
    \$\begingroup\$ @turbopipp It seems that I focused on your stackoverflow snippet and ingored your jsfiddle. I'm sorry for this. \$\endgroup\$ – blackmiaool Apr 2 '17 at 13:37
  • \$\begingroup\$ You are right when it comes to 2 and 4, and 3 doesn't really matter. :) The error was me trying to implement templateID again because the ajax calls could return information to multiple newsfields with different layout, and I had to identify the correct template inside the scope. But you had no way of knowing that was a requirement, so that's on me. :) Thanks for your help, ofc I will accept your answer now. :) \$\endgroup\$ – turbopipp Apr 2 '17 at 13:55

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.