Using javascript to color in cells of a grid of "pixels"

I have a project similar to an Etch-a-Sketch in progress. The main challenge I am currently facing is how to gradually darken each cell of the grid with every pass of the mouse cursor, based on the opacity of the cell being hovered over. When the grid cells are initially created, the background color is set to black and the opacity to 0. I have a function called shadeCells() that I expect to increase the opacity by 10% for each mouseover action, but instead it just sets the opacity to 10%, and subsequent passes of the mouse do not change anything if the opacity is already at 10%.

const container = document.querySelector('.gridContainer');
const startButton = document.querySelector('.gridCreator');

function createGrid(rows = 16, columns = 16) { // Creates default grid of 16x16 on page load
  total = rows * columns;

  for (i = 0; i < total; i++) {
    cells = document.createElement('div');
    cells.classList.add('cell');
    cells.setAttribute('style', 'margin: 0; padding: 0; background-color: black; opacity: 0;');
    container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
    container.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
    container.appendChild(cells);
  }
  shadeCells();
}
createGrid();

function newGrid(layout) { // Prompts user for input between 2 and 100 to create new grid of a different size
  const cellCount = document.querySelectorAll('.cell');

  for (i = 0; i < cellCount.length; i++) {
    container.removeChild(cellCount[i]);
  }
  do {
    layout = parseInt(prompt('How many columns and rows would you like to play? Pick between 12 and 100!'));
    gridSize = layout * layout;
  } while ((layout < 2 && Number) || (layout > 100 && Number));
  createGrid(layout, layout);
}

function shadeCells() { // Changes cell opacity on mouseover
  const cells = document.querySelectorAll('.cell');

  cells.forEach(cell => {
    cell.addEventListener('mouseover', () => {
      if (cell.style.opacity >= 0.1) {
        cell.style.opacity += 0.1;
      } else {
        cell.style.opacity = 0.1;
      }
    })
  })
}

startButton.addEventListener('click', newGrid);
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

#header {
  display: flex;
  flex-direction: row;
  justify-content: center;
  gap: 3%;
}

.headerText {
  font-size: 40px;
  font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif
}

button {
  height: 50%;
  width: 7%;
  margin: 0%;
  align-self: flex-end;
  border-radius: 10px;
  border: solid black 1px;
  box-shadow: 3px 3px;
}

.gridContainer {
  margin: auto;
  height: 600px;
  width: 600px;
  border: solid black 1px;
  display: grid;
  grid-template-columns: repeat(auto, 1fr);
}
<div id="header">
  <div class="headerText">Etch-A-Sketch</div>
  <button class="gridCreator">Create Grid</button>
</div>
<div class="gridContainer"></div>

(Link to CodePen: https://codepen.io/codesharingaccount/pen/xxPjrMy)

Answer №1

To ensure that the addition operation works correctly, you need to convert cell.style.opacity to a Number before performing the addition using the += operator:

cell.style.opacity = Number(cell.style.opacity) + 0.1;

const container = document.querySelector('.gridContainer');
const startButton = document.querySelector('.gridCreator');

function createGrid(rows = 16, columns = 16) { // Sets up default grid of 16x16 on initial page load
  total = rows * columns;

  for (i = 0; i < total; i++) {
    cells = document.createElement('div');
    cells.classList.add('cell');
    cells.setAttribute('style', 'margin: 0; padding: 0; background-color: black; opacity: 0;');
    container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
    container.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
    container.appendChild(cells);
  }
  shadeCells();
}
createGrid();

function newGrid(layout) { // Prompts user to input a number between 2 and 100 to create a new grid with a different size
  const cellCount = document.querySelectorAll('.cell');

  for (i = 0; i < cellCount.length; i++) {
    container.removeChild(cellCount[i]);
  }

  do {
    layout = parseInt(prompt('Enter the number of columns and rows for the grid (between 12 and 100):'));
  } while ((layout < 2 || layout > 100));
  
  createGrid(layout, layout);
}

function shadeCells() { // Changes opacity of grid cells on mouseover
  const cells = document.querySelectorAll('.cell');

  cells.forEach(cell => {
    cell.addEventListener('mouseover', () => {
      if (cell.style.opacity >= 0.1) {
        cell.style.opacity = Number(cell.style.opacity) + 0.1;
      } else {
        cell.style.opacity = 0.1;
      }
    })
  })
}

startButton.addEventListener('click', newGrid);
body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

#header {
  display: flex;
  flex-direction: row;
  justify-content: center;
  gap: 3%;
}

.headerText {
  font-size: 40px;
  font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif
}

button {
  height: 50%;
  width: 7%;
  margin: 0%;
  align-self: flex-end;
  border-radius: 10px;
  border: solid black 1px;
  box-shadow: 3px 3px;
}

.gridContainer {
  margin: auto;
  height: 600px;
  width: 600px;
  border: solid black 1px;
  display: grid;
  grid-template-columns: repeat(auto, 1fr);
}
<div id="header">
  <div class="headerText">Etch-A-Sketch</div>
  <button class="gridCreator">Create Grid</button>
</div>
<div class="gridContainer"></div>

