Debopriyo Das
Debopriyo Das

Reputation: 135

How to change the value of HTML default <select> tag based on custom made <select> option?

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

Answers (2)

Carsten Massmann
Carsten Massmann

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

Devsi Odedra
Devsi Odedra

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

Related Questions