1

I have a grid made with a preset value that I'd like to have a function that replaces it with a grid who's cells are generated with dynamic values gotten from a prompt input. The code for both grids work independently if I were to remove the other's code, but for whatever reason I can't get the 2nd grid to replace the first one when it gets it's value.

Here is an example of my JS code that has the .replaceWith() method attempting to replace the cells of the original grid via div. I'm stumped.

    const board = document.getElementById("container");
    let button = document.getElementById("select");
    button.style.display = "flex";
    button.style.justifyContent = "center";
    board.style.justifyContent = "center";
    const cell = document.createElement('div');
        for (x=1; x <= 16; x++) {
            const cell = document.createElement('div')
            cell.className = "cells";
            cell.setAttribute("style", "height: 200px; width: 200px; color: black;");
            cell.style.backgroundColor = "blue";
            cell.style.border = "solid 2px";
            cell.style.borderColor = "black";
            board.style.columnGap = "0px";
            board.style.display ="grid";
            board.style.gridTemplateColumns = "200px 200px 200px 200px";
            board.appendChild(cell);
            cell.addEventListener("mouseover", change = () => 
            cell.style.backgroundColor = "red")
            cell.addEventListener("mouseout", reset = () => cell.style.backgroundColor = "blue") 
            };
            
    function setBoard () {
        let a = prompt("Select a the number of cells for the grid")
        if (a != null) {
            document.getElementById("container").value = a
        }
    for (x=1; x <= a; x++) {
        const nCell = document.createElement('div')
            nCell.className = "cells";
            nCell.setAttribute("style", "height: 200px; width: 200px; color: black;");
            nCell.style.backgroundColor = "blue";
            nCell.style.border = "solid 2px";
            nCell.style.borderColor = "black";
            board.style.columnGap = "0px";
            board.style.display ="grid";
            board.style.gridTemplateColumns = "200px 200px 200px 200px";
            board.appendChild(nCell);
            nCell.addEventListener("mouseover", change = () => 
            nCell.style.backgroundColor = "red")
            nCell.addEventListener("mouseout", reset = () => nCell.style.backgroundColor = "blue");
            cell.replaceWith(nCell);
    }
        
    }
    <!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">
        <link rel="stylesheet" href="styles.css">
        <title>Etch N Sketch</title>
    </head>
    <body>
    <div id="select">
        <button id="select" onclick="setBoard()">Set the number of squares</button>
    </div> 
    <div class="container" id="container">
    </div>
    <script src="script.js">
    </script>
    </body>
    </html>
