Incorrect x and y coordinates in the 'onclick' event

Hey there, I'm currently working on a simple prototype for an app. The concept is straightforward: there is a box, and when you click on it, it creates a square that changes color when clicked on.

The issue I'm facing is that when I click on the box to generate the square, it appears below the box, which is not the desired behavior.

Below is the complete code snippet:

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
    
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
            background-color: #27293F;
        }
        
        #title {
            width: 100vw;
            height: 20vh;
        }
        
        h1 {
            width: 100%;
            color: #4FFF5A;
            text-align: center;
        }
        
         p {
            width: 100vw;
            color: #379A3D;
            text-align: center;
        }
        
        #plants {
            width: 100vw;
            height: 75vh;
            border: 10px solid #FF5555;
            display: flex;
            flex-wrap: wrap;
        }
    
        .plant {
            position: static;
            width: 50px;
            height: 50px;
        }
    </style>
    
    <script src="Plant.js" type="text/javascript" charset="utf-8"></script>
</head>

<body>
    <section id="title">
        <h1>The plant generator 🌱</h1>
        <p>click anywhere in the box to generate a plant</p>
    </section>
    
    <div id="plants">
        
    </div>

    <script type="text/javascript" charset="utf-8">
        const colors = ['red', 'blue', 'green'];
        
        const plantsContainer = document.getElementById('plants');
        
        plantsContainer.addEventListener('mousedown', function(event) {
            generatePlant(event.x, event.y, plantsContainer);
        });
    
        /**
         * Changes the plant's color on click.
         * @param   {Plant}            plant          Target to change the color.
         * @param   {HTMLDivElement}   plantElement   The plant element.
         * @returns undefined
         */
        function changeColorOnClick(plant, plantElement) {
            let index = plant.getState().colorIndex + 1;
            
            index >= colors.length ? index = 0 : null;
            
            plant.setState({ ...plant.getState(), color: colors[index], colorIndex: index });
            
            plantElement.style.backgroundColor = plant.getState().color;
        }
    
        /**
         * Generate a plant.
         * @param   {Number} x         The plant's x cordinate.
         * @param   {Number} y         The plant's y cordinate.
         * @param   {any}    appendAt? The element to append the plant at.
         * @returns {Plant}
         */
        function generatePlant(x, y, appendAt) {
            let plant = new Plant();
            
            x = x ?? Math.floor(Math.random() * window.innerWidth);
            y = y ?? Math.floor(Math.random() * window.innerHeight);
            
            generatePlantElement(plant, x, y);
            
            console.log(x, y);
            
            return plant;
        }
        
        /**
         * Generate a plant element.
         * @param {Plant}  plant     The plant.
         * @param {Number} x         The plant's x cordinate.
         * @param {Number} y         The plant's y cordinate.
         * @param {any}    appendAt? The element to append the plant element at [optional].
         */
        function generatePlantElement(plant, x, y, appendAt) {
            let plantElement = document.createElement("div");
            let plantState = plant.getState();
            
            plantElement.classList.add("plant");
            
            plantElement.style.marginLeft = `${x}px`;
            plantElement.style.marginTop = `${y}px`;
            
            plantElement.style.backgroundColor = plantState.color;
            
            (appendAt || document.body).appendChild(plantElement);
            
            plantElement.onclick = () => changeColorOnClick(plant, plantElement);
        }
    </script>
</body>

</html>

Plant.js:

window.Plant = class Plant {
    
    /**
     * @param {Object} state? Custom state [optional].
     */
    constructor(state) {
        this.state = state || { color: 'red', colorIndex: 0, waterable: true, watered: false };
    }
    
    /**
     * Changes the plant's state.
     * @param {Object} state Custom state.
     * @returns {Object}
     */
    setState(state) {
        this.state = (state ?? this.state) ?? { color: 'red', colorIndex: 0, waterable: true, watered: false };
        
        return this.state;
    }
    
    /**
     * Returns the plant's state
     * @returns {Object} 
     */
    getState() {
        return this.state ?? { color: 'red', colorIndex: 0, waterable: true, watered: false }
    }
    
    /**
     * Stringify the plant's state.
     * @returns {String}
     */
    toString() {
        return JSON.stringify(this.state);
    }
}

I have experimented with various solutions such as using transforms, negative y values, etc. Unfortunately, none of them provided the desired result...

If you have any suggestions on how to fix this issue, I would greatly appreciate it! 👍

Answer №1

It appears that your JavaScript code is correct, but the issue lies in the CSS styling causing the problem :)

To resolve this, ensure that you set the position to relative on the #plants div and position:absolute on the .plants class. Instead of using margins, use the top and left properties to define the coordinates.

