Cards grid with more container appearance

In this example below you will see how to do a Cards grid with more container appearance with some HTML / CSS and Javascript

Thumbnail
This awesome code was written by tibomahe, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright tibomahe ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Cards grid with more container appearance</title>
  
  
  <link rel='stylesheet prefetch' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'>

      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <div class="container">
  
  <div class="row card-grid js-card-grid">
   
  <!-- CARD -->
  <div data-id="1" id="card1" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 1</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 1</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="2" id="card2" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 2</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 2</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="3" id="card3" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 3</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 3</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  <!-- CARD -->
  <div data-id="4" id="card4" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 4</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 4</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="5" id="card5" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 5</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 5</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="6" id="card6" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 6</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 6</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="7" id="card7" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 7</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 7</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="8" id="card8" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 8</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 8</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
      <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
  
  
  <!-- CARD -->
  <div data-id="9" id="card9" class="card js-card">
    <div class="card__illustration"></div>
    <div class="h2 h2 card__title">Partner 9</div>
    <div class="row more-container js-more-container">
      <div class="more-container__col-left">
        <div class="more-container__illustration"></div>
        <button class="btn btn-primary more-container__cta">En savoir plus</button>
      </div>
      <div class="more-container__col-right">
        <h2 class="h2 more-container__title">Partner 9</h2>
        <div class="more-container__description">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laborum nam beatae voluptas illo impedit, ratione eveniet facere molestias. Tenetur nisi delectus ab esse dolore consectetur officia ea eaque cumque asperiores. Lorem ipsum dolor sit amet,
          consectetur adipisicing elit. Voluptatem nisi, doloremque autem obcaecati minus voluptatum. Quaerat eaque totam quo vitae iure, ea perferendis, commodi ipsum vero et cumque possimus! Officiis.</div>
      </div>
     <div class="more-container__control-btn">
        <button type="button" class="btn btn-default js-previous-content">&lt;</button>
        <button type="button" class="btn btn-default js-next-content">&gt;</button>
        <button type="button" class="btn btn-default js-close-content">X</button>
      </div>
    </div>
  </div>
   
  </div>
  
</div>
  <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js'></script>

  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/tibomahe/cards-grid-with-more-container-appearance-QNVOPb */
.container {
  margin-bottom: 30px;
}

.card {
  padding: 30px;
  width: 100%;
  max-width: calc(100% - 30px);
  margin: 15px;
  float: left;
  border: 1px solid grey;
  border-radius: 5px;
  position: relative;
}
@media (min-width: 800px) {
  .card {
    max-width: calc(33% - 30px);
  }
}
.card .card__illustration {
  width: 150px;
  height: 150px;
  background: grey;
  border-radius: 50%;
  margin: auto;
}
.card .card__title {
  text-align: center;
}
.card .more-container {
  display: none;
}
.card.active:after {
  content: '';
  display: block;
  background: #eaeaea;
  position: absolute;
  bottom: -30px;
  left: 50%;
  transform: translateX(-50%);
  width: 30px;
  height: 30px;
}

.more-container {
  border-radius: 5px;
  background: #eaeaea;
  padding: 30px;
  float: left;
  width: 100%;
  position: relative;
}
.more-container .more-container__col-left {
  width: 33%;
  float: left;
  text-align: center;
}
.more-container .more-container__col-right {
  width: 66%;
  float: right;
}
.more-container .more-container__control-btn {
  position: absolute;
  top: 30px;
  right: 30px;
}
.more-container .more-container__control-btn .btn {
  background: transparent;
  border: none;
}
.more-container .more-container__illustration {
  width: 150px;
  height: 150px;
  background: grey;
  border-radius: 50%;
  margin: auto;
}
.more-container .more-container__cta {
  margin-top: 30px;
  width: 150px;
}
.more-container .more-container__description {
  max-width: 600px;
}


/*Downloaded from https://www.codeseek.co/tibomahe/cards-grid-with-more-container-appearance-QNVOPb */
//Cache
var $card = $('.js-card'),
    $cardGrid = $('.js-card-grid');

//Generate the content
function generateContent(targetId) {
  var $target = $('#card' + targetId),
      targetContentClone = $target.find('.js-more-container').clone().html();
  return '<div id="content' + targetId + '" data-id="' + targetId + '" class="more-container js-generated-content">' + targetContentClone + '</div>';  
}

