Search code examples
jqueryformscheckboxradio-buttonchecked

How can I get either radio option in a group, to select sibling (hidden) checkboxes?


I asked a similar question yesterday, but this scenario is a bit different, because there are several radio options (can be any number of them), and I need for either of them to select the sibling checkboxes (that will be hidden).

EDIT (more info): In addition: there can be several <div class="item"> in a <div class="panel-body">

Here's a fiddle - with what I have so far, only the last radio selects the checkboxes:

https://jsfiddle.net/ByteMyPixel/cqdj4jt6/1/
UPDATED FIDDLE: https://jsfiddle.net/ByteMyPixel/cqdj4jt6/3/

Here's the html:

<div class="panel-body rosette-body">

    <div class="item">
        <div class="the-options checkbox-wrapper">

            <div class="set checker">
                <input id="green-abalone-ring" name="rosette_option" type="radio" value="Green Abalone Ring"> 
                <label for="green-abalone-ring">Green Abalone Ring</label>
            </div>

            <div class="set checker">
                <input id="bloodwood-ring" name="rosette_option" type="radio" value="Bloodwood Ring"> 
                <label for="bloodwood-ring">Bloodwood Ring</label>
            </div>

            <div class="set checker">
                <input id="koa-wood-ring" name="rosette_option" type="radio" value="Koa Wood Ring"> 
                <label for="koa-wood-ring">Koa Wood Ring</label>
            </div>

            <div class="hidden-set">
                <input name="rosette_title" type="checkbox" value="Simple Rosette example">
                <input name="rosette_img" type="checkbox" value="/images/uploads/main/simple-rosette.jpg">
            </div>
        </div>
    </div><!-- [END] item -->

</div>

The jQuery I've tried to use:

$(document).on('change', '.rosette-body .checker input[type="radio"]', function() {
  $(this)
    .parents('.panel-body.rosette-body')
    .find('.checker')
    .each(function() {
      //var checkboxes = $(this).siblings('input[type="checkbox"]'),
      var checkboxes = $(this).siblings('.hidden-set').find('input[type="checkbox"]'),
        radio = $('input[type="radio"]', this).get(0);

      checkboxes.prop('checked', radio.checked);
    });
});

