html slider controlling grid size seems to be freezing whole site

Viewed 19

Im making an etch sketch project using mainly vanilla js. I've just changed the event handling the coloring of cells from just mouseover to only coloring cells when the user is holding down the mouse, but doing so seems to have affected the slider that determines the grid size for some reason.

If you move the slider around quick enough it struggles to keep up with the changing grid size slowing down the whole site preventing you from being able to draw and sometimes even freezing it.

Coloring cells with just mouse over:

//query selector for the grid container (the grids parent container)
const gridContainer = document.querySelector(".grid-container");

let cellColor = "black";

//an object to store hex colors and their names
const colors = {
  White: "#FFFFFF",
  Black: "#000000",
  Red: "#FF2D00",
  Blue: "#002BFF",
  LightSalmon: "#FFA07A",
  IndianRed: "#CD5C5C",
  DarkRed: "#8B0000",
  LightGreen: "#90EE90  ",
  MediumSpringGreen: "#00FA9A   ",
  MediumSeaGreen: "#3CB371",
  paleTurquoise: "#40E0D0",
  aqua: "#00FFFF",
  SteelBlue: "#4682B4",
};

const gridWidth = gridContainer.offsetWidth;

//function to return a nodelist of all of the nodes that have an id of cell
const selectCell = () => {
  return (cellQuerySelectAll = document.querySelectorAll("#cells"));
};

//function to wipe grid
const wipeGrid = () => {
  gridContainer.innerHTML = "";
};

const resetGridColor = () => {
  const gridCells = selectCell();
  gridCells.forEach((cell) => {
    cell.style.backgroundColor = "";
  });
};

const colorCell = (cell, color) => {
  return (cell.style.backgroundColor = `${color}`);
};

//function used to create grid with the parameter being passed in being how many cells the user wants
const createGrid = (numberOfCells) => {
  //create a div that is a colum which will each hold a row of cells.
  const col = document.createElement("div");
  //setting the class for col so that it becomes a flex col (check css => col)
  col.className = "col";

  //for loop used to append rows of cells to col div
  for (let y = 0; y < numberOfCells; y++) {
    //create a div that will be a row of cells
    const row = document.createElement("div");
    //setting the class for row so that it becomes a flex row (check css => row)
    row.className = "row";
    //for loop used to append cells to row div
    for (let x = 0; x < numberOfCells; x++) {
      //create a div that is the cell for the grid
      const cell = document.createElement("div");
      //setting the styling for the cells
      //width and height are set dynamicallt based on the grid containers width and how many cells the user wants
      cell.style.cssText = `width:${gridWidth / numberOfCells}px; height:${
        gridWidth / numberOfCells
      }px; `;
      cell.id = "cells";

      cell.addEventListener("mousemove",() => {
        colorCell(cell, cellColor);
      });

      

      row.appendChild(cell);
    }
    col.appendChild(row);
  }
  gridContainer.appendChild(col);
};

const createColors = () => {
  //for loop to get key in colors object
  for (const colorName in colors) {
    //create div for each color
    const color = document.createElement("div");
    color.id = `${colorName}`;
    color.classList.add("color");
    //styling for each color
    color.style.cssText = `width:30px; height:30px; border-radius:50%; background-color:${colors[colorName]}; display:flex; justify-content:center;`;
    //when user clicks on color changes global color var to hex color selected
    color.addEventListener("click", () => {
      cellColor = `${colors[colorName]}`;
    });
    const colorContainer = document.querySelector(".colors-container");
    //apend colors to the color container
    colorContainer.appendChild(color);
  }
};

function ready(callback) {
  // in case the document is already rendered
  if (document.readyState != "loading") callback();
  else if (document.addEventListener)
    document.addEventListener("DOMContentLoaded", callback);
  else
    document.attachEvent("onreadystatechange", function () {
      if (document.readyState == "complete") callback();
    });
}