Here's the explanation: when you use position: absolute, it removes the element from the normal flow of the page and positions it based on x/y coordinates relative to the nearest parent element with relative positioning. Therefore, to achieve the desired positioning of the plants, provide relative positioning to the container and position the plants absolutely within that container.

Apologies for not providing the full names of the position attributes earlier. You can refer to https://www.w3schools.com/css/css_positioning.asp for more information.

[edit: replaced my abbreviated html with the original entire thing.]

window.Plant = class Plant {
    
    /**
     * @param {Object} state? Custom state [optional].
     */
    constructor(state) {
        this.state = state || { color: 'red', colorIndex: 0, waterable: true, watered: false };
    }
    
    /**
     * Changes the plant's state.
     * @param {Object} state Custom state.
     * @returns {Object}
     */
    setState(state) {
        this.state = (state ?? this.state) ?? { color: 'red', colorIndex: 0, waterable: true, watered: false };
        
        return this.state;
    }
    
    /**
     * Returns the plant's state
     * @returns {Object} 
     */
    getState() {
        return this.state ?? { color: 'red', colorIndex: 0, waterable: true, watered: false }
    }
    
    /**
     * Stringify the plant's state.
     * @returns {String}
     */
    toString() {
        return JSON.stringify(this.state);
    }
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
    
    <style type="text/css">
        body {
            margin: 0;
            padding: 0;
            background-color: #27293F;
        }
        
        #title {
            width: 100vw;
            height: 20vh;
        }
        
        h1 {
            width: 100%;
            color: #4FFF5A;
            text-align: center;
        }
        
         p {
            width: 100vw;
            color: #379A3D;
            text-align: center;
        }
        
        #plants {
            position: relative;
            width: 100vw;
            height: 75vh;
            border: 10px solid #FF5555;
            display: flex;
            flex-wrap: wrap;
        }
    
        .plant {
            position: absolute;
            width: 50px;
            height: 50px;
        }
    </style>
    
    <script src="Plant.js" type="text/javascript" charset="utf-8"></script>
</head>

<body>
    <section id="title">
        <h1>The plant generator 🌱</h1>
        <p>click anywhere in the box to generate a plant</p>
    </section>
    
    <div id="plants">
        
    </div>

    <script type="text/javascript" charset="utf-8">
        const colors = ['red', 'blue', 'green'];
        
        const plantsContainer = document.getElementById('plants');
        
        plantsContainer.addEventListener('mousedown', function(event) {
            generatePlant(event.x, event.y, plantsContainer);
        });
    
        /**
         * Changes the plant's color on click.
         * @param   {Plant}            plant          Target to change the color.
         * @param   {HTMLDivElement}   plantElement   The plant element.
         * @returns undefined
         */
        function changeColorOnClick(plant, plantElement) {
            let index = plant.getState().colorIndex + 1;
            
            index >= colors.length ? index = 0 : null;
            
            plant.setState({ ...plant.getState(), color: colors[index], colorIndex: index });
            
            plantElement.style.backgroundColor = plant.getState().color;
        }
    
        /**
         * Generate a plant.
         * @param   {Number} x         The plant's x coordinate.
         * @param   {Number} y         The plant's y coordinate.
         * @param   {any}    appendAt? The element to append the plant at.
         * @returns {Plant}
         */
        function generatePlant(x, y, appendAt) {
            let plant = new Plant();
            
            x = x ?? Math.floor(Math.random() * window.innerWidth);
            y = y ?? Math.floor(Math.random() * window.innerHeight);
            
            generatePlantElement(plant, x, y);
            
            console.log(x, y);
            
            return plant;
        }
        
        /**
         * Generate a plant element.
         * @param {Plant}  plant     The plant.
         * @param {Number} x         The plant's x coordinate.
         * @param {Number} y         The plant's y coordinate.
         * @param {any}    appendAt? The element to append the plant element at [optional].
         */
        function generatePlantElement(plant, x, y, appendAt) {
            let plantElement = document.createElement("div");
            let plantState = plant.getState();
            
            plantElement.classList.add("plant");
            
            plantElement.style.left = `${x}px`;
            plantElement.style.top = `${y}px`;
            
            plantElement.style.backgroundColor = plantState.color;
            
            (appendAt || document.body).appendChild(plantElement);
            
            plantElement.onclick = () => changeColorOnClick(plant, plantElement);
        }
    </script>
</body>

</html>

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

The FireBase getToken function with the forceRefresh set to true has failed to perform as expected

