Home > Software engineering >  Generate cells for CSS grid via JavaScript
Generate cells for CSS grid via JavaScript

Time:09-10

I'm trying to generate grid cells dynamically, but in a specific pattern. I copied a lot of <div>s, made the necessary adjustments by hand, and got the results I needed, but I'm wondering if someone can help me in developing an algorithm for producing it automatically. If you look at the example below, the left box is what I want to dynamically calculate.

function calcCells(){
let cells = '';
  for(let a = 1; a <= 10; a  ){
    for(let b = 1; b <= a; b  ){
        cells  = `<div style="background:#fd8362; grid-area: ${a % b} / ${a % b} / ${a % b} / ${a % b}">
        ${b}
      </div>`
    }
  }
  return cells;
}

document.getElementById("grid-body-algorithm").innerHTML = calcCells();
#grid-body,
#grid-body-algorithm {
    float:left;
    margin-right:30px;
    box-shadow: 0 0 10px rgba(0, 0, 0, .2);
    text-align: center;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-template-rows: repeat(5, 1fr);
    justify-content: center;
    align-content: end;
    border-radius: 5px;
    width:150px;
    height:150px;
    color:#444444;
    padding:1px;
}

#grid-body > *,
#grid-body-algorithm > *{
  border:1px solid white;
}
<div id="grid-body">
  <div style="background:#fd8362; grid-area: 1 / 1 / 1 / 1">1</div>

  <div style="background:#ffad97; grid-area: 2 / 1 / 2 / 1">1</div>
  <div style="background:#ffad97; grid-area: 1 / 2 / 1 / 2">2</div>

  <div style="background:#ffded6; grid-area: 3 / 1 / 3 / 1">1</div>
  <div style="background:#ffded6; grid-area: 2 / 2 / 2 / 2">2</div>
  <div style="background:#ffded6; grid-area: 1 / 3 / 1 / 3">3</div>

  <div style="background:#fff0ec; grid-area: 4 / 1 / 4 / 1">1</div>
  <div style="background:#fff0ec; grid-area: 3 / 2 / 3 / 2">2</div>
  <div style="background:#fff0ec; grid-area: 2 / 3 / 2 / 3">3</div>
  <div style="background:#fff0ec; grid-area: 1 / 4 / 2 / 4">4</div>
</div>

<div id="grid-body-algorithm">
  
</div>

CodePudding user response:

From the desired output, we can see that the taxicab distance from the top-left corner determines the desired color, and the column number determines the cell text.

The inner loop should be constrained to stay within a maximum taxicab distance. This maximum could be a parameter to your function. In your example that maximum is 3 (as the furthest cells that are concerned are 3 taxicab steps away from the top-left cell).

Furthermore, the desired color seems to get lighter the greater that taxicab distance is. For that we could vary the alpha part of the same, fixed background color. In RGBA notation, this alpha component which can have a value between 0 and 1.

I opted for creating a document fragment instead of HTML. That way the setting of cell attributes is more JS-syntax controlled and more object oriented.

I didn't touch your CSS.

In this implementation I added an input box with which you can control the maximum taxicab distance that will be used:

function calcCells(maxDistance) {
  const fragment = document.createDocumentFragment();
  for (let row = 1; row <= 5; row  ) {
    // row   col - 2 is taxicab distance from top-left corner
    for (let col = 1, distance = row - 1; distance <= maxDistance && col <= 5; col  , distance  ) { 
        const cell = document.createElement("div");
        // Set background transparency with the alpha part of the background color
        cell.style.background = `rgba(240,120,80,${1 - (distance   0.5) / (maxDistance   1)})`;
        cell.style.gridArea = [row, col, row, col].join(" / ");
        cell.textContent = col;
        fragment.appendChild(cell);
    }
  }
  return fragment;
}

const inputDistance = document.querySelector("input");
const outputGrid = document.getElementById("grid-body-algorithm");

function refresh() {
    const maxDistance =  inputDistance.value;
    outputGrid.innerHTML = ""; // clear previous content
    outputGrid.appendChild(calcCells(maxDistance));
}

inputDistance.addEventListener("click", refresh);
refresh();
#grid-body,
#grid-body-algorithm {
    float:left;
    margin-right:30px;
    box-shadow: 0 0 10px rgba(0, 0, 0, .2);
    text-align: center;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-template-rows: repeat(5, 1fr);
    justify-content: center;
    align-content: end;
    border-radius: 5px;
    width:150px;
    height:150px;
    color:#444444;
    padding:1px;
}

#grid-body > *,
#grid-body-algorithm > *{
  border:1px solid white;
}
<div id="grid-body-algorithm"></div>
distance: <input type="number" value="3" min="0" max="8">

CodePudding user response:

First of all, you should use an eventListener or the defer-attribute to wait until the DOM structure is loaded and the element exists.

Then you should not use innerHTML to create elements. It is slow as the DOM needs to be reparsed and is an XSS-Injection vulnerability.

To create Elements use either the appendChild or the insertAdjacentHTML method.

Instead of adding everything as inline-style I would work with CSS which is cleaner and with the use of SASS even shorter.

As you already figured out, you need 2 variables. One for the row and one for the column. The first for-loop needs the one for the rows and the 2nd for the columns:

window.addEventListener('DOMContentLoaded', function() {
  for (let i = 1, n = 5; i < 5; i  ) {
    for (let j = 1; j < n; j  ) {
      document.querySelector('#grid-body-algorithm').insertAdjacentHTML('beforeend', `
        <div data-row="${i}" data-col="${j}">${j}</div>`
      );
    }
    n--;
  }
})
#grid-body-algorithm {
  width: 150px;
  height: 150px;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-template-rows: repeat(5, 1fr);
  padding: 2px;
  gap: 2px;
  border: 1px solid black;
}

[data-row="1"] { grid-row: 1 / 2; }
[data-row="2"] { grid-row: 2 / 3; }
[data-row="3"] { grid-row: 3 / 4; }
[data-row="4"] { grid-row: 4 / 5; }
[data-row="5"] { grid-row: 5 / 6; }

[data-row="1"][data-col="1"] { background: #fd8362; }
[data-row="1"][data-col="2"],
[data-row="2"][data-col="1"] { background: #ffad97; }
[data-row="1"][data-col="3"],
[data-row="2"][data-col="2"],
[data-row="3"][data-col="1"] { background: #ffded6; }
[data-row="1"][data-col="4"],
[data-row="2"][data-col="3"],
[data-row="3"][data-col="2"],
[data-row="4"][data-col="1"] { background: #fff0ec; }
<div id="grid-body-algorithm"></div>

  • Related