//function only runs once
ready(function () {
  //query selector for slider used to control grid size
  const slider = document.getElementById("slider");

  //need to orginally make the grid once by default as the code below only creates a grid once there is input on the slider
  createGrid(1);
  // setCellEventListners();
  createColors();

  //event listner on the slider that executes the call back function inside whenver anyinput occurs on the slider
  slider.addEventListener("input", () => {
    const sliderValue = document.querySelector(".thumb-value");
    sliderValue.innerHTML = `${slider.value}`;
    //wipe the current grid so that the new grid does't just get added to the bottom of the current grid. Comment this line out
    //and observe what happens when the slider is moved.
    wipeGrid();
    createGrid(slider.value);
    // setCellEventListners();
  });

  //event listner to reset all cells on grid to having no bg color
  const reset = document.querySelector("button");
  reset.addEventListener("click", () => {
    resetGridColor();
  });

});
* {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
    font-family: 'Roboto', sans-serif;
    --thumbNumber: '5';

}

.h-100vh {
    height: 100vh;
}

.center-me {
    display: flex;
    align-items: center;
    justify-content: center;
}

.grid-container {
    border: 0.1px solid black;
    height: 400px;
    width: 400px;
}

.col {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
}

.gap-2em {
    gap: 2em;
}

.gap-1em {
    gap: 1em;
}

.title {
    font-size: 7em;
    margin:auto;
}

button {
    border:none;
    height: 50px;
    border-radius: 5px;
    background-color: #1682FF;
    color:white;
}

button:hover {
    background-color: #A0CCFF;
}

.input-label {
    font-size: 1.25rem;
}

.colors {
    display:flex;
    flex-direction: column;
    height: 100%;
    background-color: rgba(210, 210, 210, 0.1);
    border-radius: 10px;
    padding: 10px;
    justify-content:center;
}

.slider {
    -webkit-appearance: none;
    width: 100%;
    height: 25px;
    border-radius: 5px;
    background: #d3d3d3;
    outline: none;
    opacity: 1;
    -webkit-transition: .2s;
    transition: opacity .2s;
  }
  
  .slider:hover {
    opacity: 0.7;
  }
  
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 25px;
    height: 25px;
    border-radius: 5px;
    background: #1682FF;
    cursor: pointer;
 
  }
  .slider::-webkit-slider-thumb::before {
  content: var(--thumbNumber);
  }

  .range-div {
    display:flex;
    align-items: center;
    gap:0.25em;
  }


  .colors-container {
    width: 270px;
    display:flex;
    flex-direction: row;
    flex-wrap: wrap;
    row-gap:30px;
    column-gap:20px ;
    justify-content:center;
    align-items: center;
  }
  
  .color-label {
    justify-content: space-evenly;
  }

  .color:hover {
    border:none;
    outline-offset: 2px;
    outline: 2px solid #1682FF;
  }
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Etch a Sketch</title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&family=Space+Mono&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="center-me h-100vh">
      <div class="col gap-2em">
        <p class="title">Etch a Sketch</p>
        <div class="row gap-2em">
          <div class="grid-container"></div>
          <div class="col gap-1em">
            <div class="slidecontainer col gap-1em">
              <label for="slider" class="input-label"> GRID SIZE</label>
              <div class="range-div">
                <div class="thumb-value input-label">1</div>
                <input
                  name="slider"
                  type="range"
                  min="1"
                  max="100"
                  value="1"
                  class="slider"
                  id="slider"
                />
              </div>
            </div>
            <button class="input-label">RESET</button>
            <div class="gap-1em colors">
              <p class="input-label row gap-1em color-label">COLORS</p>
              <div class="center-me">
                <div class="colors-container"></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <script src="script.js" async defer></script>
  </body>
</html>

Coloring cells only when the user is holding the mouse down: (CODE IM HAVING AN ISSUE WITH)

//query selector for the grid container (the grids parent container)
const gridContainer = document.querySelector(".grid-container");

let cellColor = "black";

