1
\$\begingroup\$

I have multiple drop down lists (select & option) that are populated by data from the server. They all have the same options, but I need the use to be able to select every option only once - it can appear as selected only on one list.

This is the HTML of a single list - simple select & options list:

<div class="social-option">
    <select name="hex_theme_options[social-service-1]">
        <option selected="selected" value="0"></option>
        <option value="facebook">facebook</option>
        <option value="twitter">twitter</option>
        <option value="linkedin">linkedin</option>
        <option value="e-mail">e-mail</option>
        <option value="phone">phone</option>
        <option value="instagram">instagram</option>
        <option value="flickr">flickr</option>
        <option value="dribbble">dribbble</option>
        <option value="skype">skype</option>
        <option value="picasa">picasa</option>
        <option value="google-plus">google-plus</option>
        <option value="forrst">forrst</option>
    </select>
</div>

And this is the JS code for managing them the way I described. It works, but it looks pretty ugly to me, and I'd like to improve it's structure. Any suggestions are welcome.

jsFiddle

(function($){
    $(document).ready(function() {

        var siblings = {
            lock: function (newSelected){
                var selectedSiblings = $('.social-option select').find("option[value=" + newSelected.val() + "]");
                selectedSiblings.not(newSelected).attr('disabled', 'disabled');
            },
            unlock: function (oldSelected){
                var selectedSiblings = $('.social-option select').find("option[value=" + oldSelected.val() + "]");
                selectedSiblings.removeAttr('disabled');
            },
            unlockZero: function (){
                $('.social-option select').find("option[value='0']").removeAttr('disabled');
            }
        };


        function checkSiblings(oldSelected, newSelected) {
            if (oldSelected === '0') {
                siblings.lock(newSelected);
            } else if (newSelected === '0') {
                siblings.unlock(oldSelected);
            } else {
                siblings.unlock(oldSelected);
                siblings.lock(newSelected);
            }
        }


        $('.social-option select').each(function() {
            siblings.lock($('option:selected', this));
            siblings.unlockZero();
        });

        $('.social-option select').on('focus', function () {
            var oldSelected = $('option:selected', this);

            $('.social-option select').on('change', function () {
                var newSelected = $('option:selected', this);
                checkSiblings(oldSelected, newSelected);
            });
        });


    });
})(jQuery);
\$\endgroup\$
1
\$\begingroup\$

I could be wrong, but I think you could shorten ALOT of the code as follows:

$(function () {
    $(".social-option select").on("change", function(e) {
        $(".social-option select option:disabled").prop("disabled", false);
        $(".social-option select option:selected").each(function(i) {
            var $val = $(this).val();
            if ($val !== '0') {
                $(".social-option select option[value="+$val+"]").prop("disabled", true);
            };
        });
    }).change();
})​;

See fiddle with dynamic drop down

added here

\$\endgroup\$
  • \$\begingroup\$ Sorry, I deleted the comment as soon as I realized I was wrong. \$\endgroup\$ – Bill Barry Jul 12 '12 at 20:48
  • \$\begingroup\$ @BillBarry LoL, it's ok, i deleted mine too! \$\endgroup\$ – user10934 Jul 13 '12 at 4:16
  • \$\begingroup\$ @SpYk3HH tahnx, why did you use $val.length > 1 and not simply $val !== '0'? \$\endgroup\$ – ilyo Jul 13 '12 at 8:43
  • \$\begingroup\$ eh, coulda gone either way. a string compare maybe less intensive by a nanosecond or two, but its all up to the end coder. \$\endgroup\$ – user10934 Jul 14 '12 at 18:12
1
\$\begingroup\$

This might be a little more generic:

(function($) {
    var checkSiblings = function(group, oldSelected, newSelected) {
        group.find("option[value=" + oldSelected.val() + "]").removeAttr('disabled');
        group.find("option[value=" + newSelected.val() + "]").not(newSelected).attr('disabled', 'disabled');
    };
    $.fn.distinctValues = function() {
        var group = this;
        this.each(function(idx, selectBox) {
            $(selectBox).on('change', function() {
                var $this = $(this);
                var newSelected = $('option:selected', this);
                checkSiblings(group, $this.data('oldSelected'), newSelected);
                $this.data('oldSelected', newSelected);
            }).data('oldSelected', $('option:selected', this));
        });
    };
}(jQuery));


jQuery('.social-option select').distinctValues();​

It doesn't handle something it looks like you were trying to do but had only partially implemented, namely allow multiple different select boxes to share the empty option. I'm sure it can be done fairly simply, but my first attempt didn't work, and I'm out of time at the moment. (In other words, that's left as an exercise for the reader :-) )

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.