Search code examples
javascriptcsscss-gridgrid-layout

A way to count columns in a responsive grid


Although I have not yet been able to find an answer, the question is simple: Is there a way, other than brute force, to count the number of columns in a responsive grid?

#grid-container {
  width: 100%;
  height: 85%;
  position: relative;
  padding: var(--gap); /* adjusted with JS to make var(--gap) responsive */
  display: grid;
  grid-gap: var(--gap); /* adjusted with JS to make var(--gap) responsive */
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  box-sizing: border-box;
  background: skyblue;
}

.grid-item {
  width: 100%;
  min-width: 120px;
  max-width: 450px;
  height: ; /* adjusted with JS on resize events to roughly maintain proportions */
  min-height: 192px;
  border-radius: 10px;
  background: #333;
}

The reason I ask is because I have given the grid items a max-width that causes a massive gap at the very last breakpoint, which I would like to be able to detect instead of setting an explicit media query.

enter image description here


Solution

  • I think the easiest way without brute force is to consider a simple division. You can easily find the width of the container and each column is defined by minmax(300px,1fr) and we know the gap. Using all these information the calculation should be done like below:

    If we will have N columns then we will have N-1 gaps. We also know that W should at least be 300px and cannot be more than a value (we will call Wmax).

    Let's suppose the gap is equal to 10px.

    If N=2 and each column is equal to 300px we will have the container width equal to 300px*2 + 10px*1 = 610px.

    If N=3 and each column is equal to 300px we will have 300px*3 + 10px*2=920px.

    Now it's clear that if the container width is between 610px and 920px we will have 2 columns because there is no space to hold 3 columns but enough space to hold 2 columns that we expand to fill the remaining space (using 1fr) so Wmax in this case is (920px - 10px)/2 = 455px. In other words, the width will vary from 300px to 455px when having 2 columns.

    So if we take the formula 300px*N + 10px*(N-1) = Wc with Wc our container width you will see that N is equal to 2 when Wc=610px and 3 when Wc=920px and between we will have a result in [2,3] so we simply round the value to the smallest one (2 in this case) and we will have our column number.

    Here is a basic example:

    var gap = 10;
    var minW = 200;
    
    var Wc = document.querySelector('.grid').offsetWidth;
    var N = Math.floor((Wc+gap)/(minW+gap));
    console.log(N);
    
    
    window.addEventListener('resize', function(event){
       Wc = document.querySelector('.grid').offsetWidth;
       N = Math.floor((Wc+gap)/(minW+gap));
       console.log(N);
    });
    .grid {
      display:grid;
      grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
      grid-gap:10px;
    }
    span {
      min-height:50px;
      background:red;
    }
    <div class="grid">
      <span></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
    </div>

    In case you don't know the value of gap and min-width you can consider getComputedStyle() to get the different values. In this case, the N should already be an Integer because grid-template-columns will give us the computed width of each column (in practice, we will still have some fraction due to rounding ).

    var grid = document.querySelector('.grid');
    
    var gap = parseFloat(window.getComputedStyle(grid,null).getPropertyValue("column-gap"));
    var minW = parseFloat(window.getComputedStyle(grid,null).getPropertyValue("grid-template-columns"));
    
    var Wc = document.querySelector('.grid').offsetWidth;
    var N = (Wc+gap)/(minW+gap);
    console.log(N);
    
    
    window.addEventListener('resize', function(event){
       Wc = document.querySelector('.grid').offsetWidth;minW = parseFloat(window.getComputedStyle(grid,null).getPropertyValue("grid-template-columns"))
       N = (Wc+gap)/(minW+gap);
       console.log(N);
    });
    .grid {
      display:grid;
      grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
      grid-gap:10px;
    }
    span {
      min-height:50px;
      background:red;
    }
    <div class="grid">
      <span></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
      <span></span>
    </div>