2

I have created a HTML table (with user inputted cols and rows) and also have a dynamic way of selecting color.

Now I want to be able to click on individual cells in the table and color them with chosen color. I have this code so far.

My final goal is to be able to reset the colors when I hit "submit" again. Flow would be:

  1. Choose table size
  2. Choose color
  3. Color the cells in the table
  4. Reset the table upon hitting "submit" again

function makeGrid(ev) {
  ev.preventDefault();
  var heights = document.getElementById("inputHeight").value;
  var widths = document.getElementById("inputWidth").value;
  var body = document.getElementById("pixelCanvas");
  var table = document.createElement('TABLE')
  var tblB = document.createElement('TBODY');
  table.appendChild(tblB);
  for (var i=0; i<heights; i++){
    var tr = document.createElement('TR');
    table.appendChild(tr);
    for (var j=0; j<widths; j++){
      var td = document.createElement('TD')
      document.getElementById("pixelCanvas").onclick = function(){
        td = document.getElementById("colorPicker").value;
        alert(td);
      }
      table.appendChild(td);
    }
  }
  body.append(table);
  body.addEventListener('click', function(){
    var coloor = document.getElementById("colorPicker").value;
    body.style.backgroundColor = coloor;
  })
}
body {
    text-align: center;
}

h1 {
    font-family: Monoton;
    font-size: 70px;
    margin: 0.2em;
}

h2 {
    margin: 1em 0 0.25em;
}

h2:first-of-type {
    margin-top: 0.5em;
}

table,
tr,
td {
    border: 1px solid black;
    padding: 25px;
}

table {
    border-collapse: collapse;
    margin: 0 auto;
}

input[type=number] {
    width: 6em;
}
<!DOCTYPE html>
<html>
<head>
    <title>Pixel Art Maker!</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>Pixel Art Maker</h1>

    <h2>Choose Grid Size</h2>
    <form id="sizePicker" onsubmit="makeGrid(event)">
        Grid Height:
        <input type="number" id="inputHeight" name="height" min="1" value="1">
        Grid Width:
        <input type="number" id="inputWidth" name="width" min="1" value="1">
        <input type="submit" value= "submit">
    </form>

    <h2>Pick A Color</h2>
    <input type="color" id="colorPicker">

    <h2>Design Canvas</h2>
    <table id="pixelCanvas"></table>

    <script src="designs.js"></script>
</body>
</html>
Rafael
  • 7,605
  • 13
  • 31
  • 46
Vinay
  • 21
  • 2

2 Answers2

1

Almost, only a few changes:

  1. click events on the incorrect elements; only tds require event.
  2. appending td to the wrong element. (tds should only be apart of trs.)
  3. the color-picker's value should be assigned to the element's style attribute via HTMLElement.prototype.style (note: the css property name is normalized [camel-cased]).
  4. We should not append a table to a table; consider making pixelCanvas a div.

Notice this.style... is not td.style...; In the event handler, this refers to the target element. You could have used td.style..., if you used let keyword to declare td, but you used the keyword var: learn more about scope here.

Clearing the table

Clearing the table is straight-forward: delete the elements in pixelCanvas (reset pixelCanvas to its original state). This is done in two lines:

//reset pixelCanvas
while (body.firstChild)
    body.removeChild(body.firstChild);

If you will not add more children to pixelCanvas, you can change while to if.

All together:

function makeGrid(ev) {
  ev.preventDefault();

  //keep like-statements together
  var rows = document.getElementById("inputHeight").value;
  var cols = document.getElementById("inputWidth").value;
  var table = document.createElement('TABLE');
  var body = document.getElementById("pixelCanvas");
  
  //reset pixelCanvas
  while (body.firstChild)
      body.removeChild(body.firstChild);

  for (var i=0; i<rows; i++){
    var tr = document.createElement('TR');

    for (var j=0; j<cols; j++) {
      var td = document.createElement('td');
      td.onclick = function() {
        this.style.backgroundColor = document.getElementById("colorPicker").value;
      };
      tr.appendChild(td);
    }

    table.appendChild(tr);
 }

 body.append(table);
}
body {
    text-align: center;
}

h1 {
    font-family: Monoton;
    font-size: 70px;
    margin: 0.2em;
}

h2 {
    margin: 1em 0 0.25em;
}

h2:first-of-type {
    margin-top: 0.5em;
}

table,
tr,
td {
    border: 1px solid black;
    padding: 25px;
}

table {
    border-collapse: collapse;
    margin: 0 auto;
}

