In a bespoke shopping cart, I pull from my database a list of options that a particular product has. The number of these options can vary by product. I then turn that list of options into a JavaScript array.
An example with 3 options:
{"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}}
Using the above, I would now like to generate an HTML select field, listing all the "first" options:
<select>
<option value="Small">Small</option>
<option value="Large">Large</option>
<option value="X Large">X Large</option>
</select>
Once the user has selected the first option, I then need another <select>
box to then load with options that are available for their selection. So, if they selected "Small" from above, the new select box would be:
<select>
<option value="Super">Super</option>
<option value="Maxi">Maxi</option>
</select>
Finally, when they select from this list, a 3rd select box loads in the final options, along with the part numbers as values:
<select>
<option value="parta">Pack</option>
<option value="parte">Case</option>
</select>
The number of options can vary, from zero to 4. But, each time when options are available, I need to pull the part number based on the users selection. The part number doesn't necessarily need to be the value of the last select, it can be pushed to a new hidden variable.
I can achieve this using ajax, by making an ajax call every time a selection is chosen, but can it be done via JavaScript / jQuery, without having to make ajax calls, given that the array is on the page and available to use?
CodePudding user response:
When you dynamically create the select
element, also determine which "node" in your tree structure goes with that element, and use it to:
- populate the
select
element with options - determine the deeper node when an option is selected (also when the initial selection is made when the element is created), and use it to create the next
select
element with the same function - This cascade stops when the deeper node happens to be a string and not an object.
Here is some code for inspiration:
let optionTree = {"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}};
let container = document.querySelector("#container");
addSelector(optionTree);
function addSelector(node) {
let select = document.createElement("select");
for (let key in node) { // Populate the select element
let option = document.createElement("option");
option.value = key;
option.text = key;
select.add(option);
}
container.appendChild(select); // Add it to the page
function change() {
// Remove select elements that come after the selection
while (select.nextElementSibling) {
select.nextElementSibling.remove();
}
let key = select.value;
if (typeof node[key] !== "string") {
addSelector(node[key]); // Create the next select element(s)
}
}
// Call the above function whenever a selection is made
select.addEventListener("change", change);
change(); // ... and also call it now
}
<div id="container"></div>
CodePudding user response:
Assuming you already have the three select elements in the DOM, you can populate the first select, and add a change event listener to both the first and second to achieve this. Try this
let $s1 = document.querySelector('#select-1');
let $s2 = document.querySelector('#select-2');
let $s3 = document.querySelector('#select-3');
let object = {"Small":{"Super":{"Pack":"parta","Case":"parte"},"Maxi":{"Pack":"partb","Case":"partf"}},"Large":{"Super":{"Pack":"partc","Case":"partg"}},"X Large":{"Maxi":{"Pack":"partd"}}};
// add options to first select
$s1.innerHTML = '<option></option>'; // empty select
Object.keys(object).forEach(val => $s1.append(new Option(val, val))); // append children
$s1.dispatchEvent(new Event('change')); // trigger change event
// listen to change event on first select/get options for second select
$s1.addEventListener('change', function(e){
$s2.innerHTML = '<option></option>'; // empty select
// append children
Object.keys(object[e.target.value] ?? []).forEach(val => {
$s2.appendChild(new Option(val, val));
});
// trigger change event
$s2.dispatchEvent(new Event('change'));
});
// listen to change event on second select/get options for third select
$s2.addEventListener('change', function(e){
$s3.innerHTML = '<option></option>'; // empty select
// append children
Object.entries(object[$s1.value]?.[e.target.value] ?? []).forEach(([key, val]) => {
$s3.appendChild(new Option(key, val));
});
});
select {min-width:80px}
<select id="select-1"></select>
<select id="select-2"></select>
<select id="select-3"></select>