Get corner items in current CSS grid layout, expanding on hover

Tags: , , , ,



I’m using grid-template-columns: repeat(auto-fit, 250px) to keep grid items 250px wide but automatically adjust number of row-items according to screen-width. Now I want that when I hover over an item, it expands taking a bit of its sibling’s space, with its sibling getting shrunk. By console.log(), I found nextElementSibling that can be used. So I’m thinking of something like-

function expand(card){
    card.setAttribute("style","width: 300px;");
    card.nextElementSibling.setAttribute("style","width: 200px");
}
function restore(card){
    card.setAttribute("style","width: 250px;");
    card.nextElementSibling.setAttribute("style","width: 250px");
}

This seems pretty bad (I guess!) because I need to manage corner items separately and hence need to know the current number of items in a row. Also, when I hover over a card, the card does expand but because of repeat(auto-fit,250px), the card intersects its sibling because the individual cells remain fixed (250px). How can I solve the issues? Or any better suggested solutions? Here is my git repository.

Edit 1- As to clarify my issues:

  1. How to manage the corner items, i.e., for an item in the right corner I want its previous sibling shrink; not the next.
  2. The cells remain fixed, i.e., the expanding item intercepts the shrinking item’s border-

enter image description here

becomes

enter image description here

So how to deal with this behavior caused by auto-fit,250px?

Answer

I honestly tried to solve it using grid-template-columns: repeat(auto-fit, minmax(250px,auto)); and a different size of child elements, but in’s too unstable and not working that way. But I don’t think the grid is require for this task. The easiest way I see how this should be solved – using good old JavaScript (+jQuery) and flex. Although the right way will be resize all elements in a single row, not only nearest siblings of a hovered element. Try it on any screen size.

$(document).ready(function() {
  /* defines main digits */
  let boxWidth = $('#box').innerWidth();
  let itemOuterWidth = $('.item').outerWidth(true);
  let itemInnerWidth = $('.item').innerWidth();
  SetSeparators(boxWidth, itemOuterWidth);

  /* refresh main digits ater page resize */
  $(window).resize(function() {
    $('.item').css({
      "flex": ""
    });
    boxWidth = $('#box').innerWidth();
    itemOuterWidth = $('.item').outerWidth(true);
    itemInnerWidth = $('.item').innerWidth();
    SetSeparators(boxWidth, itemOuterWidth);
  });

  $('#box').on('mouseover', '.item', function(e) {
    GetElementsPosition($(this).index('.item'), $(this), boxWidth, itemOuterWidth, itemInnerWidth);
  });

  $('#box').on('mouseleave', '.item', function(e) {
    $('.item').css({
      "flex": ""
    });
  });

});

/* set separator elemet to avoid blocks to jump from row to row while resizing */
function SetSeparators(boxWidth, itemOuterWidth) {
  $('.separator').remove();
  let countRowItems = Math.floor(boxWidth / itemOuterWidth);
  $('<div class="separator"></div>').insertBefore('.item:nth-child(' + countRowItems + 'n+1)');
}

function GetElementsPosition(index, element, boxWidth, itemOuterWidth, itemInnerWidth) {

  /* calculating row items, column position and row position of a current elemet */
  let countRowItems = Math.floor(boxWidth / itemOuterWidth);
  let colPosition = index % countRowItems;
  let rowPosition = Math.floor(index / countRowItems);
  /* exmanpd size of a hovered element in pixels*/
  let expandSize = 50;

  /* counting number of items in a hovered row */
  let currentRowCounter = 0;
  $('.item').each(function(e) {
    let thisIndex = $(this).index('.item');
    let thisRowPosition = Math.floor(thisIndex / countRowItems);
    if (rowPosition == thisRowPosition) {
      currentRowCounter++;
    }
  });

  /* settting each element widht according to it's position in a list and row */
  $('.item').each(function(e) {
    $(this).css({
      "flex": "0 1 " + itemInnerWidth + "px"
    });
    let thisIndex = $(this).index('.item');
    let thisColPosition = thisIndex % countRowItems;
    let thisRowPosition = Math.floor(thisIndex / countRowItems);
    if ((rowPosition == thisRowPosition) && (colPosition == thisColPosition)) {
      $(this).css({
        "flex": "0 1 " + (itemInnerWidth + expandSize) + "px"
      });
    } else if (rowPosition == thisRowPosition) {
      $(this).css({
        "flex": "0 1 " + (itemInnerWidth - (expandSize / (currentRowCounter - 1))) + "px"
      });
    } else {
      $(this).css({
        "flex": ""
      });
    }
  });

}
* {
  box-sizing: border-box;
}

html {
  height: 100%;
}

body {
  min-height: 100%;
  margin: 0;
  padding: 0;
}

#box {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: flex-start;
}

.item {
  background: gray;
  flex: 0 1 250px;
  height: 50px;
  transition: all .5s ease;
  margin: 0 15px 15px 15px;
}

.separator {
  flex: 0 1 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='box'>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
  <div class='item'></div>
</div>


Source: stackoverflow