I encountered a problem with this code snippet from Firebase when trying to run it in Angular 2 CLI. It gives an error of 'unreachable code'. How can I fix this issue and get it to work properly? firebase.auth().currentUser.getToken(/forceRefres ...

Navbar-toggle divider shade

I am having trouble changing the color of the line break on the Twitter Bootstrap 3 navbar-toggle function. Upon viewing this image, you can see that there is a line break using my background color. https://i.sstatic.net/qgVAm.jpg I have tried several o ...

I am currently working with a for loop within an object in JavaScript, but there seems to be a problem with one

Here is a function called validator: import validator from "validator"; import isEmpty from "is-empty"; export default function validate(data) { const errors = {}; for (const property in data) { console.log(property); //< ...

Compose a tweet using programming language for Twitter

How can I send a message to a Twitter user from my .NET application? Are there any APIs or JavaScript code that can help with this task? Any assistance would be greatly appreciated. ...

Adjusting the spacing between rows in Bootstrap 5

Having trouble with the spacing in my latest markup update. Here's what I have: <div class="row"> <div class="col-md-12"> <div class="form-group"> <label class="control-la ...

Automated Form Submission: A Guide to Automatically Sending the Form After Captcha Verification

I'm looking to incorporate ReCaptcha into my website in order to display additional data. My goal is to have the form automatically submitted once the ReCaptcha has been completed, eliminating the need for an extra submit button. However, I've en ...

The specified property cannot be found within the type 'JSX.IntrinsicElements'. TS2339

Out of the blue, my TypeScript is throwing an error every time I attempt to use header tags in my TSX files. The error message reads: Property 'h1' does not exist on type 'JSX.IntrinsicElements'. TS2339 It seems to accept all other ta ...

Material-UI: The `paperFullWidth` key passed to the classes prop is not supported within the WithStyles(ForwardRef(Dialog)) component

When using Material-UI, please note that the key paperFullWidth provided to the classes prop is not implemented in WithStyles(ForwardRef(Dialog)). You are only able to override one of the following: root. <Dialog classes={{ paperFullWid ...

What is the process for animating classes instead of ids in CSS?

My webpage currently features a setup similar to this. function wiggle(){ var parent = document.getElementById("wiggle"); var string = parent.innerHTML; parent.innerHTML = ""; string.split(""); var i = 0, length = string.length; for (i; i ...

When I navigate to the About page in Vue, the data is automatically cleared

Assist me, please! I am encountering an issue with my website. I have two main pages: Home and About, as well as a component called SecondPage. The SecondPage component contains two tables filled with data that should be displayed on the About page. Howeve ...

Using Vue.js - Incorporate filtering functionality within the v-for loop

I've successfully implemented a Vue filter that restricts the length of an array to n elements. It functions perfectly when used like this: {{ array | limitArray(2) }} Now, I'm attempting to utilize it within a v-for loop as follows: <li v- ...

How can I ensure that a button remains fixed at the bottom of a Material UI React Component?

I am currently working on developing a layout for a web application, and I have encountered an issue that I am struggling to resolve. The structure of my grid is as follows: https://i.sstatic.net/TEU2a.png My goal is to ensure that each section inside t ...

Turning spring form data into a JSON object via automation (with a mix of Spring, jQuery, AJAX, and JSON)

Recently, I've set up a spring form that utilizes ajax for submission. Here's an overview of my form... <form:form action="addToCart" method="POST" modelAttribute="cartProduct"> <form:input type="hidden" ...

The comparison between dynamically adding elements through Javascript and hiding them using CSS

I am contemplating the advantages and disadvantages of adding elements to a page and setting display:none versus creating a function that dynamically generates the elements and appends them where needed. In my current situation, I am implementing a reply ...

What could be causing the submission failure of the form post validation?

My Code: <form method="post" name="contact" id="frmContact" action="smail.php"> ... <label for="security" class="smallPercPadTop">Please enter the result:</label> <br /><h3 id="fNum" class="spnSecurity"></h3>& ...

Is it possible to send arguments to a debounced function using _.lodash?

Utilizing _lodash.debounce() has been a bit of a challenge for me. While I have managed to get it working, I can't help but feel that there might be a better way to implement it. The examples provided on the lodash website are quite basic and don&apos ...

Tips for setting up a full-size image with nextJS and the <Image /> component

Upgrading NextJS to the latest version has resulted in some errors when using the Image component: // import Image from 'next/image' <div style={Object.assign({}, styles.slide, style)} key={key}> <Image src={src} alt="&quo ...

The functionality of display flex is not functioning as expected outside of the CodePen

I attempted to create a responsive two-column layout on Codepen with success. The layout is quite simple, featuring a YouTube video and some text. However, when I tried to transfer this to my website, it failed to work... Here is the code – hoping someo ...

Navigating through HTML content and extracting specific elements

After using an ajax call to retrieve the partialView HTML of a page, I need to extract information from the main div before displaying it. This data specifically relates to the size information in order to create a floating window. Here is the code snippe ...