Disable options using JS - javascript

I need help with my code here. I cannot figure out how I can disable the options I have if they have "N/A" value.
Here's a piece of my code.
var priceByCategoryPL = {
// Apple Price
A: [{
name: "iPhone 4",
onee: "$89.00",
twoo: "$99.00",
thre: "N/A",
four: "N/A",
five: "N/A",
sixx: "N/A"
}]
}
function updateStorage()
{
let drpOptionsStorage = "<option value='' disabled selected>Select Storage</option>";
for (let key in storageByCategoryPL) {
if (storageByCategoryPL.hasOwnProperty(key) && selectedValue == key) {
let drpValues = storageByCategoryPL[key];
if (drpValues.length > 0) {
for (let storageInfo in drpValues) {
if (Object.keys(drpValues[storageInfo]).length > 0) {
for (let option in drpValues[storageInfo]) {
drpOptionsStorage += "<option value="+option+">" + drpValues[storageInfo][option] + "</option>";
}
}
}
}
break;
}
}
if (drpOptionsStorage.length > 0) {
storageDrp.innerHTML = drpOptionsStorage;
}
}
So basically, what this does is it selects the brand, model, storage and then shows the price. But in this scenario, I just want to disable the storage if the price is not applicable.
If anyone can help me clear it up, it would help me a lot with my proj. Thanks guys.
https://codepen.io/Foxseiz/pen/KKpmywz

You need to remove the storage option for the respective prices are "N/A".
Changes you need to make,
Inside updateStorage() function, inside for (let option in drpValues[storageInfo]){...}, you need to include a filter method like, and include the options inside this filter method..
priceByCategoryPL.S.filter(x => {
if(x[option] && x[option] != "N/A"){
drpOptionsStorage += "<option value="+option+">" + drpValues[storageInfo][option] + "</option>";
}
});
Working Snippet as follows:
// Model Numbers
var brandByCategoryPL = {
// Samsung
S: [{
name: "Galaxy A20"
}]
}
var storageByCategoryPL = {
S: [{
onee: "16GB",
twoo: "32GB",
thre: "64GB",
four: "128GB",
five: "256GB",
sixx: "512GB",
sevn: "1TB"
}]
}
var priceByCategoryPL = {
// Samsung Price
S: [{
name: "Galaxy A20",
onee: "N/A",
twoo: "$279.00",
thre: "N/A",
four: "N/A",
five: "N/A",
sixx: "N/A"
}]
}
var brandDrp = document.getElementById("cpodevicelist"),
catDrp = document.getElementById("cpocategorylist"),
storageDrp = document.getElementById("cpostoragelist"),
priceText = document.getElementById("cpopricelist"),
selectedValue = "";
function updateDrp(element, type)
{
if (element.length > 0) {
selectedValue = element.value;
// console.log(selectedValue);
if (type == 'brand') {
/* Call for model. */
updateModel();
/* Call for storage. */
updateStorage();
/* Call for price. */
updatePrice();
} else if (type == 'cat') {
/* Append price. */
updatePrice();
} else if (type == 'storage') {
/* Append price. */
updatePrice();
}
}
}
function updateModel()
{
let drpOptionsModel = "<option value='' disabled selected>Select Model</option>";
for (let key in brandByCategoryPL) {
if (brandByCategoryPL.hasOwnProperty(key) && selectedValue == key) {
let drpValues = brandByCategoryPL[key];
if (drpValues.length > 0) {
for (let option in drpValues) {
drpOptionsModel += "<option value="+option+">" + drpValues[option]["name"] + "</option>";
}
}
break;
}
}
if (drpOptionsModel.length > 0) {
catDrp.innerHTML = drpOptionsModel;
}
}
function updateStorage()
{
let drpOptionsStorage = "<option value='' disabled selected>Select Storage</option>";
for (let key in storageByCategoryPL) {
if (storageByCategoryPL.hasOwnProperty(key) && selectedValue == key) {
let drpValues = storageByCategoryPL[key];
// priceByCategoryPL.S.findIndex(x => {
// console.log(key)
// });
if (drpValues.length > 0) {
for (let storageInfo in drpValues) {
if (Object.keys(drpValues[storageInfo]).length > 0) {
for (let option in drpValues[storageInfo]) {
priceByCategoryPL.S.filter(x => {
if(x[option] && x[option] != "N/A"){
drpOptionsStorage += "<option value="+option+">" + drpValues[storageInfo][option] + "</option>";
}
});
}
}
}
}
break;
}
}
if (drpOptionsStorage.length > 0) {
storageDrp.innerHTML = drpOptionsStorage;
}
}
function updatePrice()
{
priceText.value = "";
if (Object.keys(priceByCategoryPL[brandDrp.value]).length > 0) {
for (let modelKey in priceByCategoryPL[brandDrp.value]) {
if (catDrp.value == modelKey) {
for (let storageKey in priceByCategoryPL[brandDrp.value][modelKey]) {
if (storageDrp.value == storageKey) {
priceText.value = priceByCategoryPL[brandDrp.value][modelKey][storageKey];
}
}
}
}
}
}
<div class="content-label inputIconBg">
<select class="content-input" name="cpodevicelist" id="cpodevicelist" style="width:350px;" onchange="updateDrp(this, 'brand');">
<option value="" disabled selected>Select Brand</option>
<option value="S">Samsung</option>
</select>
</div>
<div class="content-label inputIconBg" id="msrpcat">
<select class="content-input" name="cpocategorylist" id="cpocategorylist" style="width:350px;" onchange="updateDrp(this, 'cat');">
<option value="" disabled selected>Select Model</option>
</select>
</div>
<div class="content-label inputIconBg" id="msrpstorage">
<select class="content-input" name="storage" id="cpostoragelist" style="width:350px;" onchange="updateDrp(this.value, 'storage');">
<option value="" disabled selected>Select Storage</option>
</select>
</div>
<div class="content-label inputIconBg" id="msrpprice">
<input class="content-input" type="text" id="cpopricelist" placeholder="CPO Price" style="width:350px;" readonly />
<i class="fas fa-dollar-sign" id="msrpdollar" data-toggle="tooltip" title="Cost" data-placement="left"></i>
</div>
Working codepen here...

