Concerns regarding the efficiency of JavaScript (Odin Project, Etch-a-Sketch) are arising

Currently, I am delving into Javascript on the Odin Project, tackling the Etch-a-Sketch exercise. This involves creating a board where you can draw with your cursor. As part of the exercise, there's a requirement to add a resize button that allows users to specify the number of tiles on each side of the board, capped at 100 (for a 100x100 board). I managed to implement this feature successfully, but encountered performance issues as my browser struggled with around 60 tiles and crashed at the maximum limit.

The exercise primarily focuses on using Javascript to manipulate the DOM. I wrote a script to generate a div container for the entire board, with a default 16x16 board created when the page loads. Each tile is represented by a div with an event listener that changes the tile's color based on a CSS rule associated with a specific class.

Upon clicking the reset button, a function is triggered. This function resets the classes for all tiles and prompts the user to enter a new value. Once a valid value is received, the CSS rule tied to the original div container is updated with the specified number of rows and columns. Subsequently, existing tiles are removed, and new tiles are added to the div container.

Here's the code for the HTML, CSS, and JavaScript:

<!DOCTYPE html>
<html>
  <head>
    <title>Page Title</title>
    <meta charset="UTF-8"/>
    <link rel="stylesheet" href="style.css">
    <script src="script.js" defer></script>
  </head>
  <body>
    <button class="reset">Reset and resize</button>
  </body>
</html>
body {
    display: flex;
    flex-direction: column;
}

.reset {
    align-self: flex-start;
}

.sketchContainer {
    width: 900px;
    height: 900px;
    border: solid 1px black;
    display: grid;
    align-self: center;
    grid-template-rows: repeat(16, auto);
    grid-template-columns: repeat(16, auto);
}
div.tileHovered {
    background-color: #c3a375;
}
generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
    const sketchContainer = document.createElement("div");
    document.body.appendChild(sketchContainer);
    sketchContainer.classList.add("sketchContainer");
    const gridTile = document.createElement("div");
    sketchContainer.appendChild(gridTile);
    gridTile.classList.add("gridTile");
    let clonedTile;
    for (let i = 0; i < 16*16-1; i++) {
        clonedTile = gridTile.cloneNode();
        sketchContainer.appendChild(clonedTile);
    }
}

function placeEventListenersOnTiles() {
    let allTiles = document.querySelectorAll(".gridTile");
    allTiles.forEach(element => {
        element.addEventListener("mouseover", changeColor);
    });
}

function changeColor() {
    this.classList.add("tileHovered");
}

function reset() {
    let allTiles = document.querySelectorAll(".tileHovered");
    allTiles.forEach(element => {
        element.classList.remove("tileHovered");
    });
    resizeGrid();
}

function resizeGrid() {
    let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
    if (newSize > 100) {
        newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
    } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) {
        newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
    } else if (newSize === null) {
        generateDefaultGrid();
    } else {
        const styleSheet = document.styleSheets[0];
        styleSheet.cssRules[2].style.gridTemplateRows=`repeat(${newSize}, auto)`;
        styleSheet.cssRules[2].style.gridTemplateColumns=`repeat(${newSize}, auto)`;
        removeAllTiles();
        generateUserGrid(newSize);
    }
}

If you could take a look at my project on GitHub, I would appreciate any insights on why my script is inefficient and how I can optimize it. This aspect wasn't covered in the course, and I'm still new to this area. Thank you in advance!

Answer №1

The issue causing the performance problem is the repeated call of placeEventListenersOnTiles(); within the for loop in the generateUserGrid function.
This leads to an accumulation of event listeners with each iteration.
For example, in a 100x100 setup, the first tile ends up with 10000 event listeners, the second one with 9999, and so on.

To resolve this, simply move the function call outside of the loop:

generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

// Rest of the functions remain the same (omitted for brevity)

To enhance the efficiency and performance, consider learning about event delegation. This way, you can set a single event listener on the container to handle all the tiles:

generateDefaultGrid();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

// Rest of the functions with updated event handling logic (omitted for brevity)

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

Turn off Typewatch binding upon pressing the Enter key