//an object to store hex colors and their names
const colors = {
  White: "#FFFFFF",
  Black: "#000000",
  Red: "#FF2D00",
  Blue: "#002BFF",
  LightSalmon: "#FFA07A",
  IndianRed: "#CD5C5C",
  DarkRed: "#8B0000",
  LightGreen: "#90EE90  ",
  MediumSpringGreen: "#00FA9A   ",
  MediumSeaGreen: "#3CB371",
  paleTurquoise: "#40E0D0",
  aqua: "#00FFFF",
  SteelBlue: "#4682B4",
};

const gridWidth = gridContainer.offsetWidth;

//function to return a nodelist of all of the nodes that have an id of cell
const selectCell = () => {
  return (cellQuerySelectAll = document.querySelectorAll("#cells"));
};

//function to wipe grid
const wipeGrid = () => {
  gridContainer.innerHTML = "";
};

const resetGridColor = () => {
  const gridCells = selectCell();
  gridCells.forEach((cell) => {
    cell.style.backgroundColor = "";
  });
};

const colorCell = (cell, color) => {
  return (cell.style.backgroundColor = `${color}`);
};

//function used to create grid with the parameter being passed in being how many cells the user wants
const createGrid = (numberOfCells) => {
  //create a div that is a colum which will each hold a row of cells.
  const col = document.createElement("div");
  //setting the class for col so that it becomes a flex col (check css => col)
  col.className = "col";

  //for loop used to append rows of cells to col div
  for (let y = 0; y < numberOfCells; y++) {
    //create a div that will be a row of cells
    const row = document.createElement("div");
    //setting the class for row so that it becomes a flex row (check css => row)
    row.className = "row";
    //for loop used to append cells to row div
    for (let x = 0; x < numberOfCells; x++) {
      //create a div that is the cell for the grid
      const cell = document.createElement("div");
      //setting the styling for the cells
      //width and height are set dynamicallt based on the grid containers width and how many cells the user wants
      cell.style.cssText = `width:${gridWidth / numberOfCells}px; height:${
        gridWidth / numberOfCells
      }px; `;
      cell.id = "cells";

      //var to store state of mouse
      let mouseDown = false;
      //event listener on whole grid to see if mouse is down on it (not just individual cells)
      gridContainer.addEventListener("mousedown", () => {
        mouseDown = true;
        //event listener on each cell to see if mouse is over them
        cell.addEventListener("mousemove", () => {
          //whenever the mouse goes over a cell the callback function in the event listner is executed
          //only if the mouse is down then will the the cells be colored
          if (mouseDown) {
            colorCell(cell, cellColor);
          }
        });
      });
      gridContainer.addEventListener("mouseup", () => {
        mouseDown = false;
      });

      row.appendChild(cell);
    }
    col.appendChild(row);
  }
  gridContainer.appendChild(col);
};

const createColors = () => {
  //for loop to get key in colors object
  for (const colorName in colors) {
    //create div for each color
    const color = document.createElement("div");
    color.id = `${colorName}`;
    color.classList.add("color");
    //styling for each color
    color.style.cssText = `width:30px; height:30px; border-radius:50%; background-color:${colors[colorName]}; display:flex; justify-content:center;`;
    //when user clicks on color changes global color var to hex color selected
    color.addEventListener("click", () => {
      cellColor = `${colors[colorName]}`;
    });
    const colorContainer = document.querySelector(".colors-container");
    //apend colors to the color container
    colorContainer.appendChild(color);
  }
};


function ready(callback) {
  // in case the document is already rendered
  if (document.readyState != "loading") callback();
  else if (document.addEventListener)
    document.addEventListener("DOMContentLoaded", callback);
  else
    document.attachEvent("onreadystatechange", function () {
      if (document.readyState == "complete") callback();
    });
}