(If you try to add to a string, it will concatenate the values, resulting in unexpected outcomes. It would be more helpful if this action threw an error instead of failing silently, but unfortunately, that's not how things work in reality)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to obtain a date in the format of yyyy_mm_dd from a datepicker tool?

I am working with 2 datepickers that have similar structures, but different names. The first one has the id "fecha_1_1" and the second one has the id "fecha_1_2". <div class="col-sm-3"> <p>Desde</p> <div class="f ...

What are the steps to create a dynamic navigation menu in Angular 2?

I have successfully implemented this design using vanilla CSS and JS, but I am encountering challenges when trying to replicate it in Angular 2. Setting aside routing concerns, here is the current state of my component: navbar.component.ts import { Comp ...

Performing asynchronous ajax calls with jQuery

Here is some code I have that involves a list and making an ajax call for each element in the list: util.testMethod = function(list) { var map = new Map(); list.forEach(function(data) { $.ajax({ ...

Encountering a `ECONNREFUSED` error while attempting to dispatch an action in the

I have decided to convert my Nuxt application to SSR in order to utilize nuxtServerInit and asyncData. Below are the steps I followed during this conversion process. Removed ssr: false from nuxt.config.js Dispatched actions to initialize the store's ...

The sidebar's background is cut off before reaching the bottom when scrolling

I have been working on creating a sidebar that includes a background image with a transparent overlay. However, I encountered an issue where the overlay background does not stretch all the way to the bottom when the scroll bar becomes visible. Despite sett ...

The Next.js application is functioning smoothly in development, but encounters errors during the building and deployment processes

While my Next.js app compiles and runs smoothly locally during development (using npm run dev), I encounter a failed build when attempting to compile the project (using npm run build). After researching online, it seems that unhandled promises may be the c ...

What is the best way to conceal an element so that it only becomes visible when a user begins to input text

Hey there! I'm in the process of adding a search feature to my Jekyll site, and I've opted to use Simple-Jekyll-Search, which you can check out here: Link. Take a peek at what's inside my search.html: <input type="text" id="my-search-in ...

What is the process for transferring data (BLOB datatype) to a different table?

I need assistance with transferring data from one blob to another table in the same data type blob. However, I encountered an error message regarding SQL syntax: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB serv ...

Changes in an image element's transition can result in either resizing or shifting positions

When I apply an opacity transition to an img element as shown here, I notice that the size of the image changes or it appears to move at the start and end of the transition. Below is a simple CSS code for styling: img{ height:165px; width:165px; ...

Filtering function that works without specific knowledge of keys

I'm working on a JavaScript method to filter a list dynamically without knowing the specific key (s). I've made some progress, but I'm stuck and not sure how to proceed. The foreach loop I have isn't correct, but I used it for clarity. ...

Connecting buttons to JavaScript functions that interact with MySQL database entries

I am working on a task involving rendering a database table using XMLHttpRequest in JavaScript to a PHP page. My goal is to display each entry from the table as an HTML row/cell with two buttons within each "entry". These buttons should trigger specific Ja ...

Issue with TableHead not functioning properly when sorting is requested

I'm currently facing an issue with my table that has clickable row headers for sorting functionality using the onRequestSort function. Unfortunately, it seems like this feature is not working as expected. I have implemented the sorting logic using rea ...

What is the best way to store article IDs using Javascript or HTML?

On my web page, I have a collection of links that users can interact with by clicking and leaving comments. These links are loaded through JSON, each with its unique identifier. But here's my dilemma - how can I determine which link has been clicked? ...

Is there a way to implement the border-box property for Internet Explorer versions 6 and

Similar Question: How to implement box-sizing in IE7 Anyone know a method or workaround to apply box-sizing:border-box; in IE6 and IE7? Even just for IE7? ...

"Quotes are essential in Javastript syntax for specifying string values

I need to implement a JavaScript syntax to generate unique URLs for each image. The Robohash website provides random robot images based on different URL endings. I tried the code below, but it seems like ${props.id} is being interpreted as part of the UR ...

Break down the text of a paragraph into separate words, placing each word within its own span element, and then add animations

I am facing an issue with my paragraph element that has the display property set to hidden. I have split each word of the paragraph and placed them inside individual span elements. My goal is to create an effect where each word comes from different locatio ...

Sass: Setting a maximum width relative to the parent element's width

I have a resizable container with two buttons inside, one of which has dynamic text. Within my scss file, I am aiming to implement a condition where if the width of the container is less than 200, then the max width of the dynamic button should be 135px, ...

What steps should I take to ensure that this website is responsive across all screen sizes?

Struggling with adapting to different screen sizes, especially with Bootstrap. Below are the images for reference: https://i.stack.imgur.com/AlCjA.png https://i.stack.imgur.com/FT2mU.png Sass source code snippet: body background: $main-background ...

What steps do I need to follow to create a controller component for a Form Element

I am trying to create a dynamic controller component in React Native, but I am facing issues with accessing errors. I am using "react-hook-form" for form elements. Here is my component: const { control, handleSubmit, formState: {errors}, ...

When attempting to add an item to an array within a sub-document using Mongoose and MongoDB, the error message "Property 'messages' not found" is returned

I am working with four different models: teacher, student, teacherMessageSchema, and studentMessageSchema. The teacherMessageSchema is a subdocument within the 'teacher' model under the messages: [teacherMessageSchema] property, while the student ...