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>