//Determine the last element of the current row to append the generated content
function determineRow(targetPosition) {
  //no need to determine all the row, we can stop at the first row found
  var rowIsFound = false;
  
  $card.each(function() {
    var currentCardPosition = $(this).position().top;
    if (currentCardPosition === targetPosition && rowIsFound === false) {
      //If the element is at the same top position than our target element, then it belongs to the same row
      //Before adding the specific class, we can remove it on all element since we only want to determine the last element of the row
      $card.removeClass('currentRow');
      $(this).addClass('currentRow');
    }
    else if (currentCardPosition > targetPosition && rowIsFound === false) {
      //if the element is below our target element, then it belongs to the next row
      //Then we can change the counter since we don't have to calculate other element
      rowIsFound = true;
    }
    else if (rowIsFound === true) {
      //break the loop
      return false;
    }
  });
}

//Manage the closing of the generated content
function closeGeneratedContent() {
  $(".js-generated-content").remove();
  $card.removeClass('active');
  $card.removeClass('currentRow');
}

//Manage click
$cardGrid.on('click', '.js-card', function(event) {
  //Get former position
  var $previousCard = $('.card.active'),
      previousPosition = 0,
      scrollPosition = $('body').scrollTop();
  if ($previousCard.length) {
    previousPosition = $previousCard.position().top;
  }
  //close current content
  closeGeneratedContent();
  //set the informations of cliked card
  var $target = $(event.currentTarget),
      targetId = $target.attr('data-id'),
      targetPosition = $target.position().top;
  //set current active class
  $target.addClass('active');
  //determine new position for generated content
  determineRow(targetPosition);
  //insert generated content
  $('.currentRow').after(generateContent(targetId));
    
  //scroll to generated content if not in the same place than before
  if (previousPosition != targetPosition) {
    //console.log('scroll !');
   $('html, body').animate({
      scrollTop : $('#content' + targetId).offset().top - 50
    }, 500); 
 }
  else {
    $('body').scrollTop(scrollPosition);
  }
});

//Add event listener to the newly created component
 $cardGrid.on('click', '.js-close-content', function(event) {   
   closeGeneratedContent();
   var targetId = $(event.currentTarget).closest('.js-generated-content').attr('data-id');
   $('#card' + targetId).removeClass('lastClick');
    //scroll back to the closed card
    $('html, body').animate({
      scrollTop : $('#card' + targetId).offset().top - 50
    }, 500);
 });
  
  //Next arrow
  $cardGrid.on('click', '.js-next-content', function(event) {
    var $currentContent = $(event.currentTarget).closest('.js-generated-content'),
        currentCardId = parseInt($currentContent.attr('data-id')),
        nextCardId = currentCardId + 1,
        nextCardPosition;
        
    var $nextCardTarget = $('#card' + nextCardId);
    
    if ($nextCardTarget.length) {
      //Get former position
      var $previousCard = $('.card.active'),
          previousPosition = 0,
          scrollPosition = $('body').scrollTop();
      if ($previousCard.length) {
          previousPosition = $previousCard.position().top;
      }
      
      closeGeneratedContent();
      nextCardPosition = $('#card' + nextCardId).position().top;
      $nextCardTarget.addClass('active');
      determineRow(nextCardPosition);
      $('.currentRow').after(generateContent(nextCardId));
      //scroll to generated content if not in the same place than before
      if (previousPosition != nextCardPosition) {
        //console.log('scroll !');
       $('html, body').animate({
          scrollTop : $('#content' + nextCardId).offset().top - 50
        }, 500); 
     }
      else {
        $('body').scrollTop(scrollPosition);
      }
    }
        
 });

//Previous arrow
  $cardGrid.on('click', '.js-previous-content', function(event) {
    var $currentContent = $(event.currentTarget).closest('.js-generated-content'),
        currentCardId = parseInt($currentContent.attr('data-id')),
        previousCardId = currentCardId - 1,
        previousCardPosition;
        
    var $previousCardTarget = $('#card' + previousCardId);
    
    if ($previousCardTarget.length) {
      //Get former position
      var $previousCard = $('.card.active'),
          previousPosition = 0,
          scrollPosition = $('body').scrollTop();
      if ($previousCard.length) {
          previousPosition = $previousCard.position().top;
      }
      
      closeGeneratedContent();
      previousCardPosition = $('#card' + previousCardId).position().top;
      $previousCardTarget.addClass('active');
      determineRow(previousCardPosition);
      $('.currentRow').after(generateContent(previousCardId));
      //scroll to generated content if not in the same place than before
      if (previousPosition != previousCardPosition) {
        //console.log('scroll !');
       $('html, body').animate({
          scrollTop : $('#content' + previousCardId).offset().top - 50
        }, 500); 
     }
      else {
        $('body').scrollTop(scrollPosition);
      }
    }
        
 });

Comments