seemwavy
  • 19
  • 5
  • any ideas? replaceChild() method isn't giving me any results, removing the div with the remove method blanks the board but the new one doesn't populate. – seemwavy Jul 30 '22 at 21:38
  • 1
    It sounds like you want to *empty* the board, not replace it - you're appending the new cells to the same board. – Bergi Jul 30 '22 at 21:48
  • @Bergi I am, however the remove methods don't seem to create the desired effect. the new version of the board doesn't load – seemwavy Aug 02 '22 at 23:28
  • Put [`board.replaceChildren()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/replaceChildren) or [something like that](https://stackoverflow.com/q/3955229/1048572) before your loop that adds `ncell`s again. Do not use `board.remove()`, that removes the board itself. Do not use `cell.replaceWith(nCell)`, the `cell` variable is not declared in the `setBoard` function. – Bergi Aug 03 '22 at 01:01
  • @Bergi Dude!! It worked!! Finally got this!! Thank you!!! LFG. Edit: They not letting me award you my guy but thank you and thanks to all – seemwavy Aug 03 '22 at 21:26
  • You can accept @wiisco's answer who wrote basically the same as I did in my last comment – Bergi Aug 03 '22 at 23:19

2 Answers2

2

As mentioned by @Bergi try using board.replaceChildren() to empty the board first first, and then refill the board like you do in the initial loop.

The cell.replaceWith does not do anything meaningful and can be removed. cell in this scope is the one you create in line 6 of your script, which you never append to the body or do anything else with.

wiisco
  • 36
  • 1
  • 4
1

Problems

  1. Ids must be unique, there are two #selects (see Figure I).

  2. Don't use ids if you can use class.

  3. Inline event handlers are garbage (see Figure I).

    Figure I

    <div id="select"><!--⬅️↙️ Ids must be unique -->
      <button id="select" onclick="setBoard()">Set the number of squares</button>          
                        <!-- ↖️ Inline event handler -->
    </div> 
    
  4. DRY Don't Repeat Yourself. There are two identical blocks of code that generates a grid.

  5. Avoid adding inline styles, use CSS.

Solutions

  1. All ids have been replaced with classes. (see Figure II)

    Figure II

    const board = document.querySelector('.board');
    // Reference class prefix with a dot↗️
    
  2. Replaced inline event handler with an onevent property (see Figure III).

    Figure III

    // Onevent property
    document.querySelector('.select').onclick = enterData;
    // OR
    // Event listener
    document.querySelector('.select').addEventListener('click', enterData);
    
  3. Removed 14 duplicated lines of JavaScript and refactored everything into two functions. enterData() is triggered by clicking .select, and setBoard() is called when enterData() receives a number to pass onto setBoard(). setBoard() is also called after the page has loaded.

  4. Replaced 10 lines of JavaScript with 3 CSS rulesets. Inline styles are limiting in that they are not applied globally like CSS stylesheets or <style> tag. The mouse event handlers were replaced by this:

    Figure IV

    .cell:hover {
      background: red;
    }
    
  5. You need to clear .board before generating a new grid

    Figure V

    board.replaceChildren();
    

    Also, <div>s cannot have a value property

    Figure VI

     document.getElementById("container").value = a //
    

Details are commented in example

// Reference .board
const board = document.querySelector(".board");

// Call setBoard() -- make a 4x4 grid
setBoard(16);

// Bind the click event to .select
document.querySelector(".select").onclick = enterData;

// Prompt takes the number entered and passes it to setBoard()
function enterData() {
  let a = prompt("Select the number of cells for the grid");
  // If 'a' is a number, call setBoard()
  if (typeof a === 'number') {
    setBoard(a);
  }
}

function setBoard(a) {
  // Remove everything within .board
  board.replaceChildren();
  // Create a grid
  for (let x = 1; x <= a; x++) {
    const nCell = document.createElement('div')
    nCell.className = "cell";
    board.appendChild(nCell);
  }
}
body {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
}

.select {
  display: block;
  width: 50%;
  margin: 10px auto;
}

.board {
  column-gap: 0px;
  display: grid;
  grid-template-columns: 200px 200px 200px 200px;
  margin: 0 auto;
}

.cell {
  height: 200px;
  width: 200px;
  color: black;
  background-color: blue;
  border: solid 2px black;
}

.cell:hover {
  background: red
}
<!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">
  <link rel="stylesheet" href="styles.css">
  <title>Etch N Sketch</title>
</head>

<body>
  <button class="select">Set the number of squares</button>
  <div class="board"></div>
  <script src="script.js"></script>
</body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • "*Don't use ids if you can use class.*" - there's no problems with ids there. And an id is totally appropriate if this is the only such element in the page. Using classes makes no sense if you have only a single element, and use `querySelector()` only - using classes means your code should support an arbitrary number of elements, with `querySelectorAll()`. – Bergi Jul 31 '22 at 00:00
  • What's the difference between `#tag1` and `.tag1` on a single element? If referenced properly there is no difference except that beginners are less likely to run into problems if they duplicate `.tag1`. – zer00ne Jul 31 '22 at 00:07
  • The id conveys the intent of uniqueness, and makes it clear that duplicating the markup is a mistake. If just using a class instead of an id, like your code does, one will still run into problems when duplicating the element, because the code doesn't handle it properly. – Bergi Jul 31 '22 at 00:14
  • Hey, sorry for the delayed response. I gave this code a try but it still doesn't replace the old preset grid with the new user defined one. I've tried some alternate variations and my closest result was the old grid being cleard but the one replacing it only having a single square. Your syntax and best practice suggestions were really helpful though, thanks – seemwavy Aug 02 '22 at 23:25