Search code examples
javascripthtmlfrontendaddeventlistenerevent-listener

How do I access the element which haven't been created yet in javascript?


I am completeing the Etch-a-Sketch project of TheOdinProject. I want to access the grid's cell for changing it's color, but they are created in the scope of a for() loop and that for() loop is inside the scope of a function. I've searched other questions and not getting what I want or maybe I cannot understand whatis being conveyed.

The function for creating grid

function changeGridSize(number) {
  let container = document.getElementById(`container`);
  totalNumber = number * number;
  while (container.hasChildNodes()) {
    container.removeChild(container.firstChild);
  }

  for (let i = 0; i < totalNumber; i++) {
    let gridSquare = document.createElement("div");
    gridSquare.classList.add("grid_square");

    let cellwidth = container.clientWidth / number + `px`;
    let cellheight = container.clientHeight / number + `px`;

    gridSquare.setAttribute(
      `style`,
      `width: ${cellwidth}; height: ${cellheight};`
    );
    container.appendChild(gridSquare);
  }
}

The code for changing color

let color = document.getElementById(`colour_picker`);
color.addEventListener(`change`, changeColor() );
let choosenColour = color.value;

function changeColor() {
  while (container.hasChildNodes()) {
    gridSquare.setAttribute(`style`, `background-color: ${choosenColour};`)
  }
}

When the user picks a color from the color picker the background color for the cells should change to that color which the user selected. But when I add the change event listener to the color picker the page is crashing and also when I try to change the grid size above 40x40.

Edit: html


    <div id="outer_container">
        <div id="container"> </div>
    </div>
    <div id="controls">
     <input type="color" class="user_input" name="" id="colour_picker" value="#000000">
    <div class="slider">
            <div class="toggle_change" id="arrow_down"></div>
            <input style="margin-bottom: 0;" class="user_input" type="range" min="2" max="100" value="16" name="options"
                id="grid_range">
            <div class="toggle_change" id="arrow_up"></div>
        </div>

    </div>
    </body>```

Solution

  • There's one main problem in your code, being how you pass the callback function to the addEventListener by invoking it instead of using its value.

    Said that, I just simplified how you style the elements programmatically by using directly the style property of the element instead of changing the value of the style attribute.

    I also decided to style the html that you didn't share according to my taste and most importatly setting the #container as a flexbox to accomodate the n*nsquares inside.

    let container = document.getElementById(`container`);
    
    //on page loaded, click the #resetGrid button
    document.addEventListener('DOMContentLoaded', ()=>{
      document.getElementById('resetGrid').click();
    })
    
    //on click over the #resetGrid button, changeGridSize() accordingly
    document.getElementById('resetGrid')
      .addEventListener('click', (event)=>{
        const size = document.getElementById('gridsize').value;
        changeGridSize(size);
      });
    
    //empty the #container and add a number of div.grid_square based on arg number
    function changeGridSize(number) {
    
      totalNumber = number * number;
    
      while (container.hasChildNodes()) {
        container.removeChild(container.firstChild);
      }
    
      for (let i = 0; i < totalNumber; i++) {
        let gridSquare = document.createElement("div");
        gridSquare.classList.add("grid_square");
    
        let cellwidth = container.clientWidth / number;
        let cellheight = container.clientHeight / number;
    
        gridSquare.style.width = `${cellwidth}px`;
        gridSquare.style.height = `${cellheight}px`;
    
        container.appendChild(gridSquare);
      }
    }
    
    let color = document.getElementById(`colour_picker`);
    color.addEventListener(`change`, changeColor);
    
    //called when #color_picker change event triggers,
    //setting the background of each single .grid_square accordingly
    function changeColor(event) {
      for (let box of container.children) {
        box.style.background = this.value;
      }
    }
    body {
      display: flex;
      justify-content: space-around;
      align-items: center;  
      padding-top:  3em;
    }
    
    #container {
      width: 100px;
      height: 100px;
      border: solid 1px black;
      display: flex;
      flex-wrap: wrap;
    }
    
    .grid_square {
      border: solid 1px;
      box-sizing: border-box;
    }
    
    /*controls styles*/
    
    .controls label[for="color_picker"]{
      vertical-align: text-bottom;
    }
    
    .controls .input-group {
        display: flex;
        align-items: center;
    }
    
    .controls #gridsize{
      width: 2em;
      margin-right: .5em;
    }
    
    .controls button{
      width: 100%;
      cursor: pointer;
    }
    <body>
      <div id="container"></div>
      <div class="controls">
        <label for="colour_picker">Pick a color:</label>
        <input id="colour_picker" type="color">
        <br><br>
        <div class="input-group">
          <input id="gridsize" type="number" value="4" style="width: 50px;">
          <button id="resetGrid">Reset</button>
        </div>
      </div>
    </body>