I have integrated the Typewatch jQuery plugin into my application and below is the code snippet used for form submission and Ajax request handling. searchForm.on('submit', function(event) { if (event) { event.preventDefault(); } ...

When the text overflows the boundaries of a paragraph in CSS HTML

I am facing an issue with text overflowing in a paragraph <p style="width:200px;">Text goes here</p> As the text in the paragraph increases in width, it does not automatically wrap to a new line, causing it to overflow outside the paragraph ...

Filtering data within a specific date range on an HTML table using JavaScript

I am attempting to implement a date filtering feature on my HTML table. Users should be able to input two dates (From and To) and the data in the "Date Column" of the table will be filtered accordingly. The inputs on the page are: <input type="date" i ...

Break the page after generating specific charts with JQuery

I have a task to create multiple charts, where the first page needs to include a header and the first 8 charts. How can I insert a page break after the 8th chart and move on to a second page without a header? Each student has different subjects and score ...

Retrieving JSON data with Node.js

Receiving a JSON response from TMDB: { "id": 283350, "results": [ { "iso_3166_1": "BR", "release_dates": [ { "certification": "12", "iso_639_1": "pt", "note": "Streaming", "release_date": ...

The most efficient way to invoke a jws void method in the fewest steps possible

When calling a void method of a SOAP JWS web-service using JavaScript without expecting a result back, what is the most efficient and concise approach? ...

Tips for clearing Nightwatch session storage efficiently

To ensure the pop-up functionality was working properly, I had to reset the browser's session storage. By doing this, the pop-up will reappear. Is there a way to clear the page's Session Storage in Nightwatch? ...

Unable to reset input in a functional component using React JS

I have a component named TextInput that is responsible for rendering an input element: function TextInput({ type = "text", label, name, required = false }) { const [value, setValue] = useState(""); function handleChange(e) { se ...

Respond to a recommendation with a response

I've been working on setting up a bot for my Discord server, and I recently added a "marry" command to it. Whenever a user makes an offer, a message announcing the proposal pops up with two reaction options. The first reaction signifies yes, while th ...

Validation on the client side for a form that is displayed within a bootstrap modal using ajax technology

Within my ASP.Net Core application, I am faced with the need to utilize validation on both the server side and client side in a bootstrap modal form. While I have successfully implemented server side validation, I have encountered difficulties when it come ...

Ways to transfer large amounts of data to Amazon Web Services

After reading the node documentation on sqs.sendMessage, I noticed that it mentions the ability to send messages up to 256KB in size. However, my messages tend to exceed this limit. What is the recommended approach for handling large payloads in this scena ...

Issue with jQuery's .height() method not updating

Here is the code I am working with: $(function() { $('#team-ch').click(function() { $('#team-camb-hert').height('auto'); }); }); I am trying to change the height of a div when a link is clicked. After adding an alert ...

The React class component is throwing an unexpected error with the keyword 'this'

I encountered an error stating "Unexpected keyword 'this'" while attempting to update the React state using Redux saga. Could someone shed light on what's wrong with the code below and how I can fix it? class Welcome extends React.Component ...

Is there a way to extract the visible text from an HTML TextNode without including the HTML tags?

My current challenge involves converting a DOM node and all of its children into plain text markup of a custom design. Utilizing node.childNodes allows me to retrieve a list of all content, which can then be recursively transformed into my desired string f ...

Leverage the power of React by utilizing SVGR to easily integrate SVG files

Wondering if there's a way to bring in an SVG file from my public folder and use it as a React component like this: import { ReactComponent as MySvg } from '/assets/svg/mysvg.svg'; const MyComponent = () => { return ( <div> ...

Disregard all numbers following the period in regex

I have developed a function to format numbers by adding commas every 3 digits: formatNumber: (num) => { return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') }, The issue with this function is that it also add ...

I encountered an issue with my node/express server where Res.json is causing a

I am encountering an issue with a 100mb object that I am trying to return using a GET request: function completeTask(req, res) { // generates a large object on a child process, then sends it back to the main process res.json(bigObject); // <--- p ...

Ways to make a button blink using AngularJS

Currently working on an AngularJS SPA application where a button is present in the front-end HTML page. When this button is clicked, it triggers an operation which takes some time to complete. I want to inform the user that the operation is still in prog ...

How can you efficiently showcase multiple fetch functions on a single page by utilizing loops in PHP?

I am attempting to showcase the countries from each continent on a single page using buttons for each continent. When a user clicks on a button, I want the countries of the selected continent to be displayed. However, I am encountering an issue where only ...

Retrieving data from the Vuex store in a Nuxt component

https://i.stack.imgur.com/htnuL.png Seeking guidance on passing a list of button names into a menu component from the Vuex store as per the instructions at https://nuxtjs.org/guide/vuex-store In my /store/store.js: export const state = () => ({ & ...