Search code examples
javascriptgrid

how to prevent grid from changing size


I made grid 16x16 with borders for each cell, then i added a button to toggle on/off borders of those cells, but it also changes overall size of my grid. how do i prevent this?

in the future i want to implement "change size" button that will increase numbers of cells but not change grid size. I'm sure i need to define grid size somehow but i don know how. Whatever i try either messes up grid size or cell size or both

here is my code

const grid = document.getElementById('grid');
const size = document.getElementById('size');
const eraser = document.getElementById('eraser');
const color = document.getElementById('color');
const gridBorder = document.getElementById('grid-borders');

// grid
function makeGrid(number) {
  grid.style.gridTemplateColumns = `repeat(${number}, 1fr)`;
  grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
  for (let i = 0; i < number * number; i++) {
    let cell = document.createElement('div');
    grid.appendChild(cell).setAttribute('id', 'box');
  }
}
makeGrid(16);

// drawing on hover
color.addEventListener('click', function () {
  grid.addEventListener('mouseover', function (e) {
    e.target.style.backgroundColor = 'black';
  });
});

// erase functionality
eraser.addEventListener('click', function () {
  grid.addEventListener('mouseover', function (e) {
    e.target.style.backgroundColor = 'white';
  });
});

// gird borders
const allBoxes = document.querySelectorAll('#box');
gridBorder.addEventListener('click', function () {
  for (let i = 0; i < allBoxes.length; i++) {
    if (allBoxes[i].style.border === '1px solid black') {
      allBoxes[i].style.border = 'none';
    } else {
      allBoxes[i].style.border = '1px solid black';
    }
  }
});
body {
  height: 100vh;
}

#grid {
  display: grid;
  justify-content: center;
  border: 1px solid #ccc;
}

#box {
  padding: 1em;
  border: 1px solid black;
}

#title {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: 230px;
}

#container {
  display: flex;
  height: 60%;
  width: 1204px;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 20px;
}

#menu {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Etch-a-Sketch</title>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js" defer></script>
  </head>
  <body>
    <div id="title">
      <h1>Etch-a-Sketch</h1>
    </div>
    <main id="container">
      <div id="menu">
        <button id="size">Canvas Size</button>
        <button id="color">Color</button>
        <button id="eraser">Eraser</button>
        <button id="grid-borders">Grid Borders</button>
      </div>
      <div id="grid"></div>
    </main>
  </body>
</html>


Solution

  • You can use outline instead of border. Change your CSS box definition and remove the border there as it will be used to query your box class.

    NOTE: I added a box and border class initially when creating your boxes as querying multiple elements should be targeted using a class and not a unique ID.

    Now that you have the class targeted, you can simple toggle classes with the click event and use css to add/remove -> toggle the outlines state using its corresponding toggled class style.

    I also added a conditional to check which event is being fired in your hover state listener, this will prevent the grid from being toggled so only its children, then boxes are toggled.

    Let me know if you have any issues with the code or if this isn't working for your needs and I can either remove this answer or edit to tailor any other issues you may be having.

    const grid = document.getElementById('grid');
    const size = document.getElementById('size');
    const eraser = document.getElementById('eraser');
    const color = document.getElementById('color');
    const gridBorder = document.getElementById('grid-borders');
    
    // grid
    function makeGrid(number) {
      grid.style.gridTemplateColumns = `repeat(${number}, 1fr)`;
      grid.style.gridTemplateRows = `repeat(${number}, 1fr)`;
      for (let i = 0; i < number * number; i++) {
        let cell = document.createElement('div');
        grid.appendChild(cell).id = 'box';
        // added class border and box
        cell.classList.add('border'); //--> border will be used to toggle outline in css
        cell.classList.add('box') //--> box used to query all the dynamically created box elements
      }
    }
    
    makeGrid(16);
    
    // drawing on hover
    color.addEventListener('click', function() {
      grid.addEventListener('mouseover', function(e) {
        // make sure event.target is not the grid itself
        e.target !== grid ? e.target.style.backgroundColor = 'black' : null;
      });
    });
    
    // erase functionality
    eraser.addEventListener('click', function() {
      grid.addEventListener('mouseover', function(e) {
        // make sure event.target is not the grid itself
        e.target !== grid ? e.target.style.backgroundColor = 'white' : null;
      });
    });
    
    // grid borders
    const allBoxes = document.querySelectorAll('.box');
    gridBorder.addEventListener('click', function() {
      // added a forEach method to toggle classes in order to track click state and style using css styling
      allBoxes.forEach(box => {
        box.classList.toggle('no-border');
        box.classList.toggle('border');
      })
    
    });
    body {
      height: 100vh;
    }
    
    #grid {
      display: grid;
      justify-content: center;
      border: 1px solid #ccc;
    }
    
    .box {
      /* removed the initial outline &/or border property here so it can be added and removed (toggled) using JS el.classList.toggle */
      padding: 1em;
    }
    
    #title {
      display: flex;
      align-items: flex-end;
      justify-content: center;
      height: 230px;
    }
    
    #container {
      display: flex;
      height: 60%;
      width: 1204px;
      align-items: flex-start;
      justify-content: flex-end;
      gap: 20px;
    }
    
    #menu {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    
    
    /* added the following classes to be toggled using JS depending on state of gridBorders button */
    
    .border {
      outline: 1px solid black;
    }
    
    .no-border {
      outline: none;
    }
    
    .black-bg {
      background: black;
    }
    
    .white-bg {
      background: white;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Etch-a-Sketch</title>
      <link rel="stylesheet" href="style.css" />
      <script src="script.js" defer></script>
    </head>
    
    <body>
      <div id="title">
        <h1>Etch-a-Sketch</h1>
      </div>
      <main id="container">
        <div id="menu">
          <button id="size">Canvas Size</button>
          <button id="color">Color</button>
          <button id="eraser">Eraser</button>
          <button id="grid-borders">Grid Borders</button>
        </div>
        <div id="grid"></div>
      </main>
    </body>
    
    </html>