Verifying dynamic number inputs generated using JavaScript values and calculating the total with a MutationObserver

Preamble: I've referenced Diego's answer on dynamic field JS creation and Anthony Awuley's answer on MutationObserver for the created fields.

After extensive searching, I found a solution that meets my needs, but it feels somewhat bulky despite its functionality.

I aim to design a flexible form that can generate shipments based on specific input data received. These details include criteria such as a weight limit that must not be exceeded when packaging items. Users specify the number of packages in a dedicated field, and dynamically create as many fields as required.

In previous attempts, I tried monitoring changes in the 'weight' fields to calculate the total weight entered. However, since the fields are not yet created when the page loads, there was nothing to monitor.

My queries are:

  • Is using MutationObserver necessary in this scenario?
  • Is it normal to require nearly 40 lines of code for this operation, or is there a simpler method to achieve the same result?

To streamline my demonstration, I have reduced the number of generated fields.

HTML

<div id="total-order-weight">20</div>
    <div id="over-weight-alert" style="display:none; color:red;">OVER WEIGHT</div>
    <form action="#" id="parcel-form" method="post">
        <div class="multiparcel">
            <input type="number" class="qty" min="0">
            <div class="parcels"></div>
        </div>
        <button id="submit" class="btn btn-success mt-3" type="submit">Go</button>

    </form>
</div>

JS FIELDS CREATION

<script>
        overWeightAlert = document.querySelector('#over-weight-alert');
        totalOrderWeight = parseInt(document.querySelector('#total-order-weight').innerHTML);
        parcelForm = document.querySelector('#parcel-form');
        //add input event listener to qty input
        qtyEl = document.querySelector('.qty')
        qtyEl.addEventListener('input', (e) => {
            const qtyEl = e.target;
            const qty = e.target.value;
            clearweights(qtyEl);
            addweights(qtyEl, qty);
        });

        function clearweights(from) {
            const target = from.closest('.multiparcel').querySelector('.parcels');
            target.innerHTML = '';
        }

        function addweights(from, n) {
            const target = from.closest('.multiparcel').querySelector('.parcels');
            for (let i = 0; i < n; i++) {
                group = createGroup()
                const weight = createweights();
                const insurance = createInsurance();
                group.append(weight);
                group.append(insurance);
                target.append(group);
            }
        }

        function createGroup() {
            const group = document.createElement('div');
            group.classList.add('input-group', 'my-2');
            return group;
        }

        function createweights(i) {
        const label = document.createElement("Label");
        label.htmlFor = "weight" + i;
        label.innerHTML = "Poids";
        label.classList.add('form-label');
        const input = document.createElement('input');
        input.name = 'weight' + i;
        input.type = "number";
        input.classList.add('form-control', 'me-4');
        const weight = document.createElement('div');
        weight.classList.add('weight');
        weight.append(label);
        weight.append(input);
        return weight;
    }

    function createInsurance(i) {
        const label = document.createElement("Label");
        label.htmlFor = "insurance"+i;
        label.innerHTML = "Assurance";
        label.classList.add('form-label');
        const input = document.createElement('input');
        input.name = 'insurance' + i
        input.type = "number";
        input.classList.add('form-control', 'ms-4');
        input.value = 0;
        const insurance = document.createElement('div');
        insurance.classList.add('insurance');
        insurance.append(label);
        insurance.append(input);
        return insurance;
    }
    </script>

JS MUTATIONOBSERVER

