JavaScript para alterar a cor de fundo da célula da grade ao clicar

Sep 17 2020

Tenho HTML básico conforme abaixo:

<!DOCTYPE html>
<html>
<head>
    <title>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</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">
    </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>

Os JavaScript abaixo são usados ​​para:

  1. obter a entrada do usuário: altura e largura
  2. desenhar grade com base na altura e largura
  3. obter seletor de cores HTML
  4. preencher uma célula com a cor de fundo com base na etapa (3) quando o usuário clicar na célula

Estou preso na etapa (4). Criei uma função respondToClick (event) e anexei a tblRow com eventListener. Deve preencher a célula com a cor de fundo ao "clicar"; mas isso não acontece. Por favor, avise onde está errado.

// obtém o valor do tamanho da grade; altura largura

let height = document.getElementById('inputHeight').value;

let width = document.getElementById('inputWidth').value;

const gridHeight = document.getElementById('inputHeight');

gridHeight.addEventListener("input", function() {
  height = document.getElementById('inputHeight').value;
})

const gridWidth = document.getElementById('inputWidth');

gridWidth.addEventListener("input", function() {
  width = document.getElementById('inputWidth').value;
})

/ função para criar tela

const table = document.getElementById('pixelCanvas');

function createCanvas(event) {
  
  for (let h = 1; h <= height; h++) {
    const row = document.createElement('tr');
    
    for (let w = 1; w <= width; w++) {
      const cell = document.createElement('td');
      
      cell.style.cssText = "height: 15px; width: 15px";
      row.appendChild(cell);
    }
    
    table.appendChild(row);
  }  
}

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

// vincula createCanvas () a "submit"

form.addEventListener('submit', createCanvas);

// ouvinte de evento para atualizar a cor

let color = document.getElementById('colorPicker').value;

document.getElementById('colorPicker').onchange = function() {
  color = this.value;
}

// função ativada quando o usuário clica apenas em

function respondToClick(event) {
  if (event.target.nodeName.toLowerCase() === 'td') {
    event.target.style.backgroundColor = color;
  }
}

const tblRow = document.getElementsByTagName('tr');

tblRow.forEach(row => function() {
  row.addEventListener("click", respondToClick);
});

Respostas

Yousaf Sep 17 2020 at 13:40

A seguir estão os problemas em seu código:

  • document.getElementsByTagName(...)retorna um HTMLCollectionno qual você não pode chamar o .forEach()método. Em vez disso, use document.querySelectorAll(...)which retorna a NodeListque tem um método chamado .forEach().

    const tblRow = document.querySelectorAll('tr');
    
  • Você precisa evitar o comportamento padrão do submitevento usandoEvent.preventDefault()

    function createCanvas(event) {
       event.preventDefault();    
       ...
    }
    
  • Como você chamou o .forEach()método no HTMLCollectionretornado por document.getElementsByTagName('tr'), ocorreu um erro e nenhum ouvinte de evento foi adicionado a qualquer trelemento.

    Em vez de adicionar clickouvinte de evento em cada trelemento, você pode aproveitar Event Bubblinge apenas adicionar o ouvinte de evento ao tableelemento.

    table.addEventListener("click", respondToClick);
    

O snippet de código a seguir mostra o exemplo de código fixo:

let height = document.getElementById('inputHeight').value;
let width = document.getElementById('inputWidth').value;
const gridHeight = document.getElementById('inputHeight');
const gridWidth = document.getElementById('inputWidth');
const table = document.getElementById('pixelCanvas');
const form = document.querySelector('form');
const colorPicker = document.getElementById('colorPicker');
let color = colorPicker.value;

gridHeight.addEventListener("input", function() {
  height = document.getElementById('inputHeight').value;
})

gridWidth.addEventListener("input", function() {
  width = document.getElementById('inputWidth').value;
})

function createCanvas(event) {
  event.preventDefault();

  for (let h = 1; h <= height; h++) {
    const row = document.createElement('tr');

    for (let w = 1; w <= width; w++) {
      const cell = document.createElement('td');
      row.appendChild(cell);
    }

    table.appendChild(row);
  }
}

form.addEventListener('submit', createCanvas);

colorPicker.addEventListener('change', function() {
  color = this.value;
});

function respondToClick(event) {
  if (event.target.matches('td')) {
    event.target.style.backgroundColor = color;
  }
}

table.addEventListener("click", respondToClick);
table,
tr,
td {
  border: 1px solid;
  border-collapse: collapse;
  ;
}

td {
  width: 30px;
  height: 30px;
}
<h1>Pixel Art</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" />
</form>

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

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


Observação lateral: dentro do responseToClick()manipulador de eventos, você tem a seguinte linha de código:

cell.style.cssText = "height: 15px; width: 15px";

Embora isso funcione, é melhor definir esses estilos no CSS. Eu fiz o mesmo no trecho de código acima.

Outro ponto que deve ser observado é que, após corrigir o código conforme mostrado no trecho de código acima, você pode querer remover o conteúdo da tabela criado anteriormente antes de criar um novo. Se é isso que você deseja fazer, você pode fazer isso definindo o .innerHTMLdo tableelemento como uma string vazia dentro do createCanvas()manipulador de eventos.

1 GabrielePetrioli Sep 17 2020 at 13:42

alguns problemas

  • você deve vincular o manipulador de cliques após criar o, rowspois vinculou o tr. Ou melhor ainda, como você já delega o evento, use o elemento table para vincular o manipulador, pois ele está lá desde o início.
  • você precisa impedir o envio do formulário ( causa um recarregamento da página )
  • você precisa limpar a tela ao criar um novo

// obtain grid size value; height & width

const gridHeight = document.getElementById('inputHeight');
const gridWidth = document.getElementById('inputWidth');

let height = gridHeight.value;
let width = gridWidth.value;

gridHeight.addEventListener("input", function() {
  height = document.getElementById('inputHeight').value;
})

gridWidth.addEventListener("input", function() {
  width = document.getElementById('inputWidth').value;
})

const table = document.getElementById('pixelCanvas');

function createCanvas(event) {
  event.preventDefault();
  table.innerHTML = '';
  for (let h = 1; h <= height; h++) {
    const row = document.createElement('tr');

    for (let w = 1; w <= width; w++) {
      const cell = document.createElement('td');
      row.appendChild(cell);
    }
    table.appendChild(row);
  }
}

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

// bind createCanvas() to "submit"
form.addEventListener('submit', createCanvas);

// event listener to update color
const picker = document.getElementById('colorPicker')
let color = picker.value;

picker.onchange = function() {
  color = this.value;
}

// function activated when user click on only

function respondToClick(event) {
  if (event.target.nodeName.toLowerCase() === 'td') {
    event.target.style.backgroundColor = color;
  }
}

table.addEventListener("click", respondToClick);
table {
  border: 1px solid black;
 }
 
 td{
 width:15px;
 height:15px;
 overflow:hidden;
 }
<h1>Pixel Art</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">
</form>

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

<h2>Design Canvas</h2>
<table id="pixelCanvas" cellspacing="0" cellpadding="0"></table>