Search code examples
javascriptfunctioncapitalize

Creating a grid using javascript without display:grid


i'm currently working on an etch-a-sketch on the odin project. Here's the code I've come up with.

const container = document.querySelector(".container");
const input = document.querySelector("input");
const submitBtn = document.querySelector("button");

function createGrid() {
    let value=input.value;
    for (let i = 0; i < value; i++) {

        
        const createDivRow = document.createElement("div");
        createDivRow.classList.add("gridrow")
         createDivRow.textContent = i;
        container.appendChild(createDivRow);
    }
    
}

function createGridItems() {
    let value = input.value;
    for (let i = 0; i < value; i++) {
        const divRow = document.querySelectorAll(".gridrow");
        const createDivItem = document.createElement("div");
        createDivItem.textContent = "aa";
        divRow.appendChild(createDivItem);
    }
}

submitBtn.addEventListener("click", createGrid);
submitBtn.addEventListener("click", createGridItems);

In the createGrid() function, I aim to create n number of divs(n being the user input from an input textbox), which I have succeeded. The result was n number of divs with class="gridrow" in the con†ainer.

In the createGridItems() function, I'd like to put the same number of divs "n" into each .gridrow row(which I've given the variable divRow).

I tried declaring const divRow = document.querySelector(".gridRow") and the result was n number of divs in the FIRST .gridrow div, which is almost what I wanted.

So, I figured I'd declare const divRow = document.querySelectorAll(".gridrow") to target all the .gridrow I've created and put n number of divs into EACH .gridrow div. As a result, all I got was just empty n number of .gridrow divs, without the divs that I want.

Can anyone tell me where I've gone wrong ? I was sure the code above would work because I've already targeted all the .divrow divs when I declare divRow = document.querySelectorAll(".gridrow").

Thank you!


Solution

  • document.querySelectorAll returns a NodeList (a collection of elements).

    If you checked the javascript console, your code throws an error when trying to .appendChild over the collection.

    You instead needed to iterate over that collection and for each item (being a .gridrow) append there a number of elements.

    https://developer.mozilla.org/en-US/docs/Web/API/NodeList

    Note: Although NodeList is not an Array, it is possible to iterate over it with forEach(). It can also be converted to a real Array using Array.from().

    This is a quick demo:

    const container = document.querySelector(".container");
    const input = document.querySelector("input");
    const submitBtn = document.querySelector("button");
    
    function createGrid() {
        const value = input.value;
        for (let i = 0; i < value; i++) {        
            const createDivRow = document.createElement("div");
            createDivRow.classList.add("gridrow")
             createDivRow.textContent = i;
            container.appendChild(createDivRow);
        }
        
    }
    
    function createGridItems() {  
        const value = input.value;
        //here you select the rows as .gridrow elements
        const divRows = document.querySelectorAll(".gridrow");    
        //iterate over each one of them
        divRows.forEach(divRow =>{
            //and append to it a new div for as many times as the value fetched from the input element
            for(let i=0;i < value; i++){
              const createDivItem = document.createElement("div");
              createDivItem.textContent = "aa";
              divRow.appendChild(createDivItem);
            }        
        });       
    }
    
    submitBtn.addEventListener("click", createGrid);
    submitBtn.addEventListener("click", createGridItems);
    .gridrow{
      border: solid 1px;  
      margin-top: 1em;
    }
    
    .gridrow > div {
      border: solid 1px red;
      margin-left: 1em;
      display: inline-block;
    }
    <div class="container"></div>
    
    <input type="text">
    <button>submit</button>

    And finally since the performance became a concern among the comments, here is a performance test to show the difference in time for each solution:

    • addDivsOneByOne took 2.5999999046325684 milliseconds
    • addDivsInBatch took 2 milliseconds
    • addDivsUsingInnerHTML took 1 milliseconds

    const numElements = 1000;
    
    function addDivsOneByOne(containerId) {
      const container = document.getElementById(containerId);
      for (let i = 0; i < numElements; i++) {
        const div = document.createElement('div');
        div.textContent = `${i + 1}`;
        container.appendChild(div);
      }
    }
    
    function addDivsInBatch(containerId) {
      const container = document.getElementById(containerId);
      const fragment = document.createDocumentFragment();
      for (let i = 0; i < numElements; i++) {
        const div = document.createElement('div');
        div.textContent = `${i + 1}`;
        fragment.appendChild(div);
      }
      container.appendChild(fragment);
    }
    
    function addDivsUsingInnerHTML(containerId) {
      const container = document.getElementById(containerId);
      let html = '';
      for (let i = 0; i < numElements; i++) {
        html += `<div>${i + 1}</div>`;
      }
      container.innerHTML = html;
    }
    
    function runBenchmark() {
      document.getElementById('container1').innerHTML = '';
      document.getElementById('container2').innerHTML = '';
      document.getElementById('container3').innerHTML = '';
    
      const results = [];
    
      // Measure addDivsOneByOne
      let start = performance.now();
      addDivsOneByOne('container1');
      let end = performance.now();
      results.push(`addDivsOneByOne took ${end - start} milliseconds.`);
    
      // Measure addDivsInBatch
      start = performance.now();
      addDivsInBatch('container2');
      end = performance.now();
      results.push(`addDivsInBatch took ${end - start} milliseconds.`);
    
      // Measure addDivsUsingInnerHTML
      start = performance.now();
      addDivsUsingInnerHTML('container3');
      end = performance.now();
      results.push(`addDivsUsingInnerHTML took ${end - start} milliseconds.`);
    
      // Display results
      document.getElementById('results').innerHTML = results.join('<br>');
    }
    .container {
      border: 1px solid #ccc;
      padding: 10px;
      margin: 10px 0;
    }
    
    .container div {
      margin: 2px 0;
    }
    <div id="results"></div>
    
    <div class="container" id="container1"></div>
    <div class="container" id="container2"></div>
    <div class="container" id="container3"></div>
    <div>
      <button onclick="runBenchmark()">Run Benchmark</button>
    </div>