<script>
    const targetNode = document.getElementById("parcel-form");

    const config = { childList: true, subtree: true };
    const callback = (mutationList, observer) => {
        for (const mutation of mutationList) {
            if (mutation.type === "childList") {
                overWeightAlert.style.display = 'none';
                weights = parcelForm.querySelectorAll('input[name="parcel-weight"]');
                weights.forEach(weight => {
                    weight.addEventListener('keyup', (event) => {
                        var check = checkWeight();
                        if (check > totalOrderWeight) {
                            overWeightAlert.classList.add('d-block', 'alert', 'alert-danger');
                            overWeightAlert.classList.remove('d-none');
                            submitButton.classList.add('d-none');
                        } else {
                            overWeightAlert.classList.add('d-none');
                            submitButton.classList.remove('d-none');
                            submitButton.classList.add('d-block');
                        }
                    });
                })
            }
        }
    };
    const observer = new MutationObserver(callback);
    observer.observe(targetNode, config)

    function checkWeight() {
        weights = parcelForm.querySelectorAll('input[name="parcel-weight"]');
        var totalWeight = 0;
        for (var i = 0; i < weights.length; i++) {
            qty = weights[i].value;
            if (qty) {
                totalWeight += parseInt(qty);
            }
        }
        return totalWeight;
    }
</script>

Answer №1

To efficiently handle the input, consider utilizing event delegation. Here is a concise demonstration showcasing this technique: minimal reproducible example. The total value will now be computed only upon the addition of a new input.

If the use of MutationObserver is indispensable, you can explore a working example on Stackblitz project.

document.addEventListener(`click`, handle);
const maxWeight = 100;

function total() {
  const values = document.querySelectorAll("[data-value]");
  if (values.length) { 
    const total = [...values]
      .reduce( (acc, el) => acc + +el.dataset.value, 0);
    const totalElem = document.querySelector("#total")
    totalElem.innerHTML = `TOTAL: <b>${total}</b>`;
    
    if (total > maxWeight) {
      document.querySelector("#add").setAttribute("disabled", "disabled");
      totalElem.innerHTML += ` <b class="warn"><i>Overweight!</i></b>`;
    }
  }
}

function handle(evt) {
  if (evt.target.id === "add") {
    const inp = document.querySelector("#weight");
    const newElem = Object.assign(document.createElement("div"), 
      {textContent: +inp.value ?? "0"});
    document.querySelector("#weights").appendChild(newElem);
    newElem.dataset.value = inp.value ?? "0";
    inp.value = 0;
    total();
  }  
}
#total {
  margin-top: 0.6rem;
  width: 125px;
}

.warn {
  color: red;
}
<div class="input">
<input type="number" id="weight" value="0">
<button id="add">add weight</button>
</div>

<div id="weights">
</div>

<div id="total">
</div>

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

I need help figuring out how to send a POST/GET request from AJAX to a custom module controller in Odoo 10, but I'm running into issues

I have implemented a custom module in Odoo 10 with a simple controller. Everything works smoothly when accessing http://127.0.0.1:8069/cmodule/cmodule through the browser, displaying the expected return string. However, I encountered an issue when attempt ...

Using data across multiple instances in node.js EJS partials

I need some advice regarding my nodejs project. I have created a sidebar partial that requires user data to be displayed on it. This sidebar is utilized across multiple pages in the project. However, currently, I have to include the user data in every func ...

Utilizing ES6 promises in node.js to send a null response

