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'
.
To add a dropdown list dynamically, you would need to create the HTML <select> element, its label and optionally a <br> tag. In pure JavaScript, you can use the document. createElement() method to programmatically create a dropdown list. Then you can call the Node's appendChild() method or jQuery's .
A cascading drop-down list is a series of dependent DropDownList controls in which one DropDownList control depends on the parent or previous DropDownList controls. The items in the DropDownList control are populated based on an item that is selected by the user from another DropDownList control.
ready(function () { var list1 = document. getElementById('firstList'); list1. options[0] = new Option('--Select--', ''); list1. options[1] = new Option('Snacks', 'Snacks'); list1.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With