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\$

2 Answers 2

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\$
4
  • \$\begingroup\$ Sorry, I deleted the comment as soon as I realized I was wrong. \$\endgroup\$
    – Bill Barry
    Commented Jul 12, 2012 at 20:48
  • \$\begingroup\$ @BillBarry LoL, it's ok, i deleted mine too! \$\endgroup\$
    – user10934
    Commented Jul 13, 2012 at 4:16
  • \$\begingroup\$ @SpYk3HH tahnx, why did you use $val.length > 1 and not simply $val !== '0'? \$\endgroup\$
    – ilyo
    Commented Jul 13, 2012 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
    Commented Jul 14, 2012 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 and acknowledge you have read our privacy policy.

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