input[type=number] {
    width: 6em;
}
<!DOCTYPE html>
<html>
<head>
    <title>Pixel Art Maker!</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>Pixel Art Maker</h1>

    <h2>Choose Grid Size</h2>
    <form id="sizePicker" onsubmit="makeGrid(event)">
        Grid Height:
        <input type="number" id="inputHeight" name="height" min="1" value="1">
        Grid Width:
        <input type="number" id="inputWidth" name="width" min="1" value="1">
        <input type="submit" value= "submit">
    </form>

    <h2>Pick A Color</h2>
    <input type="color" id="colorPicker">

    <h2>Design Canvas</h2>
    <table id="pixelCanvas"></table>

    <script src="designs.js"></script>
</body>
</html>
Rafael
  • 7,605
  • 13
  • 31
  • 46
  • Wow!! Works like a charm. Only thing left is to reset the table , I can use the same "Submit" button(press after coloring the table) and a blank table should appear. Any ideas? I can't thank you enough – Vinay Oct 04 '18 at 00:11
  • You certainly should do that in the `onsubmit` handler, `makeGrid()`. The idea is that you want to reset the state of `pixelCanvas` *before* constructing the table. – Rafael Oct 04 '18 at 00:33
1

Edit

Added the reset process. Also replaced the inline attribute event (onsubmit) on the form with an event listener.


Don't waste resources on assigning event listeners to a ton of <td>s, use Event Delegation to use the <table> to listen for all of the <td> instead. Details on implementing the pattern is commented in demo as well as alternative methods to use that are more specialized, less verbose, and more efficient.

Demo

Details commented in demo

/*
Register the first (and only) form to submit event
Look for a node with class .destroy and if present remove it from
DOM.
Call makeGrid()
*/

document.forms[0].addEventListener('submit', function(e) {

  const destroy = document.querySelector('.destroy');

  if (destroy) {
    destroy.parentNode.removeChild(destroy);
  }
  makeGrid(e);

});


function makeGrid(ev) {

  ev.preventDefault();

  /* 
  Since there's a form with multiple form controls we are using
  HTMLFormControlsCollection API.
  Just reference the forms HTMLCollection once...
  */
  var ui = document.forms[0].elements;

  /* 
  ...and then use that reference any and every form control
  nested within that referenced form. (colorPicker was moved into
  the form)
  */
  var rowsQty = ui.inputHeight.value;
  var cellsQty = ui.inputWidth.value;
  var cellColor = ui.colorPicker.value;
  var body = document.getElementById("pixelCanvas");
  var table = document.createElement('TABLE');

  /*
  There's 2 loops: 
  1. first loop: the insertRow() method is used once on each loop. 
     insertRow() advantage is that it creates and appends with one
     call.
  */
  for (let r = 0; r < rowsQty; r++) {

    var rowObj = table.insertRow();

    /*
    2. second loop: the insertCell() method is used as many times
       as the submited number (cellsQty). insertCell() also 
       creates and appends in one call as well.
    */
    for (let c = 0; c < cellsQty; c++) {

      var cellObj = rowObj.insertCell();

    }

  }

  /*
  We will use Event Delegation so that we only need to register
  the parent node (table) to listen for an event not only for
  itself but for all of the nodes nested within it. BTW, this 
  works perfectly for dynamically created nodes when the number
  of nodes is unknown....
  */
  // Here we are registering table to listen for clicks...
  table.addEventListener('click', function(e) {

    // Reference the origin of event (clicked td)
    var tgt = e.target;
    // Reference the node registered to the event (table)
    var cur = e.currentTarget;

    // if the clicked node IS NOT the table...
    if (tgt !== cur) {

      // ...change its background to whatever value colorPicker has
      tgt.style.background = cellColor;

    }
  });
  // Mark table for reset
  table.classList.add('destroy');

  // Add completed table to DOM
  body.appendChild(table);

}
body {
  text-align: center;
}

h1 {
  font-family: Monoton;
  font-size: 70px;
  margin: 0.2em;
}

h2 {
  margin: 1em 0 0.25em;
}

h2:first-of-type {
  margin-top: 0.5em;
}

table,
tr,
td {
  border: 1px solid black;
  padding: 25px;
}

table {
  border-collapse: collapse;
  margin: 0 auto;
}

input[type=number] {
  width: 6em;
}
<!DOCTYPE html>
<html>

<head>
  <title>Pixel Art Maker!</title>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <h1>Pixel Art Maker</h1>

  <h2>Choose Grid Size</h2>
  <form id="sizePicker">
    Grid Height:
    <input type="number" id="inputHeight" name="height" min="1" value="1"> Grid Width:
    <input type="number" id="inputWidth" name="width" min="1" value="1">
    <input type="submit" value="submit">


    <h2>Pick A Color</h2>
    <input type="color" id="colorPicker">
  </form>
  <h2>Design Canvas</h2>
  <table id="pixelCanvas"></table>

  <script src="designs.js"></script>
</body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68