0

I have three dropdowns. Suppose there is the combination of "M / Grey" but not "M / Red" in the name="id" dropdown. If I select "M" in the first .variant dropdown the "Red" option should not be shown in the second .variant dropdown.

$(document).ready(function () {
$('.variant').change(function(){    
 var options = $(this).find('.variant').children(":selected").get().map(function(el) {
  return el.value
 }).join(" / ");
 $('select[name="id"] option:contains(' + options + ')').prop('selected', true);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<form action="#" method="post">
  <select class="variant">
   <option value="S">S</option>
   <option value="M">M</option>
   <option value="L">L</option>
  </select>
  <select class="variant">
   <option value="Grey">Grey</option>
   <option value="Red">Red</option>
   <option value="White">White</option>
  </select>
  <br>
  <select name="id">
   <option value="M / Grey">M / Grey</option>
   <option value="L / Grey">L / Grey</option>
   <option value="S / Red">S / Red</option>
   <option value="L / Red">L / Red</option>
   <option value="M / White">M / White</option>
   <option value="L / White">L / White</option>
   </select>
</form>
biberman
  • 5,606
  • 4
  • 11
  • 35
Anupam Mistry
  • 403
  • 6
  • 21

2 Answers2

2

Had the same idea as @biberman but my code allows you to change your .variants's second class to whatever you like - it could be <select class="variant size"> or <select class="variant variant1"> or whatever

$(document).ready(function () {
  
  function gatherCombinations () {
    var combos = $('select[name="id"] > option');
    var result = [];
    
    for (var i = 0; i < combos.length; i++) {
      var comboVal = $(combos[i]).val();
      var comboValArr = comboVal.split(" / ");
      
      result.push(comboValArr);
    }
    
    return result;
  }
  
  function isExistingCombination (var1, var2) {
    var combinations = gatherCombinations();
    
    for (var i = 0; i < combinations.length; i++) {
      var combo = combinations[i];
      
      if (combo.indexOf(var1) >= 0 && combo.indexOf(var2) >= 0) {
        return true;
      }
    }
    
    return false;
  }
  
  $('.variant').on('change', function() {
    var variantType = $(this).attr('data-var');
    var variantVal = $(this).val();
    var otherVariants = $('.variant:not(.' + variantType + ')');
    var otherVariantsOptions = $('.variant:not(.' + variantType + ') > option');
    
    otherVariantsOptions.show();
    
    for (var i = 0; i < otherVariantsOptions.length; i++) {
      var otherVariantOptionVal = $(otherVariantsOptions[i]).val();
      
      if (isExistingCombination(variantVal, otherVariantOptionVal) == false) {
        $(otherVariantsOptions[i]).hide();
      }
    }
    
    
  });
  
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form action="#" method="post">
  <select class="variant size" data-var="size">
   <option value="S">S</option>
   <option value="M">M</option>
   <option value="L">L</option>
  </select>
  <select class="variant color" data-var="color">
   <option value="Grey">Grey</option>
   <option value="Red">Red</option>
   <option value="White">White</option>
  </select>
  <br>
  <select name="id">
   <option value="M / Grey">M / Grey</option>
   <option value="L / Grey">L / Grey</option>
   <option value="S / Red">S / Red</option>
   <option value="L / Red">L / Red</option>
   <option value="M / White">M / White</option>
   <option value="L / White">L / White</option>
   </select>
</form>
DVN-Anakin
  • 1,549
  • 1
  • 8
  • 12
  • How It will work when I have `XL / Red`, value in `name=id` dropdown. Because when I select `L` and `Red` it will show `XL / Red` not the `L / Red` – Anupam Mistry Jul 02 '21 at 06:35
1

First: You should add to each select a placeholder like an empty option. Otherwise you couldn't trigger the change event if you select a preselected value (here S and Grey).


To update the other select.variant you have to compare its values (combined with the chosen value) with the values of the select[name="id"].

You could achieve this with multiple solutions, for example data-attributes, the value strings or classes. I used classes for the following solution.

<option class="m grey" value="M / Grey">M / Grey</option>

So you can simply check if there is an option that has the questionable class.

if (!$('option').is('.' + val + '.' + color_val)) {...}

At the beginning of the change handler you have to "reset" the options of the other select.variant (remove all disabled attributes).

Working example:

$(document).ready(function() {
  $('.variant').change(function() {
    const val = this.value.toLowerCase();
    const variant_type = this.className.replace('variant', '').trim();
    
    $('.variant:not(.' + variant_type + ') option').attr('disabled', false);
    $('.variant:not(.' + variant_type + ') option').each(function() {
      const other_val = this.value.toLowerCase();

      if (!$('option').is('.' + other_val + '.' + val)) {
        this.disabled = true;
      }
    });

  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<form action="#" method="post">
  <select class="variant size">
    <option></option>
    <option value="S">S</option>
    <option value="M">M</option>
    <option value="L">L</option>
    <option value="XL">XL</option>
  </select>
  <select class="variant color">
    <option></option>
    <option value="Grey">Grey</option>
    <option value="Red">Red</option>
    <option value="White">White</option>
  </select>
  <br>
  <select name="id">
    <option></option>
    <option class="m grey" value="M / Grey">M / Grey</option>
    <option class="l grey" value="L / Grey">L / Grey</option>
    <option class="s red" value="S / Red">S / Red</option>
    <option class="l red" value="L / Red">L / Red</option>
    <option class="m white" value="M / White">M / White</option>
    <option class="l white" value="L / White">L / White</option>
    <option class="xl red" value="XL / Red">XL / Red</option>
  </select>
</form>

If you want to use data-attributes you have to modify the function a slightly bit. The select.variant get instead of an additional class a data-type attribute. For example:

<select class="variant" data-type="size">

Combined example: (data-attributes and classes)

$(document).ready(function() {
  $('.variant').change(function() {
    const val = this.value.toLowerCase();
    const variant_type = this.dataset.type;

    $('.variant:not([data-type="' + variant_type + '"]) option').attr('disabled', false);
    $('.variant:not([data-type="' + variant_type + '"]) option').each(function() {
      const other_val = this.value.toLowerCase();

      if (!$('option').is('.' + other_val + '.' + val)) {
        this.disabled = true;
      }
    });

  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<form action="#" method="post">
  <select class="variant" data-type="size">
    <option value="S">S</option>
    <option value="M">M</option>
    <option value="L">L</option>
    <option value="XL">XL</option>
  </select>
  <select class="variant" data-type="color">
    <option value="Grey">Grey</option>
    <option value="Red">Red</option>
    <option value="White">White</option>
  </select>
  <br>
  <select name="id">
    <option class="m grey" value="M / Grey">M / Grey</option>
    <option class="l grey" value="L / Grey">L / Grey</option>
    <option class="s red" value="S / Red">S / Red</option>
    <option class="l red" value="L / Red">L / Red</option>
    <option class="m white" value="M / White">M / White</option>
    <option class="l white" value="L / White">L / White</option>
    <option class="xl red" value="XL / Red">XL / Red</option>
  </select>
</form>

For a solution with data-attributes only the select[name="id"] gets instead of two classes two data-attributes, one for each possible data-type-value - in this example: data-size and data-color. For example:

<option data-size="S" data-color="Red" value="S / Red">S / Red</option>

To get the data-values you can use the dataset keyword. To select an element you can use attribute-selectors. For example:

const variant_type = this.dataset.type;

$('.variant:not([data-type="' + variant_type + '"])')

With the data-type from the select.variant you could define which data-attribute of select[name="id"] you want to select. For example:

$('[data-' + other_type + '="' + other_val + '"]')

Working example:

$(document).ready(function() {
  
  $('.variant').change(function() {
    const val = this.value;
    const variant_type = this.dataset.type;
    const other_type = $('.variant:not([data-type="' + variant_type + '"])')[0].dataset.type;

    $('.variant:not([data-type="' + variant_type + '"]) option').attr('disabled', false);
    $('.variant:not([data-type="' + variant_type + '"]) option').each(function() {
      const other_val = this.value;

      if (!$('option').is('[data-' + variant_type + '="' + val + 
        '"][data-' + other_type + '="' + other_val + '"]')) {
        this.disabled = true;
      }
    });

  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<form action="#" method="post">
  <select class="variant" data-type="size">
    <option></option>
    <option value="S">S</option>
    <option value="M">M</option>
    <option value="L">L</option>
    <option value="XL">XL</option>
  </select>
  <select class="variant" data-type="color">
    <option></option>
    <option value="Grey">Grey</option>
    <option value="Red">Red</option>
    <option value="White">White</option>
  </select>
  <br>
  <select name="id">
    <option></option>
    <option data-size="M" data-color="Grey" value="M / Grey">M / Grey</option>
    <option data-size="L" data-color="Grey" value="L / Grey">L / Grey</option>
    <option data-size="S" data-color="Red" value="S / Red">S / Red</option>
    <option data-size="L" data-color="Red" value="L / Red">L / Red</option>
    <option data-size="M" data-color="White" value="M / White">M / White</option>
    <option data-size="L" data-color="White" value="L / White">L / White</option>
    <option data-size="XL" data-color="Red" value="XL / Red">XL / Red</option>
  </select>
</form>

If you want to auto update the third select when both select.variant are chosen you could define a data object. For example:

let selected = {};

With each call of the change handler you would update the object with the data-type and the value (add a new entry to the object or overwrite an existing one):

selected[variant_type] = val;

After that you would update the third select (add the selected attribute to the fitting option), but only if both select.variant were chosen, which means that both got an entry in the data object:

if (selected[variant_type] && selected[other_type]) {...}

Working example:

$(document).ready(function() {
  let selected = {};
  
  $('.variant').change(function() {
    const val = this.value;
    const variant_type = this.dataset.type;
    const other_type = $('.variant:not([data-type="' + variant_type + '"])')[0].dataset.type;

    selected[variant_type] = val;

    $('.variant:not([data-type="' + variant_type + '"]) option').attr('disabled', false);
    $('.variant:not([data-type="' + variant_type + '"]) option').each(function() {
      const other_val = this.value;

      if (!$('option').is('[data-' + variant_type + '="' + val + 
        '"][data-' + other_type + '="' + other_val + '"]')) {
        this.disabled = true;
      }
    });

    if (selected[variant_type] && selected[other_type]) {
      $('option[data-' + variant_type + '="' + selected[variant_type] + 
        '"][data-' + other_type + '="' + selected[other_type] + '"]'
        )[0].selected = true;
    }

  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<form action="#" method="post">
  <select class="variant" data-type="size">
    <option></option>
    <option value="S">S</option>
    <option value="M">M</option>
    <option value="L">L</option>
    <option value="XL">XL</option>
  </select>
  <select class="variant" data-type="color">
    <option></option>
    <option value="Grey">Grey</option>
    <option value="Red">Red</option>
    <option value="White">White</option>
  </select>
  <br>
  <select name="id">
    <option></option>
    <option data-size="M" data-color="Grey" value="M / Grey">M / Grey</option>
    <option data-size="L" data-color="Grey" value="L / Grey">L / Grey</option>
    <option data-size="S" data-color="Red" value="S / Red">S / Red</option>
    <option data-size="L" data-color="Red" value="L / Red">L / Red</option>
    <option data-size="M" data-color="White" value="M / White">M / White</option>
    <option data-size="L" data-color="White" value="L / White">L / White</option>
    <option data-size="XL" data-color="Red" value="XL / Red">XL / Red</option>
  </select>
</form>
biberman
  • 5,606
  • 4
  • 11
  • 35
  • @AnupamMistry I updated my answer with your "XL"-question from the comments. Please test it... – biberman Jul 02 '21 at 16:07
  • I test your given snippets, they all are working well. But the `data-attributes` are suitable to my code. But I don't want to use `const val = this.value.toLowerCase();` because my data values in `capitalize`. `data-size="XL"` `data-color="Grey"`. – Anupam Mistry Jul 08 '21 at 03:57
  • Also I want to auto select `name="id"` as I select both dropdown `class="variant"`. [Like this](https://stackoverflow.com/questions/68173488/how-to-auto-select-option-depending-on-another-dropdowns-value). – Anupam Mistry Jul 08 '21 at 04:02
  • 1
    I removed `toLowerCase()` in the pure `data-attributes` example. I will see if i can add the auto selection, although it wasn't part of the question... – biberman Jul 08 '21 at 08:52
  • I added the auto update to the second example and fixed a bug: instead of `$('.variant option').attr('disabled', false);` it should be `$('.variant:not(.' + variant_type + ') option').attr('disabled', false);`. Otherwise all disabled option are reenabled, so that you could for example after selecting another color select all colors again (M -> Grey -> White -> Red would now be selectable) – biberman Jul 08 '21 at 14:46
  • Thanks [biberman](https://stackoverflow.com/users/15377355/biberman) for good efforts. But can `const val` will get by `$(this).parent('form').find('.variant').children('data-size')` insted of `this.value`. – Anupam Mistry Jul 09 '21 at 11:58
  • Sorry, i don't understand what you mean. In the duplicate question that you asked yesterday (https://stackoverflow.com/questions/68296037/how-to-hide-and-auto-select-option-using-data-attributes#comment120737453_68296037) the solution is also with `const val = this.value;` – biberman Jul 09 '21 at 12:20
  • I have multiple forms like that. So whenever I select another form's dropdown value, It will be also changed in all `name="id"` dropdowns. So for that, I want to use the parent in `const val`. Somthing like `$(this).parent('form').find('.variant').children('data-size')` insted of `this.value` – Anupam Mistry Jul 12 '21 at 11:39
  • Hi. How can I reset the change function to the initial state with a button click? – Gedanggoreng May 17 '23 at 22:18