4

Desired Behaviour

Apply styling to every third visible child div in a container (ie 1,3,5,7 etc) using:

.card:nth-child(2n+1) {
    margin-right: 20px !important;
    background: pink; 
} 

Actual Behaviour

nth-child styling is applied to all child divs, regardless of visibility (this is known behaviour).

Scenario

A container with child divs that are filtered on input keyup.

enter image description here

After filtering, nth-child styling is applied to all child divs, not just visible divs.

enter image description here
What I've Tried

The answer linked below provided a solution to a similar question utilising detach(), but I couldn't figure out how to dynamically re-insert detached elements after each keyup filter (and wasn't sure if that was the best approach).

https://stackoverflow.com/a/32380418

jsFiddle: link

$(document).on("keyup", ".my_input", function() {
  var input_val = $(this).val();
  var input_length = input_val.length;
  // minimum 2 chars for search
  if (input_length > 2) {
    filter_cards(input_val);
  } else if (input_length <= 2) {
    $(".card").show();
    // remove matched text styling
    // see: https://stackoverflow.com/a/4232971
    $('span.matched_text').contents().unwrap();
  }
});

// filter function
function filter_cards(input_val) {

  // iterate over each card
  $(".card").each(function() {

    var match_counter = 0;

    // instance of card
    var $card = $(this);

    var text = $card.text();

    var exists_in_string =
      text.toLowerCase().indexOf(input_val.toLowerCase()) !== -1;

    if (exists_in_string === false) {
      $card.html(text);
    } else if (exists_in_string === true) {
      match_counter += 1;
      var reg = new RegExp(input_val, 'i');
      $card.html(text.replace(reg, '<span class="matched_text">$&</span>'));
    }

    if (match_counter > 0) {
      $card.show();
    } else {
      $card.hide();
    }
  });
}
* {
  box-sizing: border-box;
}

.my_input {
  width: 100%;
  font-size: 48px;
  margin-bottom: 20px;
}

.my_cards {
  display: flex;
  flex-wrap: wrap;
}

.card {
  width: calc(50% - 10px);
  font-size: 38px;
  border: 1px solid #000;
  margin-bottom: 30px;
}


/* add margin on child divs 1,3,5,7 etc */

.card:nth-child(2n+1) {
  margin-right: 20px !important;
  background: pink;
}

.matched_text {
  background: green;
  color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input class="my_input" placeholder="search for hello, min 2 chars...">

<div class="my_cards">

  <div class="card" data-initial_index="0">
    01 - apples and hello
  </div>

  <div class="card" data-initial_index="1">
    02 - oranges and hello
  </div>

  <div class="card" data-initial_index="2">
    03 - bananas
  </div>

  <div class="card" data-initial_index="3">
    04 - passionfruits and hello
  </div>

  <div class="card" data-initial_index="4">
    05 - mangos and hello
  </div>

  <div class="card" data-initial_index="5">
    06 - limes and hello
  </div>


</div>
user1063287
  • 10,265
  • 25
  • 122
  • 218
  • 2
    This detach solution seems overly complicated. I’d go with https://api.jquery.com/visible-selector/, and then add/remove a class to highlight every third item in that collection. – misorude Oct 22 '18 at 10:19
  • I added the following for loop in three places: 1) on content load 2) within the keyup handler if less than 2 characters in input (to reset the interface) and 3) at the end of the `filter_cards` function: `$(".card:visible").each(function(index) { // if odd number if ((index + 1) % 2 === 1) { $(this).addClass("odd_div_styling");} else { $(this).removeClass("odd_div_styling");} }); ` Seems to work: https://jsfiddle.net/rwone/29suzbw3/1/ Feel free to post as an answer. – user1063287 Oct 22 '18 at 11:05

1 Answers1

3

You can try a combination of visible and odd/even pseudo-selector to change your css:

$('.card:visible:even').css({ 'background': 'pink'});
$('.card:visible:odd').css({ 'background': 'white'});

$(document).on("keyup", ".my_input", function() {
  var input_val = $(this).val();
  var input_length = input_val.length;
  // minimum 2 chars for search
  if (input_length > 2) {
    filter_cards(input_val);
  } else if (input_length <= 2) {
    $(".card").show();
    // remove matched text styling
    // see: https://stackoverflow.com/a/4232971
    $('span.matched_text').contents().unwrap();
  }
  $('.card:visible:even').css({ 'background': 'pink','marginRight':'20px'});
  $('.card:visible:odd').css({ 'background': 'white','marginRight':0});
});

// filter function
function filter_cards(input_val) {

  // iterate over each card
  $(".card").each(function() {

    var match_counter = 0;

    // instance of card
    var $card = $(this);

    var text = $card.text();

    var exists_in_string =
      text.toLowerCase().indexOf(input_val.toLowerCase()) !== -1;

    if (exists_in_string === false) {
      $card.html(text);
    } else if (exists_in_string === true) {
      match_counter += 1;
      var reg = new RegExp(input_val, 'i');
      $card.html(text.replace(reg, '<span class="matched_text">$&</span>'));
    }

    if (match_counter > 0) {
      $card.show();
    } else {
      $card.hide();
    }
 
  });
}
* {
  box-sizing: border-box;
}

.my_input {
  width: 100%;
  font-size: 48px;
  margin-bottom: 20px;
}

.my_cards {
  display: flex;
  flex-wrap: wrap;
}

.card {
  width: calc(50% - 10px);
  font-size: 38px;
  border: 1px solid #000;
  margin-bottom: 30px;
  overflow:hidden;
}


/* add margin on child divs 1,3,5,7 etc */

.card:nth-child(2n+1) {
  margin-right: 20px;
  background: pink;
}

.matched_text {
  background: green;
  color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input class="my_input" placeholder="search for hello, min 2 chars...">

<div class="my_cards">

  <div class="card" data-initial_index="0">
    01 - apples and hello
  </div>

  <div class="card" data-initial_index="1">
    02 - oranges and hello
  </div>

  <div class="card" data-initial_index="2">
    03 - bananas
  </div>

  <div class="card" data-initial_index="3">
    04 - passionfruits and hello
  </div>

  <div class="card" data-initial_index="4">
    05 - mangos and hello
  </div>

  <div class="card" data-initial_index="5">
    06 - limes and hello
  </div>


</div>
madalinivascu
  • 32,064
  • 4
  • 39
  • 55
  • 1
    should be added at the end of the filter function I guess – Temani Afif Oct 22 '18 at 10:47
  • i made a jsFiddle of your answer, it seems to work - did you just make one adjustment? the `:even` and `:odd` handling at the end of the `keyup` handler? https://jsfiddle.net/rwone/g85yj2q7/2/ – user1063287 Oct 22 '18 at 11:19
  • 1
    i modified the css, removed the important from the margin and those 2 lines of code – madalinivascu Oct 22 '18 at 11:23
  • 1
    for reference, my scenario is responsive, ie if screen is less than 1200px, then only one column of cards is displayed. using the solution above, i found that if you filter, then resize window, the jquery `marginRight` styling persists (even if other responsive css is defined otherwise). so i tried catching the `$(window).resize` event and handling conditionally like this: `if ($(window).width() < 1200) {$(".card").css("marginRight","0px");} else {$('.card:visible:even').css("marginRight","20px");$('.topic_card:visible:odd').css("marginRight",0);}` it seems to work. – user1063287 Oct 22 '18 at 11:58
  • and i also added a `$(window).width()` conditional to your `:odd`/`:even` handler. – user1063287 Oct 22 '18 at 12:02