Preamble: Searches are based on Diego answer about dynamic field JS creation and Anthony Awuley answer about MutationObserver of the created fields
I spent a lot of time (searching and) finding a solution that suits my needs but the result - even if it is functional - seems a bit heavy to me.
I want to create a dynamic form based on certain informations transmitted to it in order to create shipments that will be sent by a webservice to the carrier. This informations include among others a weight limit which must not be exceeded when creating the packages. The number of these packages is indicated by the user in a dedicated field and as many fields as necessary are dynamically created.
I previously tried to listen to the changes in the "weight" fields and to calculate the total of the weights as they are entered, but given that when the page loads, the fields are not yet created, there is nothing to listen to.
2 questions then:
- Is it mandatory to use MutationObserver in my case
- Is it "normal" to need almost 40 lines for this and therefore, is there an easier way to achieve my goal.
To lighten my demonstration, I reduced the number of fields created.
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 e 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);
});
//removes the weights in the given multiparcel
function clearweights(from) {
const target = from.closest('.multiparcel').querySelector('.parcels');
target.innerHTML = '';
}
//adds a number of weights in the given multiparcel
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");
// Options for the observer (which mutations to observe)
const config = { childList: true, subtree: true };
// Callback function to execute when mutations are observed
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');
// overWeightAlert.style.display = 'block';
} else {
overWeightAlert.classList.add('d-none');
submitButton.classList.remove('d-none');
submitButton.classList.add('d-block');
}
});
})
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// // Start observing the target node for configured mutations
observer.observe(targetNode, config)
// ---------------------------- END DOM CHANGES DETECTION
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>