I'm looking for assistance on how to execute a query using ES6 native promises in node.js. The code I have below is what I've been working with: let arr= []; conn.query('select * from table1', (err, b) => { for (let i = 0; i ...

Creating a standalone executable for Node.js without including the entire Node.js framework

Trying to pack a simple hello world script into an executable has led to some challenges. Both pkg and nexe seem to include the entirety of Node.js in the output file, resulting in quite larger files than necessary (around 30 MB). Although EncloseJS was fo ...

ExpressJS and cookies - Struggling to initialize a cookie with express-cookie

I recently followed a tutorial on YouTube (https://youtu.be/hNinO6-bDVM?t=2m33s) that demonstrated how to display a cookie in the developer tools Application tab by adding the following code snippet to the app.js file: var session = require('express- ...

Getting data from an API with authorization in Next.js using axios - a step-by-step guide

click here to view the image user = Cookies.get("user") I'm having trouble accessing this pathway. I stored user data, including the token, using cookies. Please assist me with this issue. ...

Non-reactive arrays in Vue.js

I am facing an issue. Here is the problem: data: { tracks: [] } The tracks array will contain a complex object. I want to achieve reactivity when assigning a new value to tracks nested object, but I do not need deep reactivity for the object. ...

Is there a CSS3 Selector With Similar Functionality to jQuery's .click()?

For a few years now, I have been utilizing a pure CSS navigation system. However, with the recent increase in mobile site projects at my workplace, I am encountering issues with drop-down menus not functioning properly on mobile devices. Despite this chall ...

Troubleshooting MongoDB and Node.js: Issue with updating variables when inserting documents in a loop

As a newcomer to MongoDB, I'm facing a puzzling issue that has left me confused. In my dataset, I have an array of Employee objects structured like this: { "Name" : "Jack Jackson", "Title" : "Senior Derp Engineer", "Specialties" : [ "Kicki ...

JavaScript's addition function is not functioning properly

Does anyone know how to calculate the sum of these variables? var totalPrice = document.values.T1.value; var pounds = document.values.T2.value; var serviceFee = 5.00 var counter1 = 0; var counter2 = 0; var processing = 0; var shippingCost = 0; var ...

The SDK generated by AWS API Gateway does not include the JavaScript client file

After setting up my API with the AWS Api Gateway Service, I am now looking to integrate the javascript SDK into a basic webpage. The provided instructions for using the javascript SDK can be found here. However, they mention importing a js file named apig ...

The NextJs image entered into an endless loop, throwing an error message that said: "The 'url' parameter is correct, but the response from the

I have been using next/image component with next js version ^12.2.3-canary.17 for my current project. The issue I am encountering is that some images are missing from the source directory, resulting in infinite error logs like the one shown below: https:/ ...

Updating Jqplot display upon ajax call completion

Currently, I have a Jqplot set up using the AJAX JSON Data Renderer and it's functioning properly. There is a button on the page where users can input new values (updated through ajax) which are then stored in the same DB as the json data source. Whe ...

Using JQuery, identify cells located in the first column of a table excluding those in the header section

In the past, I had code that looked like this: $(elem).parents('li').find(...) I used this code when elem was an item in a list, making it easy to reference all items in the list. But now, I've made some changes and decided to use a table ...

Creating a Consistent Look for Italic Font Formatting in Tailwind

In an effort to establish consistent typography on a website being developed by my team, we have devised custom utility classes in Tailwind. One of these classes, small-italicized, also requires the text to be italicized. However, it seems that the fontSiz ...

Transform a log file into a JSON structure

In my log file titled request.log, the following entries are present: [2022-06-30T09:56:40.146Z] ### POST https://test.csdf/auth/send_otp { "method": "POST", "headers": { "User-Agent": "testing&q ...

Access Flask variable in JavaScript code

Currently working on a CTF challenge, my query is not seeking assistance in solving it, but rather pertains to syntax. The task involves retrieving the secret key from Flask server's configuration. This key is stored within the app.secret_key variable ...

Issues with ng-show functionality occurring during the initialization of the webpage

While working on a webpage using HTML, CSS, and Angular.js, I encountered an issue where the page content would not display properly upon loading. The objective was to show selected content based on user choices from a dropdown menu. Although the filtering ...

What methods are available in JavaScript regex for validating city names?

var cityRegex = /^[a-zA-z] ?([a-zA-z]|[a-zA-z] )*[a-zA-z]$/; is the regular expression I attempted to create. However, it fails when inputting a city name like "St. Petersburg." Update: It seems challenging to create a perfect regex pattern for city name ...

Testing a directive that has templates all contained within a single file with script tags

I'm struggling to include my directive's templates in my Karma unit tests. The templates are all in one file within different script tags. The error message I encounter is: PhantomJS 1.9 (Linux) ERROR SyntaxError: Parse error at /var/www/html ...