I'm not sure if this is what you want, this prevents creating an <option> element when it encounters "N/A" value.
for (let option in drpValues[storageInfo]) {
if (drpValues[storageInfo][option] !== "N/A") {
drpOptionsStorage += "<option value="+option+">" + drpValues[storageInfo][option] + "</option>";
}
}
Let me know if this is not it.
Anyway, using insertHTML on everything might be tempting because it's less typing but I'd recommend appendChild() instead. Mostly because it's a way cleaner and more maintainable approach, but also better for performance. Secondly if I was you I'd seriously rethink the way you're structuring your data, i.e here:
var priceByCategoryPL = {
A: [{
name: "iPhone 4",
onee: "$89.00",
twoo: "$99.00",
thre: "N/A",
four: "N/A",
five: "N/A",
sixx: "N/A"
}]
}
You're treating every price as property when in the "real world" price is a product's property. Here's example:
var priceByCategoryPL = {
A: [{
name: "iPhone 4",
prices: ["$89.00", "$99.00", "N/A", "N/A", "N/A", "N/A"]
}]
}
But from what I've seen there are more things that I'd consider as design flaws that may kick back if your project grows, so actually reworking only that part makes little to no sense at all in my opinion.

I believe it's as simple as adding an else:
if (drpOptionsStorage.length > 0) {
storageDrp.innerHTML = drpOptionsStorage;
}
else {
storageDrp.disabled = true;
}
Add the disabled attribute to the <select> element if there are no options.
Given that you have 1 default option, though, and it's stored as a string, I don't think .length is acting the way you expect. It's counting characters in the string, not options in an array. So you should probably change the options to an array of strings and join('') them immediately before adding them to the select.

Related

Update output displayed json data when changing select value dropdown

I basically want to dynamically update the displayed output json fields based on what value I select in a dropdown, without reloading the page. In this case, display the city based on the selected AveragePrice dropdown. By the way, it's plain javascript (Vanilla)
JSON:
[
{
"country":"UK",
"cities":[
{
"city":"Manchester",
"averagePrice":100
},
{
"city":"London",
"averagePrice":300
},
]
},
{
"country":"France",
"cities":[
{
"city":"Toulouse",
"averagePrice":150
},
{
"city":"Nice",
"averagePrice":200
},
]
},
]
HTML:
<div id="city"></div>
<select id="averagePriceDropdown">
<option value="100">100</option>
<option value="150">150</option>
<option value="175">175</option>
<option value="200">200</option>
<option value="300">300</option>
</select>
Javascript:
var valueDropdown = document.getElementById("averagePriceDropdown");
var valueData = valueDropdown.options[valueDropdown.selectedIndex].value;
var getAverage = async function () {
fetch("../data/data.json").then(
res => {
res.json().then(
data => {
for (var x in data) {
if (data[x].cities[x].averagePrice == valueData) {
return document.getElementById("city").innerHTML += data[x].cities[x].city;
} else {
return document.getElementById("city").innerHTML += "Could not find city.";
}
}
}
)
}
)
};
As you can see, I'm defining variable names for different ID fields. In the IF condition, I am updating the city text content (innerHTML) matching the AveragePrice. It works if I change the default dropdown value in the HTML and reload the page, it renders the correct city. But changing the dropdown on the page, it doesn't update the city. I tried adding an onchange eventlistener to the dropdown but with no success.
The issue is that you are not referencing the array elements correctly. Here is how you want to cycle through the data and make the comparison:
function choose(){
var valueDropdown = document.getElementById("averagePriceDropdown");
var valueData = valueDropdown.options[valueDropdown.selectedIndex].value;
var i, j, match;
match = false;
for (i = 0; i< data.length; i++) {
for (j = 0; j < data[i].cities.length; j++){
if (data[i].cities[j].averagePrice == valueData){
match = true;
console.log("Matching cities: " + data[i].cities[j].city);
document.getElementById("city").innerHTML = data[i].cities[j].city
}
}
}
if (match == false) {
console.log("No matching cities for average price = " + valueData);
document.getElementById("city").innerHTML = "No matching cities.";
}
}
data =
[
{
"country":"UK",
"cities":[
{
"city":"Manchester",
"averagePrice":100
},
{
"city":"London",
"averagePrice":300
},
]
},
{
"country":"France",
"cities":[
{
"city":"Toulouse",
"averagePrice":150
},
{
"city":"Nice",
"averagePrice":200
},
]
},
]
<div id="city"></div>
<select id="averagePriceDropdown" onchange="choose()">
<option value="-">-</option>
<option value="100">100</option>
<option value="150">150</option>
<option value="175">175</option>
<option value="200">200</option>
<option value="300">300</option>
</select>

