1
\$\begingroup\$

I want to toggle button visibility based on values a user selects within a group of checkboxes.

For example, the user may be given a group of checkboxes with colors:

<input type="checkbox" value="red">Redbr> 
<input type="checkbox" value="blue">Blue<br> 
<input type="checkbox" value="green">Green

And the available vehicles would be visible based on the colors selected:

<button class="choice-button" data-attributes=  "red,blue" type="button">Truck</button><br>
<button class="choice-button" data-attributes=  "green" type="button">Car</button><br>

I have code that works, but I do not believe it is optimized:

<!DOCTYPE HTML>
<html>
    <head>
    <script src="http://code.jquery.com/jquery-1.8.2.min.js"></script>
    <script type="text/javascript">

    //http://stackoverflow.com/questions/1181575/javascript-determine-whether-an-array-contains-a-value
    var indexOf = function(needle) {
        if(typeof Array.prototype.indexOf === 'function') {
            indexOf = Array.prototype.indexOf;
        } else {
            indexOf = function(needle) {
                var i = -1, index;

                for(i = 0; i < this.length; i++) {
                    if(this[i] === needle) {
                        index = i;
                        break;
                    }
                }

                return index;
            };
        }

        return indexOf.call(this, needle);
    };

    function doTheMagic()
    {
        //Re-initialize buttons by showing all of them
        $('.choice-button').show();

        //Find out which buttons are checked and store their value into an array
        var buttonRequirements = new Array();
        $('#myform :checkbox').each(function(){
            if($(this).is(':checked'))
                buttonRequirements.push($(this).val());
        });   


        //Loop through each of the available "choice buttons" and hide ones that do not fit the checked criteria (e.g. buttonRequirements)
        $('.choice-button').each(function(){

            buttonAttributes = $(this).data('attributes');
            buttonArray = buttonAttributes.toString().split(',');

            for (var i = 0; i < buttonRequirements.length; i++) {
                //If the button does not contain the checked attribute, hide it
                index = indexOf.call(buttonArray, buttonRequirements[i]); 
                if (index<0)
                    $(this).hide();
            }

        });

    }

    $(document).ready(function() {
        //Every time a checkbox value changes, call the function to show/hide the appropriate button
        $('#myform :checkbox').click(function() {
            doTheMagic();
        });
    });

    </script>
    </head>
    <body>
    <form id="myform">
    <h3>Filter choices based on attributes:</h3>
    <input type="checkbox" value="1">Attribute 1<br>
    <input type="checkbox" value="2">Attribute 2<br>
    <input type="checkbox" value="3">Attribute 3<br>
    <input type="checkbox" value="4">Attribute 4<br>
    <input type="checkbox" value="5">Attribute 5<br>

    <h3>Available choices</h3>
    <button class="choice-button" data-attributes="1,3,4,5" type="button">Choice 1</button><br>
    <button class="choice-button" data-attributes="2,3,4,5" type="button">Choice 2</button><br>
    <button class="choice-button" data-attributes=  "1,2,3,4" type="button">Choice 3</button><br>
    <button class="choice-button" data-attributes=  "1" type="button">Choice 4</button><br>
    <button class="choice-button" data-attributes=  "2" type="button">Choice 5</button><br>
    <button class="choice-button" data-attributes=  "3" type="button">Choice 6</button><br>
    <button class="choice-button" data-attributes=  "4" type="button">Choice 7</button><br>
    <button class="choice-button" data-attributes=  "5,2" type="button">Choice 8</button>

    </form>
    </body>
</html>

For my particular Use Case, I am pulling both the “choices” and “attributes” from a database. This form will be created dynamically every time and I want all data loaded up front in the UI.

There are currently 130 “choice buttons” and 78 “attributes”.

Throughout the lifetime of this application, this list will not change much. It would be a very rare scenario to grow even 20% over the next five years.

I thought about using bit comparison but that looks to be out of the question since I have more than 32 options to choose from.

I would like to steer away from the loops and minimize the code if possible.

How can I make this faster?

\$\endgroup\$
2
  • \$\begingroup\$ A function with magic in its name is usually pretty bad. \$\endgroup\$ – Florian Margaine Jul 1 '13 at 15:02
  • \$\begingroup\$ @FlorianMargaine It's an example; not production code. \$\endgroup\$ – ray023 Jul 1 '13 at 15:15
2
\$\begingroup\$

One thing, there is no reason to loop through to determine if the checkbox is checked

$('#myform :checkbox').each(function(){
    if($(this).is(':checked'))
        buttonRequirements.push($(this).val());
});   

do it with the selector.

$('#myform :checkbox:checked')....

You can also use map instead of pushing so you can avoid the each

var buttonRequirements = $(":checkbox:checked").map( 
    function () { 
        return this.value; 
    }).get();

And if you used a class instead of a data attribute you could shrink the code

var colors = $(":checkbox:checked").map( function(){ return this.value; }).get();
var goodClasses = colors.join(",");
$(".item").hide().filter(goodClasses).show();

JSFiddle: http://jsfiddle.net/Kmc65/

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