//function only runs once
ready(function () {
  //query selector for slider used to control grid size
  const slider = document.getElementById("slider");

  //need to orginally make the grid once by default as the code below only creates a grid once there is input on the slider
  createGrid(1);
  // setCellEventListners();
  createColors();

  //event listner on the slider that executes the call back function inside whenver anyinput occurs on the slider
  slider.addEventListener("input", () => {
    const sliderValue = document.querySelector(".thumb-value");
 
    sliderValue.innerHTML = `${slider.value}`;
    //wipe the current grid so that the new grid does't just get added to the bottom of the current grid. Comment this line out
    //and observe what happens when the slider is moved.
    wipeGrid();
    createGrid(slider.value);
    // setCellEventListners();
  });

  //event listner to reset all cells on grid to having no bg color
  const reset = document.querySelector("button");
  reset.addEventListener("click", () => {
    resetGridColor();
  });
  //An alternative method to the event listner above
  // slider.oninput = function () {
  //   wipeGrid();
  //   createGrid(this.value);
  //   setCellEventListners();
  // };
});
* {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
    font-family: 'Roboto', sans-serif;
    --thumbNumber: '5';

}

.h-100vh {
    height: 100vh;
}

.center-me {
    display: flex;
    align-items: center;
    justify-content: center;
}

.grid-container {
    border: 0.1px solid black;
    height: 400px;
    width: 400px;
}

.col {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
}

.gap-2em {
    gap: 2em;
}

.gap-1em {
    gap: 1em;
}

.title {
    font-size: 7em;
    margin:auto;
}

button {
    border:none;
    height: 50px;
    border-radius: 5px;
    background-color: #1682FF;
    color:white;
}

button:hover {
    background-color: #A0CCFF;
}

.input-label {
    font-size: 1.25rem;
}

.colors {
    display:flex;
    flex-direction: column;
    height: 100%;
    background-color: rgba(210, 210, 210, 0.1);
    border-radius: 10px;
    padding: 10px;
    justify-content:center;
}

.slider {
    -webkit-appearance: none;
    width: 100%;
    height: 25px;
    border-radius: 5px;
    background: #d3d3d3;
    outline: none;
    opacity: 1;
    -webkit-transition: .2s;
    transition: opacity .2s;
  }
  
  .slider:hover {
    opacity: 0.7;
  }
  
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 25px;
    height: 25px;
    border-radius: 5px;
    background: #1682FF;
    cursor: pointer;
 
  }
  .slider::-webkit-slider-thumb::before {
  content: var(--thumbNumber);
  }

  .range-div {
    display:flex;
    align-items: center;
    gap:0.25em;
  }


  .colors-container {
    width: 270px;
    display:flex;
    flex-direction: row;
    flex-wrap: wrap;
    row-gap:30px;
    column-gap:20px ;
    justify-content:center;
    align-items: center;
  }
  
  .color-label {
    justify-content: space-evenly;
  }

  .color:hover {
    border:none;
    outline-offset: 2px;
    outline: 2px solid #1682FF;
  }
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Etch a Sketch</title>
    <meta name="description" content="" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@900&family=Space+Mono&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="center-me h-100vh">
      <div class="col gap-2em">
        <p class="title">Etch a Sketch</p>
        <div class="row gap-2em">
          <div class="grid-container"></div>
          <div class="col gap-1em">
            <div class="slidecontainer col gap-1em">
              <label for="slider" class="input-label"> GRID SIZE</label>
              <div class="range-div">
                <div class="thumb-value input-label">1</div>
                <input
                  name="slider"
                  type="range"
                  min="1"
                  max="100"
                  value="1"
                  class="slider"
                  id="slider"
                />
              </div>
            </div>
            <button class="input-label">RESET</button>
            <div class="gap-1em colors">
              <p class="input-label row gap-1em color-label">COLORS</p>
              <div class="center-me">
                <div class="colors-container"></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <script src="script.js" async defer></script>
  </body>
</html>

My guess as to why this is happening is that the events are bubbling to the grid container since I've put 2 event listeners on it whenever the mouse moves in the grid. To try and fix this I changed the mouse move event on cells to a capture event rather than a bubble, but it didn't seem to work.

The event listeners are set in the createGrid function.

If anyone can help me figure this out it would be much appreciated.

0 Answers
Related