Solution

  • I would suggest you not bother iterating over all the radio buttons, there's really no need. All you likely want is the value of the currently selected radio button, to determine from THAT how you are to handle the hidden checkboxes. If that is the case, the following code does exactly that. The last line is the one that actually instantiates my RadioToCheckboxWidget, which wraps a given element with the appropriate listeners. When the user chooses a radio button, we get the value of the chosen radio button (either a value or "" in my example). Based on that value, we iterate over the checkboxes and set them ON for an actual value.

    To see this as a fiddle, here you go.

    /*****
     * RadioToCheckboxWidget(jQueryDOMNode)
     *   The RadioToCheckboxWidget takes a collection of radio button els
     *   and a collection of checkbox els, then toggles the checkbox based
     *   on the status of the radio buttons. if the radio with NO value is
     *   selected, the checkboxes are unchecked. If any other radio button
     *   is selected, then the checkboxes are checked.
     *
     *****/
    var RadioToCheckboxWidget = function(element) {
      this.el = element;
      this.__init();
    };
    
    $.extend(RadioToCheckboxWidget.prototype, {
      __init: function() {
        /***
         * This function was just edited, to allow the user to pass
         *  a collection of nodes. Thus, the user can either apply it
         *  one-by-one like this:
         *     var myRadio = new RadioToCheckboxWidget($("#myID") );
         *  ... or it can be applied to a collection of nodes like:
         *     var myRadios = new RadioToCheckboxWidget($(".item") );
         *
         *  The second scenario above is how I've applied it, but by doing
         *   the rewrite, there is flexibility.
         *
         ***/
         
         // iterate over every node in the jQuery collection, and
         //  set up each one as its own widget with its own listeners.
         //  by doing this, selections in one have no affect on others.
          this.el.each(function() {
            // convenience -- create a reference to the current el.
            var el = $(this);
            // references to the radio and checkbox els.
            var radioBtnEls = el.find(".checker");
            var checkboxEls = el.find(".hidden-set input[type=checkbox]");
            
            // listen for changes to ANY of the radio buttons
            el.on("change", radioBtnEls, function() {
              // if the radio has a value (so it's not 'None'), 
              //  we check the boxes. Otherwise, we UNcheck them.
                var toggleVal = $(this).find("input[type=radio]:checked").val();
                checkboxEls.each(function() {
                    if (toggleVal == "") {
                      $(this).prop("checked", false);
                    } else {
                      $(this).prop("checked", true);
                    } // end if toggleVal
                  }) // end checkboxEls.each()
              }) // end on change
          });
        } // end init()
    
    });
    
    
    // This is the line that actually uses all the above code -- this
    //  one line creates as many widgets as I have .item nodes.
    var myRadioPanes = new RadioToCheckboxWidget($(".item"));
    .item {
      border: 1px dotted #ccc;
      display: inline-box;
      float: left;
      padding: 10px;
      margin: 10px;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="panel-body rosette-body">
    
      <fieldset class="item">
      <legend>
      Wooden ring:
      </legend>
      <div class="the-options checkbox-wrapper">
    
          <div class="set checker">
            
            <label><input name="rosette_option" type="radio" checked value=""> None</label>
          </div>
          <div class="set checker">
            <input id="green-abalone-ring" name="rosette_option" type="radio" value="Green Abalone Ring">
            <label for="green-abalone-ring">Green Abalone Ring</label>
          </div>
    
          <div class="set checker">
            <input id="bloodwood-ring" name="rosette_option" type="radio" value="Bloodwood Ring">
            <label for="bloodwood-ring">Bloodwood Ring</label>
          </div>
    
          <div class="set checker">
            <input id="koa-wood-ring" name="rosette_option" type="radio" value="Koa Wood Ring">
            <label for="koa-wood-ring">Koa Wood Ring</label>
          </div>
    
          <div class="hidden-set">
            <input name="rosette_title" type="checkbox" value="Simple Rosette example">
            <input name="rosette_img" type="checkbox" value="/images/uploads/main/simple-rosette.jpg">
          </div>
        </div>
      </fieldset>
      <!-- [END] item -->
    
      <fieldset class="item">
      <legend>
      Gold chain necklace:
      </legend>
        <div class="the-options checkbox-wrapper">
    
          <div class="set checker">
            
            <label><input name="chain_option" type="radio" checked value=""> None</label>
          </div>
          <div class="set checker">
            <input id="three-strand-braid" name="chain_option" type="radio" value="Three-strand braid">
            <label for="three-strand-braid">Three-strand braid</label>
          </div>
    
          <div class="set checker">
            <input id="five-strand-braid" name="chain_option" type="radio" value="Five-strand gold braid">
            <label for="five-strand-braid">Five-strand braid</label>
          </div>
    
          <div class="set checker">
            <input id="seven-strand-braid" name="chain_option" type="radio" value="Seven-strand braid">
            <label for="seven-strand-braid">Seven-strand braid</label>
          </div>
    
          <div class="hidden-set">
            <input name="chain_title" type="checkbox" value="Simple Chain example">
            <input name="chain_img" type="checkbox" value="/images/uploads/main/simple-chain.jpg">
          </div>
        </div>
      </fieldset>
      <!-- [END] item -->
    
    </div>

    Per your request, you wanted to know how to use this for multiple .items. One way, perhaps, is to simply have a hard reference to each .item. But that's not really a great scalable solution. Instead, I've rewritten the widget's __init() function, so that you can pass in a collection of jQuery nodes and it'll set each one up as an independent widget. Now, you can either create a bunch of references to specific nodes (like new RadioToCheckBox($('#aSingleDOMNode') ); ), to as many as you need, or you can create a single reference to the widget and pass in an entire collection (like var myCollectionOfItems = new RadioToCheckbox($(".items") ); ).