Search code examples
javascriptevent-handlingdom-eventshtml-select

When one option is selected all others should be disabled


I have a dropdown menu that allows multiple selections. Now I want to make it when one particular option has selected all others to be disabled and enabled for selection. If that one particular is de-selected all others should be enabled again.

This is my select dropdown:

<select class="input-fields selectpicker" id="select_heigh" name="search[]" multiple>
    <option value="all" selected>Search all</option>
    <option value="tag">Tags</option>
    <option value="username">Username</option>
    <option value="email">Email</option>
    <option value="full_name">Full Name</option>
</select>

And here is what I have tried for the js

$(document).ready(function() {
    $('.selectpicker').selectpicker();
    $('.selectpicker').on('change', function() {
        if ($('option[value="all"]', this).is(':selected') && $(this).val().length > 1) {
            $('option[value="all"]', this).prop('selected', false);
            $('.selectpicker').selectpicker('refresh');
        }

        var selected = $(this).val();
        if (selected.includes("tag")) {
            $('option[value!="tag"]', this).prop('disabled', true);
        } else {
            $('option[value!="tag"]', this).prop('disabled', false);
        }

        if (selected.length > 3) {
            $(this).selectpicker('setStyle', 'selected-count', 'btn-danger');
            $(this).selectpicker('setTitle', selected.length + ' select(s)');
        } else {
            $(this).selectpicker('setStyle', 'selected-count', 'btn-default');
            $(this).selectpicker('setTitle', 'Select');
        }
    });
});

I want when "Tag" is selected the other options to be disabled. When "Tag" is de-selected the others are enabled. When any other option is selected to no effect on others.

Also, the counting of selected choices doesn't work as expected. It should start showing Selected(3), Selected(4) ... after the third selection. Currently, it shows all of them not count of them.

I'm not that familiar with JS and not sure if I'm on the right path here


Solution

  • What the OP wants to achieve is a rather unexpected behavior of a native form control.

    And in case one changes the behavior it should be based on using what form elements or elements in particular do support natively like the disabled- and the dataset-property.

    An implementation then could be as simple as querying the correct select element and subscribing an event listener to any click event which occurres on the very select element. The change event can not be used since any further changes are impossible once a single option is selected but all other option are disabled. An option element's dataset gets used as lookup in order to detect whether the very element already has been selected before the current click handling.

    function handleOptionClickBehavior({ target }) {
      const optionNode = target.closest('option');
    
      const nodeValue = optionNode?.value;
      if (nodeValue === 'tag') {
    
        const optionNodeList = [...optionNode.parentNode.children]
          .filter(node => node !== optionNode);
    
        const { dataset } = optionNode;
        if (dataset.hasOwnProperty('selectedBefore')) {
    
          Reflect.deleteProperty(dataset, 'selectedBefore');
          optionNode.selected = false;
    
          optionNodeList
            .forEach(node => node.disabled = false);
        } else {
          dataset.selectedBefore = '';
    
          optionNodeList
            .forEach(node => node.disabled = true);
        }
      }
    }
    
    document
      .querySelector('.selectpicker')
      .addEventListener('click', handleOptionClickBehavior)
    body { zoom: 1.2 }
    <select class="input-fields selectpicker" id="select_heigh" name="search[]" size="5" multiple>
      <option value="all" selected>Search all</option>
      <option value="tag">Tags</option>
      <option value="username">Username</option>
      <option value="email">Email</option>
      <option value="full_name">Full Name</option>
    </select>