Related
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.
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();
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);
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>
I have applied the logic in view.jsp in Liferay to show the state list on State drop down based on the Country selected from the Country drop down. I have used html and java script to achieve this goal of showing the state drop down for the selected country from drop down list. Currently, when I first load the form, both of the Country and the state label with drop down is displaying. The state drop down is empty at first. Then when I select country, Example: USA, state drop down is changed to the state list related to USA which is working as expected. But, I want to not show the state drop down and State label at first when form is loaded as it will be empty and there will be "Select Country" option only selected in Country drop down. This is not working for me. Any idea what should I do to make State label and drop down to show at first when form loads and just to show state drop down list only when the country is selected from Country drop down?
Below is the html and java script code that I have:
<select name="<portlet:namespace/>Country" id="countryId" onchange="javascript:countryChange()">
<option value="0">Select Country</option>
<option value='US'>United States</option>
<option value='CA'>Canada</option>
</select>
<label id="stateLabel">State:</label>
<select name="<portlet:namespace/>State" id="stateId">
</select>
<!--Country and State Change Javascript-->
<script>
function CountryChange() {
var countryState = [
[
'US', [
['', 'State/Province'],
['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
], ],
[
'CA', [
['', 'State/Province'],
['AB', 'Alberta'],
['BC', 'British Columbia'],
['MB', 'Manitoba'],
['NB', 'New Brunswick'],
]]
];
var countryElement = document.getElementById('countryId');
var stateElement = document.getElementById('stateId');
var stateLabelElement = document.getElementById('stateLabel');
if (countryElement && stateElement) {
var listOfState = [
['XX', 'None']
];
var currentCountry = countryElement.options[countryElement.selectedIndex].value;
for (var i = 0; i < countryState.length; i++) {
if (currentCountry == countryState[i][0]) {
listOfState = countryState[i][1];
}
}
if (listOfstate.length < 2)
{
stateElement.style.display = 'none';
stateLabelElement.style.display = 'none';
}
else
{
stateElement.style.display = 'inline';
stateLabelElement.style.display = 'inline';
}
var selectedState;
for (var i = 0; i < stateElement.length; i++) {
if (stateElement.options[i].selected === true) {
selectedState = stateElement.options[i].value;
}
}
stateElement.options.length = 0;
for (var i = 0; i < listOfState.length; i++) {
stateElement.options[i] = new Option(listOfState[i][1], listOfState[i][0]);
if (listOfState[i][0] == selectedState) {
stateElement.options[i].selected = true;
}
}
}
}
</script>
You had a lot of mis-spellings. Check this out: https://jsfiddle.net/jj8we58t/1/
I also set the initial display to none for the stateLabel and stateId elements.
<label id="stateLabel" style="display: none">State:</label>
<select name="<portlet:namespace/>State" style="display: none" id="stateId">
Your code was close, but due to some inconsistencies in variable names, it failed to function. For instance, your onchange event is bound to countryChange, but your function is named CountryChange. It's a good idea to drop your script into some sort of validator like jsHint to analyze why your code isn't working as expected.
with a couple tweaks and those inconsistencies ironed out, it appears to be functioning as expected now
HTML:
<select name="<portlet:namespace/>Country" id="countryId" onchange="window.CountryChange()">
<option value="0">Select Country</option>
<option value='US'>United States</option>
<option value='CA'>Canada</option>
</select>
<div id="stateField" style="display:none">
<label id="stateLabel">State:</label>
<select name="<portlet:namespace/>State" id="stateId">
</select>
</div>
JS:
window.CountryChange = function () {
var countryState = [
[
'US', [
['', 'State/Province'],
['AL', 'Alabama'],
['AK', 'Alaska'],
['AZ', 'Arizona'],
['AR', 'Arkansas'],
],
],
[
'CA', [
['', 'State/Province'],
['AB', 'Alberta'],
['BC', 'British Columbia'],
['MB', 'Manitoba'],
['NB', 'New Brunswick'],
]
]
];
var countryElement = document.getElementById('countryId');
var stateElement = document.getElementById('stateId');
var stateLabelElement = document.getElementById('stateLabel');
var stateFieldElement = document.getElementById('stateField');
if (countryElement && stateElement) {
var listOfState = [
['XX', 'None']
];
var currentCountry = countryElement.options[countryElement.selectedIndex].value;
for (var i = 0; i < countryState.length; i++) {
if (currentCountry == countryState[i][0]) {
listOfState = countryState[i][1];
}
}
if (listOfState.length < 2) {
stateFieldElement.style.display = "none";
} else {
stateFieldElement.style.display = "inline-block";
}
var selectedState;
for (var i = 0; i < stateElement.length; i++) {
if (stateElement.options[i].selected === true) {
selectedState = stateElement.options[i].value;
}
}
stateElement.options.length = 0;
for (var i = 0; i < listOfState.length; i++) {
stateElement.options[i] = new Option(listOfState[i][1], listOfState[i][0]);
if (listOfState[i][0] == selectedState) {
stateElement.options[i].selected = true;
}
}
}
}