Cascading Dropdowns using Javascript

I am trying to implement a cascading drop down using this. I'm not really familiar with Javascript but I think I'm on the right track. It should display the array values [0, 1, 2, 3] as opposed to the values ["Name", "Definition", "Owner"]
var typeObject = {
StoredProcedures: ["Name", "Definition", "Owner"],
Tables: ["Name", "Definition", "Owner", "Schema"],
Views: ["Name", "Definition", "Owner"]
}
window.onload = function() {
var typeSel = document.getElementById("typeSel"),
fieldSel = document.getElementById("fieldSel")
for (var type in typeObject) {
typeSel.options[typeSel.options.length] = new Option(type, type);
}
typeSel.onchange = function() {
fieldSel.length = 1; // remove all options bar first
if (this.selectedIndex < 1) return; // done
for (var field in typeObject[this.value]) {
fieldSel.options[fieldSel.options.length] = new Option(field, field);
}
}
typeSel.onchange();
I made a modification to your script
var typeObject = {
StoredProcedures: ["Name", "Definition", "Owner"],
Tables: ["Name", "Definition", "Owner", "Schema"],
Views: ["Name", "Definition", "Owner"]
}
window.onload = function () {
var typeSel = document.getElementById("typeSel"),
fieldSel = document.getElementById("fieldSel")
for (var type in typeObject) {
typeSel.options[typeSel.options.length] = new Option(type, type);
}
typeSel.onchange = function () {
fieldSel.length = 1; // remove all options bar first
if (this.selectedIndex < 1) return; // done
var ft = typeObject[this.value];
for (var field in typeObject[this.value]) {
fieldSel.options[fieldSel.options.length] = new Option(ft[field], field);
}
}
typeSel.onchange();
}
<select name="optone" id="typeSel" size="1">
<option value="" selected="selected">Select type</option>
</select>
<br/>
<br/>
<select name="opttwo" id="fieldSel" size="1">
<option value="" selected="selected">Select field</option>
</select>

Can't populate multiple dropdowns with JSON data, no errors

I have a JSON file with the information of watches. I want to build a simple form that allows a user to select a brand of watch, then the second dropdown would be populated with the values within "Model" and the final dropdown would be populated with the values within "Movement".
I've built what I assume to be right only it isn't working and I'm getting no errors?
HTML
<form name="myform" id="myForm">
<select name="optone" id="brands" size="1">
<option value="" selected="selected">Select a brand</option>
</select>
<br>
<br>
<select name="opttwo" id="model" size="1">
<option value="" selected="selected">Please select model</option>
</select>
<br>
<br>
<select name="optthree" id="movement" size="1">
<option value="" selected="selected">Please select a movement</option>
</select>
</form>
HTML
var watches = {
"Rolex": {
"Model": [
"Submariner",
"Yachtmaster",
"Oyster",
"Datejust"
],
"Movement": [
{
"label": "OysterDate",
"Id": "6694"
},
{
"label": "Hulk",
"Id": "3920"
},
{
"label": "DeepSea",
"Id": "2342"
}
]
},
"Omega": {
"Model": [
"Seamaster",
"Speedmaster",
"MoonWatch"
],
"Movement": [
]
}
}
window.onload = function () {
var brands = document.getElementById("brands"),
model = document.getElementById("model"),
movement = document.getElementById("movement");
for (var brand in watches) {
brands.options[brands.options.length] = new Option(brands, brands);
}
brands.onchange = function () {
model.length = 1; // remove all options bar first
testCase.length = 1; // remove all options bar first
if (this.selectedIndex < 1) return; // done
for (var model in watches[this.value]) {
model.options[model.options.length] = new Option(model, model);
}
}
brands.onchange(); // reset in case page is reloaded
model.onchange = function () {
movement.length = 1; // remove all options bar first
if (this.selectedIndex < 1) return; // done
var movement = watches[brand.value][this.value];
alert(movement);
for (var i = 0; i < movement.length; i++) {
movement.options[movement.options.length] = new Option(movement, movement);
}
}
}
watches();
https://jsfiddle.net/z3xcyprt/3/
Ok, there were a lot of issues here.
Mainly over writing your variable names, but also incorrect navigation of array values, using the for( x in obj) when you should use forEach(func())
Also note that you JSON does not have a relationship between Model and Movement I noted this in the script, but you will likely want to look at that.
var watches = {
"Rolex": {
"Model": [
"Submariner",
"Yachtmaster",
"Oyster",
"Datejust"
],
"Movement": [
{
"label": "OysterDate",
"Id": "6694"
},
{
"label": "Hulk",
"Id": "3920"
},
{
"label": "DeepSea",
"Id": "2342"
}
]
},
"Omega": {
"Model": [
"Seamaster",
"Speedmaster",
"MoonWatch"
],
"Movement": [
]
}
}
const createOption = (value, text) => {
let opt = document.createElement('option');
opt.value = value;
opt.text = text;
return opt;
};
window.onload = function () {
var brands = document.getElementById("brands"),
model = document.getElementById("model"),
movement = document.getElementById("movement");
for (var brand in watches) {
brands.options.add(createOption(brand, brand));
}
brands.onchange = function () {
model.length = 1; // remove all options bar first
if (brands.selectedIndex < 1) return; // done
// This is an array of string.
watches[brands.value].Model.forEach(m => {
model.add( createOption(m, m));
});
}
// There is NO link in the JSON between model and movement ?
model.onchange = function () {
movement.length = 1; // remove all options bar first
if (this.selectedIndex < 1) return; // done
watches[brands.value].Movement.forEach(m => {
movement.options.add(createOption(m.Id, m.label));
});
}
}
// This does nothing.
//watches();
<form name="myform" id="myForm">
<select name="optone" id="brands">
<option value="" selected="selected">Select a brand</option>
</select>
<br>
<br>
<select name="opttwo" id="model">
<option value="" selected="selected">Please select model</option>
</select>
<br>
<br>
<select name="optthree" id="movement">
<option value="" selected="selected">Please select a movement</option>
</select>
</form>
Try with this.
On window.onload function you are declaring the brands, model but you are overriding it in the loops.
Also read about the differences between for ... of and for ... in
window.onload = function () {
var brands = document.getElementById("brands"),
model = document.getElementById("model"),
movement = document.getElementById("movement");
for (var brand in watches) {
brands.options[brands.options.length] = new Option(brand);
}
brands.onchange = function () {
if (this.selectedIndex < 1) return; // done
for (var modelValue of watches[this.value].Model) {
model.options[model.options.length] = new Option(modelValue);
}
}
brands.onchange(); // reset in case page is reloaded
model.onchange = function () {
if (this.selectedIndex < 1) return; // done
var movementValue = watches[brands.value].Movement;
for (var i = 0; i < movementValue.length; i++) {
movement.options[movement.options.length] = new Option( movementValue[i].label, movementValue[i].Id);
}
}
}
watches();

Updating dropdown based on previous dropdown selection

I have an ecommerce site that has products with multiple attributes (e.g. size, colour, etc,.)
On each product page there is a dropdown for each attribute with a class of 'attribute_price'.
I have also preloaded hidden inputs onto the page from my database with the pricing for each product with a class of 'hidden_attribute_value'.
So not every combination of size and colour is an option. For example, we might have 'small_red' or 'medium_red' but no 'large_red'
So if they select 'large' from the size dropdown menu 'red' should not be an option for the colour.
What I have so far is:
$("select.attribute_price").on("change", function(){
var id = event.target.id;
// determine which dropdown was changed (size or colour)
var attribute_value = document.getElementById(id).value+'_';
// get the value of the dropdown that they selected
var other_attribute_ids = []
var i;
var other_attributes = document.getElementsByClassName("attribute_price");
for(i=0; i<other_attributes.length; i++){
if(other_attributes[i].id != id){
var other_attribute_id = document.getElementById(other_attributes[i].id).id;
other_attribute_ids.push(other_attribute_id);
}
}
// create an array of all of the other dropdown ids excluding the one they changed
var all_attribute_ids = []
var i;
var all_attributes = document.getElementsByClassName("hidden_attribute_value");
for(i=0; i<all_attributes.length; i++){
all_attribute_ids.push(all_attributes[i].id);
}
// create an array of all of the possible values that it can be
});
So I have a variable 'attribute_value' which would be something like 'red_' or 'blue_'.
I have an array called 'all_attribute_values' which has the ids of hidden inputs for all possible combinations. These would have values like 'small_red_' or 'small_blue'.
And I have an array called 'other_attribute_ids' which has the id of the other dropdown menus that haven't been selected.
So if an item in 'all_attribute_values' does not contain 'attribute_value' remove that option from 'other_attribute_ids'.
I have created a sample html based on your usecase. Solution is likewise, but you should get inspiration for solving yours.
I have considered independent attributes, so the solution will scale to new attributes with different values. I have also considered that server response is not editable.
I have a quick link in jsfiddle to checkout the solution
https://jsfiddle.net/nfLx6aok/1/
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<select id="size" class="attribute_price">
<option value="small">Small</option>
<option value="large">Large</option>
</select>
<select id="color" class="attribute_price">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="black">Black</option>
</select>
<select id="pattern" class="attribute_price">
<option value="solids">Solids</option>
<option value="checked">Checked</option>
<option value="dots">Dots</option>
</select>
<input type="hidden" id="small_red_solids" class="hidden_attribute_value">
<input type="hidden" id="small_black_dots" class="hidden_attribute_value">
<input type="hidden" id="large_green_checked" class="hidden_attribute_value">
<script>
// on page load
$( document ).ready(function() {
renderOptions();
});
$("select.attribute_price").on("change", function () {
renderOptions();
});
function renderOptions() {
// create an array of all of the possible values that it can be
// allowed_attribute_values = ['small_red', 'large_blue']
var allowed_attribute_values = [];
var all_attributes = document.getElementsByClassName("hidden_attribute_value");
for (var i = 0; i < all_attributes.length; i++) {
allowed_attribute_values.push(all_attributes[i].id);
}
function getAllPossibleValues(current_level, all_attributes) {
var depth_index = all_attributes.length;
var selected_combination = '';
for (var i = 0; i < depth_index; i++) {
if (i <= current_level) {
selected_combination += all_attributes[i].value;
if (i != all_attributes.length - 1) {
selected_combination += '_';
}
}
}
// hide all lower options
for (var i = current_level + 1; i < depth_index; i++) {
var selectedIdOptions = all_attributes[i].options;
all_attributes[i].value = null
for (var j = 0; j < selectedIdOptions.length; j++) {
// hide all lower options
selectedIdOptions[j].hidden = true;
var el = allowed_attribute_values.find(a => a.includes(selected_combination + selectedIdOptions[j].value));
if (el) {
selectedIdOptions[j].hidden = false;
}
}
}
}
if (event) {
var id = event.target.id;
} else {
var id = document.getElementsByClassName("attribute_price")[0].id;
}
var other_attributes = document.getElementsByClassName("attribute_price");
for (var i = 0; i < other_attributes.length; i++) {
if (other_attributes[i].id == id) {
allPossibleValues = getAllPossibleValues(i, other_attributes);
// we dont want to go deeper as of now
break;
}
}
}
</script>
this would work with any number of dropdown.
you can generate random attributes to test.
$(document).ready(function () {
/* generates random attributes */
var div_attributes = $('#div_attributes');
var select_colors = $('#select_colors');
var select_sizes = $('#select_sizes');
var select_attr0 = $('#select_attr0');
var select_attr1 = $('#select_attr1');
$('#btnGenerate').on('click', function () {
var result = "";
//
var index = getRandomArbitrary(1, select_sizes.find('option').get().length);
var random_attribute = select_sizes.find('option').eq(index).attr('value');
result += random_attribute;
//
index = getRandomArbitrary(1, select_colors.find('option').get().length);
random_attribute = select_colors.find('option').eq(index).attr('value');
result += '_' + random_attribute;
//
index = getRandomArbitrary(1, select_attr0.find('option').get().length);
random_attribute = select_attr0.find('option').eq(index).attr('value');
result += '_' + random_attribute;
//
index = getRandomArbitrary(1, select_attr1.find('option').get().length);
random_attribute = select_attr1.find('option').eq(index).attr('value');
result += '_' + random_attribute;
$('<div>' + result + '</div>').appendTo(div_attributes);
div_attributes.find('div').each(function () {
var item = $(this);
attributes.push(item.text());
});
SetFirstSelect();
});
var attributes = [];
//sets first select
SetFirstSelect();
function SetFirstSelect() {
$.each(attributes, function (i, val) {
var attribute = val.split('_')[0];
$('.attribute_price').eq(0).find('option[value="' + attribute + '"]').show();
});
}
//control attributes array
var selected_val = [];
$('.attribute_price').on('change', function () {
var item = $(this);
var index = item.index('.attribute_price');
selected_val[index] = item.val();
var select = $('.attribute_price').eq(index + 1);
var selected_attribute = item.val();
for (var i = index + 1; i < $('.attribute_price').get().length; i++) {
$('.attribute_price').eq(i).find('option').hide();
$('.attribute_price').eq(i).prop('selectedIndex', 0)
}
var selected_val_str = selected_val[0];
for (var i = 1; i <= index; i++) {
selected_val_str += '_' + selected_val[i];
}
$.each(attributes, function (j, val) {
if (val.indexOf(selected_val_str) >= 0) {
var attribute1 = val.split('_')[index + 1];
select.find('option[value="' + attribute1 + '"]').show();
}
});
});
function getRandomArbitrary(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
});
.attribute_price option {
display: none;
}
.container {
margin:30px;
}
.row > div {
padding:10px;
}
.btn {
margin-top:20px;
}
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<div class="container">
<div class="row">
<div style="width:50%; float:left">
<input type="button" class="btn btn-primary" id="btnGenerate" value="generate random attributes" />
<div id="div_attributes"></div>
</div>
<div style="width:50%; float:left">
<div class="form-group">
<label>Sizes</label>
<select class="form-control attribute_price" id="select_sizes">
<option value="">select size</option>
<option value="small">small</option>
<option value="large">large</option>
<option value="medium">medium</option>
</select>
</div>
<div class="form-group">
<label>color</label>
<select id="select_colors" class="form-control attribute_price">
<option value="">select color</option>
<option value="black">black</option>
<option value="yellow">yellow</option>
<option value="red">red</option>
</select>
</div>
<div class="form-group">
<label>attrType0</label>
<select id="select_attr0" class="form-control attribute_price">
<option value="">select attr0</option>
<option value="attr00">attr00</option>
<option value="attr01">attr01</option>
<option value="attr02">attr02</option>
</select>
</div>
<div class="form-group">
<label>attrType1</label>
<select id="select_attr1" class="form-control attribute_price">
<option value="">select attr1</option>
<option value="attr10">attr10</option>
<option value="attr11">attr11</option>
<option value="attr12">attr12</option>
</select>
</div>
</div>
</div>
</div>
I don't know if I have understood exactly what you are trying to explain or not but here's what I understood
you have a website with suppose dropdowns for say clothes having attributes size, price, color, brand and you have an array of objects containing all of these attributes for each object.
I will be explaining this in reactjs since i am more familiar with it than php
so for each dropdown you can have an onchange handler which invokes a function where you check the values of the other dropdowns. If You select size M then enable and populate the dropdown list after filtering clothes with size M. when that is done invoke a similar function for checking the other attributes.
at this time all of your dropdowns will be active, now if the user makes any changes to the first dropdown i.e. size you can either reset the other dropdowns to be reselected or just pass the new list depending on how you are handling
Here is a similar thing i had done for setting date
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July','August', 'September', 'October', 'November', 'December']
const Month30 = ['4', '6', '9', '11']
const Month31 = ['1', '3', '5', '7', '8', '10', '12']
class Dropdown extends React.Component {
constructor(props) {
super(props)
this.state = {
birthDay: '',
birthMonth: '',
birthYear: ''
}
}
componentDidMount() {
}
getMonthTotalDays = (birthMonth, birthYear) => {
if (birthMonth === '02') {
if (birthYear !== '' && birthYear % 4 === 0) {
return 29
} else {
return 28
}
} else if (Month30.includes(birthMonth)) {
return 30
} else {
return 31
}
}
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
}, () => {
const { birthMonth, birthYear, birthDay } = this.state
const days = this.getMonthTotalDays(birthMonth, birthYear)
if (birthDay > days) {
this.setState({ birthDay: '' })
}
})
}
renderMonths = () => {
return months.map((month, i) => {
if(i < 9){
return (<option key={`${month}-${i}`} value={'0' + (i + 1)}>{month}</option>)
}
else
return (<option key={`${month}-${i}`} value={i + 1}>{month}</option>)
})
}
renderDay = () => {
const { birthMonth, birthDay, birthYear } = this.state
const daysOptions = []
let days = this.getMonthTotalDays(birthMonth, birthYear)
for (let day=1; day<=days; day++) {
daysOptions.push(<option key={`'date-'${day}`} value={day}> { day } </option>)
}
return daysOptions
}
renderYears = () => {
const toYear = (new Date()).getFullYear() - 16
const yearOptions = []
for (let year = 1960; year <= toYear; year++) {
yearOptions.push(<option key={`year-${year}`} value={year}> { year } </option>)
}
return yearOptions
}
render() {
const { birthDay, birthMonth, birthYear } = this.state
return (
<div>
<label>Month</label>
<select
name="birthMonth"
value={ birthMonth }
onChange={this.handleChange}
>
<option disabled selected value=''>Month</option>
{ this.renderMonths() }
</select>
<label>Day</label>
<select
name='birthDay'
value={ birthDay }
onChange={this.handleChange}
>
<option disabled selected value=''>Day</option>
{ this.renderDay() }
</select>
<label>Year</label>
<select
name='birthYear'
value={ birthYear }
onChange={this.handleChange}
>
<option disabled selected value=''>Year</option>
{ this.renderYears() }
</select>
</div>
)
}
}
ReactDOM.render(
<Dropdown />,
document.getElementById('drop')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="drop"></div>
Here is a solution that takes a skus array as input and create a dropdown for each attribute. When any dropdown value changes, the options in all other dropdowns are updated to show only options consistent with picked options.
https://codepen.io/rockysims/pen/PyJbbv
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script type="text/javascript">
skus = [{
id: 1,
attributes: {
color: "red",
size: "small",
shape: "circle"
}
}, {
id: 2,
attributes: {
color: "red",
size: "medium",
shape: "square"
}
}, {
id: 3,
attributes: {
color: "yellow",
size: "small",
shape: "circle"
}
}, {
id: 4,
attributes: {
color: "yellow",
size: "medium",
shape: "square"
}
}, {
id: 5,
attributes: {
color: "yellow",
size: "large",
shape: "square"
}
}, {
id: 6,
attributes: {
color: "green",
size: "medium",
shape: "square"
}
}, {
id: 7,
attributes: {
color: "green",
size: "large",
shape: "square"
}
}];
$(function() {
const allOptionsByAttrName = buildAllOptionsByAttrName();
//create dropdowns
for (let attrName in allOptionsByAttrName) {
const dropdownId = attrName + "Dropdown";
const options = allOptionsByAttrName[attrName];
let html = "";
html += attrName + ": ";
html += buildDropdownHtml(dropdownId, options);
html += "<br/>";
$("#dropdowns").append(html);
}
//on dropdown changes, update options of all dropdowns
for (let changedAttrName in allOptionsByAttrName) {
$("#" + changedAttrName + "Dropdown").on('change', function() {
//build pickedOptionByAttrName
const pickedOptionByAttrName = {};
for (let attrName in allOptionsByAttrName) {
const dropdown = $("#" + attrName + "Dropdown");
pickedOptionByAttrName[attrName] = dropdown.val();
}
refreshAvailableOptions(pickedOptionByAttrName);
});
}
});
function buildAllOptionsByAttrName() {
const allOptionsByAttrName = {};
for (let sku of skus) {
for (let attrName in sku.attributes) {
allOptionsByAttrName[attrName] = allOptionsByAttrName[attrName] || [];
if (allOptionsByAttrName[attrName].indexOf(sku.attributes[attrName]) == -1) {
allOptionsByAttrName[attrName].push(sku.attributes[attrName]);
}
}
}
return allOptionsByAttrName;
}
function buildDropdownHtml(dropdownId, options) {
let html = "";
html += "<select id='" + dropdownId + "'>";
html += "<option value=''>";
html += "";
html += "</option>";
for (let option of options) {
html += "<option value='" + option + "'>";
html += option;
html += "</option>";
}
html += "</select>";
return html;
}
function refreshAvailableOptions(pickedOptionByAttrName) {
for (let attrName in pickedOptionByAttrName) {
//build availableOptions
const dropdown = $("#" + attrName + "Dropdown");
const options = $("#" + attrName + "Dropdown option");
const availableOptions = buildAvailableOptions(pickedOptionByAttrName, attrName);
availableOptions.push(""); //nothing picked option
//show available options and hide others
options.each(function() {
const option = $(this);
const optionIsAvailable = availableOptions.indexOf(option.val()) != -1;
if (optionIsAvailable) {
option.show();
} else {
option.hide();
}
});
}
}
function buildAvailableOptions(pickedOptionByAttrName, attrNameToBuildFor) {
//build availableSkus
const availableSkus = skus.filter(function(sku) {
let available = true;
for (let attrName in pickedOptionByAttrName) {
if (attrName != attrNameToBuildFor) {
const pickedOption = pickedOptionByAttrName[attrName];
if (pickedOption) {
available = available && sku.attributes[attrName] == pickedOption;
}
}
}
return available;
});
//build availableOptions
const availableOptions = [];
for (let sku of availableSkus) {
if (availableOptions.indexOf(sku.attributes[attrNameToBuildFor]) == -1) {
availableOptions.push(sku.attributes[attrNameToBuildFor]);
}
}
return availableOptions;
}
</script>
<div id="dropdowns">
</div>
If you prefer not to dynamically create the dropdowns, comment out the for loop under //create dropdowns and replace <div id="dropdowns"></div> with the following:
<div id="dropdowns">
color:
<select id="colorDropdown">
<option value=""></option>
<option value="red">red</option>
<option value="yellow">yellow</option>
<option value="green">green</option>
</select><br/>
size:
<select id="sizeDropdown">
<option value=""></option>
<option value="small">small</option>
<option value="medium">medium</option>
<option value="large">large</option>
</select><br/>
shape:
<select id="shapeDropdown">
<option value=""></option>
<option value="circle">circle</option>
<option value="square">square</option>
</select><br>
</div>
If you can identify in PHP which options for other categories are available for certain drop-down category-value selection. You can incrementally flag each option as excluded for each selected value to keep it disabled:
JS:
$(document).ready(function () {
$(".dropdown").change(function(){
var val = $(this).val();
var id = $(this).attr('id');
$.get('get_options.php', {category: id, value:val}, function(data) {
$(".dropdown:not(#"+id+")").each(function() {
var cat = $(this).attr('id');
$(this).find('option').each(function() {
var cat_val = $(this).val();
var options = data[cat];
var exluded = $(this).attr('exlude');
if ($.inArray(cat_val, options) !== -1) {
$(this).attr('exlude', exluded-1);
if(exluded == 1) {
$(this).prop('disabled', false);
}
} else {
$(this).attr('exlude', exluded+1);
$(this).prop('disabled', true);
}
});
});
}, 'json');
});
});
HTML:
<div id="dropdowns">
color:
<select id="color" class="dropdown">
<option value=""></option>
<option value="red">red</option>
<option value="yellow">yellow</option>
<option value="green">green</option>
</select><br/>
size:
<select id="size" class="dropdown">
<option value=""></option>
<option value="small">small</option>
<option value="medium">medium</option>
<option value="large">large</option>
</select><br/>
shape:
<select id="shape" class="dropdown">
<option value=""></option>
<option value="circle">circle</option>
<option value="square">square</option>
<option value="triangle">triangle</option>
</select><br>
**SAMPLE DATA: ** example if user selects color first, return all available options for other drop-down categories.
{"size": ["small","large"],"shape":["circle"]}
or in php:
$data = array(
'size' => array(
'small', 'large'
),
'shape' => array(
'circle'
),
);
echo json_encode($data);

disabling options in a dropdown based on another dropdown

I am trying to disable/remove options in a dropdown, based on the selection of other dropdown options.
But the dropdown/selector we have to choose from, has a sub-dropdown list and both main and sub dropdowns are
sharing the same classes and ids but since both are wrapped in different divs with different ids, I have been
trying to access the second selector but couldn't .
Below is the js code that I used for this;
<script type="text/javascript">
document.getElementById("lvl1").querySelector("select").onchange = function()
{
if(this.value=="8"){
var bikeSizes = document.getElementById("pa_size[]");
for ( var i = 1; i <8; i++) {
bikeSizes.options[i].removeAttribute("disabled", "disabled");
}
for ( var i = 8; i < 12; i++) {
bikeSizes.options[i].setAttribute("disabled", "disabled");
}
} else {
var bikeSizes = document.getElementById("pa_size[]");
for ( var i = 1; i < 8; i++) {
bikeSizes.options[i].setAttribute("disabled", "disabled");
}
for ( var i = 8; i < 12; i++) {
bikeSizes.options[i].removeAttribute("disabled", "disabled");
}
}
return false;
};
</script>
The html code of the selector/dropdowns are;
— Select —
Camping
Road Sports
Snow Sports
Water Sports
<div id="lvl1" level="1" style="">
<select data-required="yes" data-type="select" name="product_cat" id="product_cat" class="cat-ajax product_cat wpuf_product_cat_">
<option value="-1">— Select —</option>
<option class="level-0" value="8">Bikes</option>
</select>
<span data-taxonomy="{"required":"yes","name":"product_cat","exclude_type":"child_of","exclude":"camping,road-sports,water-sports","orderby":"name","order":"ASC"}"></span>
</div>
I fixed the quote then put it in an onload function. Also, there is no second argument on a removeAttribute. This worked for me.
<script>
window.onload = function () {
document.getElementById("product_cat").onchange = function () {
if (this.value == "8") {
var bikeSizes = document.getElementById("pa_size[]");
for (var i = 1; i < 8; i++) {
bikeSizes.options[i].removeAttribute("disabled");
}
for (var i = 8; i < 12; i++) {
bikeSizes.options[i].setAttribute("disabled", "disabled");
}
} else {
var bikeSizes = document.getElementById("pa_size[]");
for (var i = 1; i < 8; i++) {
bikeSizes.options[i].setAttribute("disabled", "disabled");
}
for (var i = 8; i < 12; i++) {
bikeSizes.options[i].removeAttribute("disabled");
}
}
return false;
};
}
</script>
<!-- my test html -->
<div id="lvl1" level="1" style="">
<select data-required="yes" data-type="select" name="product_cat" id="product_cat" class="cat-ajax product_cat wpuf_product_cat_">
<option value="-1">— Select —</option>
<option class="level-0" value="8">Bikes</option>
</select>
<span data-taxonomy="{"required":"yes","name":"product_cat","exclude_type":"child_of","exclude":"camping,road-sports,water-sports","orderby":"name","order":"ASC"}"></span>
<select id="pa_size[]">
<option value="-1">--Select--</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
</select>
</div>
Based on what I saw on your website, here is an alternative solution:
<script>
var group1 = [ { value:"33", text:"Small Unisex"},
{ value:"34", text:"Large Unisex"},
{ value:"35", text:"XL Unisex"},
{ value:"36", text:"Small Womens"},
{ value:"37", text:"Medium Womens"},
{ value:"38", text:"Large Womens"},
{ value:"39", text:"XL Womens"}];
var group2 = [{value:"40", text:"Medium"},
{value:"41", text:"Small"},
{ value:"43", text:"XL"},
{ value:"42", text:"Large"}];
window.onload = function() {
document.getElementById("product_cat").onchange = function () {
var bikeSizes = document.getElementById("pa_size[]");
bikeSizes.innerHTML = "";
bikeSizes.appendChild(new Option("--Select--", "-1"));
bikeSizes.disabled = false;
if (this.value == "-1") {
bikeSizes.disabled = true;
}
else if (this.value == "8") {
for (var i = 0; i < group1.length; ++i){
var opt = new Option(group1[i].text, group1[i].value);
bikeSizes.appendChild(opt);
}
} else {
for (var i = 0; i < group2.length; ++i) {
var opt = new Option(group2[i].text, group2[i].value);
bikeSizes.appendChild(opt);
}
}
return false;
};
}
</script>
so is this closer to what you need? This is 3 select boxes where you can only see the second one after the first one has a valid selection. Then you can only see the third one when the second one has a valid choice.
The options available is dependent on its master select box.
you can see it work here http://jsbin.com/nijahoh/edit?html,js,output
<script>
var fakeData = {
group1: [{ value: "group3", text: "3" }, { value: "group4", text: "4" }],
group2: [{ value: "group5", text: "5" }, { value: "group6", text: "6" }],
group3: [{ value: "group7", text: "7" }, { value: "group8", text: "8" }],
group4: [{ value: "group9", text: "9" }, { value: "group10", text: "10" }]
};
$(document).ready(function(){
// first select box change handler
$("#product_cat").on("change", function () {
var val = this.value;
$("#d").val("-1");
setUpSel($("#d"), val);
$("#d").trigger("change");
$("#d").css("display", val=="-1"?"none":"");
});
$("#d").on("change", function () {
var val = this.value;
$("#c").val("-1");
setUpSel($("#c"), val);
$("#c").trigger("change");
$("#c").css("display", val == "-1" ? "none" : "");
});
});
function setUpSel($sel, group) {
$sel.html("<option value='-1'>--Select--</option>");
var selData = fakeData[group];
$.each(selData, function (i, opt) {
$sel.append(new Option(opt.text, opt.value));
});
}
</script>

Categories

Resources