Reputation: 135
I am trying to change my value of the <select><option value=""></option></select>
based on a custom select option that I created. As select options can't be customised so easily we use JS, jQuery to customise it. As I am not good with JS I just searched a video and did exactly the same to change my select option. Now the issue is when I select a option from the custom select option the value of the original select option doesn't change, and because of this I cannot submit my form data.
Here is the default select option
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">Test Product 2</option>
<option value="2">Test Product 1</option>
<option value="1">Test Product</option>
</select>
</div>
</div>
</div>
Here is the custom one made with js
<div class="card my-5">
<div class="card-body">
<div class="dropdown">
<div class="dropdown__selected">---------</div>
<div class="dropdown__menu">
<input placeholder="Search..." type="text" class="dropdown__menu_search">
<div class="dropdown__menu_items">
<div class="dropdown__menu_item selected" data-value="">---------</div>
<div class="dropdown__menu_item" data-value="3">Test Product 2</div>
<div class="dropdown__menu_item" data-value="2">Test Product 1</div>
<div class="dropdown__menu_item" data-value="1">Test Product</div>
</div>
</div>
</div>
</div>
</div>
JS code to render the above custom select option
const dropdowns = document.querySelectorAll('.select-menu, #id_weight_1');
const form = document.querySelector('form')
if(dropdowns.length > 0){
dropdowns.forEach(dropdown => {
createCustomDropdown(dropdown);
});
}
// if(form !=null){
// form.addEventListener('submit', (e) => {
// e.preventDefault();
// const a = form.querySelector('[name=product]');
// console.log('Select:', a.options[a.selectedIndex].text);
// });
// }
// Create custom dropdown
function createCustomDropdown(dropdown) {
// Get all options and convert them from nodelist to array
const options = dropdown.querySelectorAll('option');
const optionsArr = Array.prototype.slice.call(options);
// Create custom dropdown element and add class dropdown to it
// Insert it in the DOM after the select field
const customDropdown = document.createElement('div');
customDropdown.classList.add('dropdown');
dropdown.insertAdjacentElement('afterend', customDropdown);
// Create element for selected option
// Add class to this element, text from the first option in select field and append it to custom dropdown
const selected = document.createElement('div');
selected.classList.add('dropdown__selected');
selected.textContent = optionsArr[0].textContent;
customDropdown.appendChild(selected);
// Create element for dropdown menu, add class to it and append it to custom dropdown
// Add click event to selected element to toggle dropdown menu
const menu = document.createElement('div');
menu.classList.add('dropdown__menu');
customDropdown.appendChild(menu);
selected.addEventListener('click', toggleDropdown.bind(menu));
// Create serach input element
// Add class, type and placeholder to this element and append it to menu element
const search = document.createElement('input');
search.placeholder = 'Search...';
search.type = 'text';
search.classList.add('dropdown__menu_search');
menu.appendChild(search);
// Create wrapper element for menu items, add class to it and append to menu element
const menuItemsWrapper = document.createElement('div');
menuItemsWrapper.classList.add('dropdown__menu_items');
menu.appendChild(menuItemsWrapper);
// Loop through all options and create custom option for each option and append it to items wrapper element
// Add click event for each custom option to set clicked option as selected option
optionsArr.forEach(option => {
const item = document.createElement('div');
item.classList.add('dropdown__menu_item');
item.dataset.value = option.value;
item.textContent = option.textContent;
menuItemsWrapper.appendChild(item);
item.addEventListener('click', setSelected.bind(item, selected, dropdown, menu));
});
// Add selected class to first custom option
menuItemsWrapper.querySelector('div').classList.add('selected');
// Add input event to search input element to filter items
// Add click event to document element to close custom dropdown if clicked outside of it
// Hide original dropdown(select)
search.addEventListener('input', filterItems.bind(search, optionsArr, menu));
document.addEventListener('click', closeIfClickedOutside.bind(customDropdown, menu));
// dropdown.style.display = 'none';
}
// Toggle dropdown
function toggleDropdown() {
// Check if dropdown is opened and if it is close it, otherwise open it and focus search input
if(this.offsetParent !== null) {
this.style.display = 'none';
}else {
this.style.display = 'block';
this.querySelector('input').focus();
}
}
// Set selected option
function setSelected(selected, dropdown, menu) {
// Get value and label from clicked custom option
const value = this.dataset.value;
const label = this.textContent;const th =
// Change the text on selected element
// Change the value on select field
selected.textContent = label;
dropdown.value = value;
// Close the menu
// Reset search input value
// Remove selected class from previously selected option and show all divs if they were filtered
// Add selected class to clicked option
menu.style.display = 'none';
menu.querySelector('input').value = '';
menu.querySelectorAll('div').forEach(div => {
if(div.classList.contains('selected')) {
div.classList.remove('selected');
}
if(div.offsetParent === null) {
div.style.display = 'block';
}
});
this.classList.add('selected');
}
// Filter items
function filterItems(itemsArr, menu) {
// Get all custom options
// Get the value of search input and convert it to all lowercase characters
// Get filtered items
// Get the indexes of filtered items
const customOptions = menu.querySelectorAll('.dropdown__menu_items div');
const value = this.value.toLowerCase();
const filteredItems = itemsArr.filter(item => item.label.toLowerCase().includes(value));
const indexesArr = filteredItems.map(item => itemsArr.indexOf(item));
// Check if option is not inside indexes array and hide it and if it is inside indexes array and it is hidden show it
itemsArr.forEach(option => {
if(!indexesArr.includes(itemsArr.indexOf(option))) {
customOptions[itemsArr.indexOf(option)].style.display = 'none';
}else {
if(customOptions[itemsArr.indexOf(option)].offsetParent === null) {
customOptions[itemsArr.indexOf(option)].style.display = 'block';
}
}
});
}
// Close dropdown if clicked outside dropdown element
function closeIfClickedOutside(menu, e) {
if(e.target.closest('.dropdown') === null && e.target !== this && menu.offsetParent !== null) {
menu.style.display = 'none';
}
}
Like here we have tow select options, the #taxes
value get changed based on the value of #invoice
. Similarly I want to make my select option work like that, since I have one select option and the custom one are made of div elements. I hope you won;t mind answering my question, I am really bad with js, please help!
<select id="invoice">
<option value="no" selected>no invoice</option>
<option value="invoice-type-1">invoice type 1</option>
<option value="invoice-type-2">invoice type 2</option>
</select>
<select id="taxes">
<option value="0"> tax free</option>
<option value="19"> apply 19%</option>
</select>
$('#invoice').change(function(){
if($(this).val()=='no')
$('#taxes').val('0');
else
$('#taxes').val('19');
});
Upvotes: 0
Views: 736
Reputation: 28246
I don't know why you insist on having a "custom built" select box as your second widget. You can simplify life if you use a standard select box and synchronize the first one by doing something simple as shown below:
document.getElementById('id_custom').addEventListener('change',ev=>
document.getElementById('id_product').value=ev.target.value )
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select>
</div>
</div>
</div>
<div class="card my-5">
<div class="card-body">
<label>custom<br>
<select id="id_custom">
<option value="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select></label>
</div>
</div>
If you want the change to be synchronized in both directions you could do this:
document.getElementById('filt').oninput=ev=>
[...document.getElementById('id_custom').children].forEach(o=>
o.classList.toggle('hidden',o.textContent.toLowerCase().indexOf(ev.target.value.toLowerCase())<0)
)
document.getElementById('container').addEventListener('change',ev=>
ev.target.tagName=='SELECT' && [...document.querySelectorAll('SELECT')].forEach(s=>s.value=ev.target.value)
)
.hidden {display:none}
<div id="container">
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select>
</div>
</div>
</div>
<div class="card my-5">
<div class="card-body">
<label>custom<br>
<input type="text" id="filt" placeholder="filter ..."><br>
<select id="id_custom">
<option value="">---------</option>
<option value="3">ABC test product 2</option>
<option value="2">DEF test product 1</option>
<option value="1">GHI test product 0</option>
</select></label>
</div>
</div>
</div>
In this snippet I also included a simple filter mechanism for the second select box. No further library or framework required.
Upvotes: 1
Reputation: 5322
See as below selection based on custom dropdown
const dropdowns = document.querySelectorAll('.select-menu, #id_weight_1');
const form = document.querySelector('form')
if(dropdowns.length > 0){
dropdowns.forEach(dropdown => {
createCustomDropdown(dropdown);
});
}
// if(form !=null){
// form.addEventListener('submit', (e) => {
// e.preventDefault();
// const a = form.querySelector('[name=product]');
// console.log('Select:', a.options[a.selectedIndex].text);
// });
// }
// Create custom dropdown
function createCustomDropdown(dropdown) {
// Get all options and convert them from nodelist to array
const options = dropdown.querySelectorAll('option');
const optionsArr = Array.prototype.slice.call(options);
// Create custom dropdown element and add class dropdown to it
// Insert it in the DOM after the select field
const customDropdown = document.createElement('div');
customDropdown.classList.add('dropdown');
dropdown.insertAdjacentElement('afterend', customDropdown);
// Create element for selected option
// Add class to this element, text from the first option in select field and append it to custom dropdown
const selected = document.createElement('div');
selected.classList.add('dropdown__selected');
selected.textContent = optionsArr[0].textContent;
customDropdown.appendChild(selected);
// Create element for dropdown menu, add class to it and append it to custom dropdown
// Add click event to selected element to toggle dropdown menu
const menu = document.createElement('div');
menu.classList.add('dropdown__menu');
customDropdown.appendChild(menu);
selected.addEventListener('click', toggleDropdown.bind(menu));
// Create serach input element
// Add class, type and placeholder to this element and append it to menu element
const search = document.createElement('input');
search.placeholder = 'Search...';
search.type = 'text';
search.classList.add('dropdown__menu_search');
menu.appendChild(search);
// Create wrapper element for menu items, add class to it and append to menu element
const menuItemsWrapper = document.createElement('div');
menuItemsWrapper.classList.add('dropdown__menu_items');
menu.appendChild(menuItemsWrapper);
// Loop through all options and create custom option for each option and append it to items wrapper element
// Add click event for each custom option to set clicked option as selected option
optionsArr.forEach(option => {
const item = document.createElement('div');
item.classList.add('dropdown__menu_item');
item.dataset.value = option.value;
item.textContent = option.textContent;
menuItemsWrapper.appendChild(item);
item.addEventListener('click', setSelected.bind(item, selected, dropdown, menu));
});
// Add selected class to first custom option
menuItemsWrapper.querySelector('div').classList.add('selected');
// Add input event to search input element to filter items
// Add click event to document element to close custom dropdown if clicked outside of it
// Hide original dropdown(select)
search.addEventListener('input', filterItems.bind(search, optionsArr, menu));
document.addEventListener('click', closeIfClickedOutside.bind(customDropdown, menu));
// dropdown.style.display = 'none';
}
// Toggle dropdown
function toggleDropdown() {
// Check if dropdown is opened and if it is close it, otherwise open it and focus search input
if(this.offsetParent !== null) {
this.style.display = 'none';
}else {
this.style.display = 'block';
this.querySelector('input').focus();
}
}
// Set selected option
function setSelected(selected, dropdown, menu) {
// Get value and label from clicked custom option
const value = this.dataset.value;
console.log(value);
document.getElementById("id_product").selectedIndex = value;
const label = this.textContent;const th =
// Change the text on selected element
// Change the value on select field
selected.textContent = label;
dropdown.value = value;
// Close the menu
// Reset search input value
// Remove selected class from previously selected option and show all divs if they were filtered
// Add selected class to clicked option
menu.style.display = 'none';
menu.querySelector('input').value = '';
menu.querySelectorAll('div').forEach(div => {
if(div.classList.contains('selected')) {
div.classList.remove('selected');
}
if(div.offsetParent === null) {
div.style.display = 'block';
}
});
this.classList.add('selected');
}
// Filter items
function filterItems(itemsArr, menu) {
// Get all custom options
// Get the value of search input and convert it to all lowercase characters
// Get filtered items
// Get the indexes of filtered items
const customOptions = menu.querySelectorAll('.dropdown__menu_items div');
const value = this.value.toLowerCase();
const filteredItems = itemsArr.filter(item => item.label.toLowerCase().includes(value));
const indexesArr = filteredItems.map(item => itemsArr.indexOf(item));
// Check if option is not inside indexes array and hide it and if it is inside indexes array and it is hidden show it
itemsArr.forEach(option => {
if(!indexesArr.includes(itemsArr.indexOf(option))) {
customOptions[itemsArr.indexOf(option)].style.display = 'none';
}else {
if(customOptions[itemsArr.indexOf(option)].offsetParent === null) {
customOptions[itemsArr.indexOf(option)].style.display = 'block';
}
}
});
}
// Close dropdown if clicked outside dropdown element
function closeIfClickedOutside(menu, e) {
if(e.target.closest('.dropdown') === null && e.target !== this && menu.offsetParent !== null) {
menu.style.display = 'none';
}
}
<label for="id_product">Product</label>
<div class="card my-5">
<div class="card-body">
<div class="select-menu my-5">
<select name="product" class="custom-select product-select" id="id_product">
<option value="" selected="">---------</option>
<option value="3">Test Product 2</option>
<option value="2">Test Product 1</option>
<option value="1">Test Product</option>
</select>
</div>
</div>
</div>
Upvotes: 2