1

I have 5 checkboxes that I use as filters for a set of data (rails app) and I'm having a hard time getting the Javascript down to manipulate the checkboxes for what I need.

Conditions:

  • If all is selected, uncheck any 'other' checkboxes but if unchecking this will leave none checked then keep it checked.
  • If no checkboxes are checked, set all to checked
  • If any 4 'other' checkboxes are checked, uncheck all

Essentially, one of them must be checked at all times, with the all filter being the default, however, all cannot be checked when any of the others are checked and if all is checked, then none of the others can be.

Code so far

jQuery(function($) {
    // all checkbox 
    var all_checkbox = $('#orderStatusAll');
    // 'other' checkboxes
    var open_checkbox = $('#orderStatusOpen');
    var complete_checkbox = $('#orderStatusCompleted');
    var reactivated_checkbox = $('#orderStatusReactivated');
    var canceled_checkbox = $('#orderStatusCanceled');

    var status_filter_form = $('#orderStatusFilters');
    var all_status_filters = status_filter_form.find('input[type=checkbox]');
    var filter_status_all = $('#filterStatusAll').find('input[type=checkbox]');
    var filter_status_other = $('#filterStatusOther').find('input[type=checkbox]');

    all_status_filters.on('change', function() {

       if ($(this).attr('id') === 'orderStatusAll' ) {
           if (all_status_filters.not(':checked')) {
               $(this).attr('checked', true);
               $(this).closest('label.checkbox').addClass('checked');
           } else {
               filter_status_other.each(function() {
                   $(this).attr('checked', false);        
                   $(this).closest('label.checkbox')
                   .removeClass('checked');
               });
           }
           status_filter_form.submit();
       } else if (all_status_filters.not(':checked')) {
           all_checkbox.attr('checked', true);
           all_checkbox.closest('label.checkbox').addClass('checked');
           status_filter_form.submit();
       } else {
           all_checkbox.attr('checked', false);
           all_checkbox.closest('label.checkbox').removeClass('checked');
           status_filter_form.submit();
       }
    });
});

The above follows the logic I need but clearly it doesn't work. I'm not sure how to return true if all checkboxes are unchecked and the else if part above is happening always and subsequently the first part of the if statement under the orderStatusAll is keeping the all checkbox checked always.

Javascript isn't my strong suit so any help and understanding would be greatly appreciated! Thanks!

Edit - HTML

<div class="panel-collapse collapse in" aria-expanded="true" id="statusFilters">
      <div class="panel-body">
        <%= form_tag(external_order_status_filter_path(current_user.client_id), { remote: true, id: 'orderStatusFilters' } ) do %>
          <div id="filterStatusAll">
            <label class="checkbox checked">
              <%= check_box_tag 'status[]', "All", true, { data: { toggle: 'checkbox' }, id: 'orderStatusAll' } %>
              All
              <span class="pull-right">
                [<%= co_status_count_helper(current_user.client, nil) %>]
              </span>
            </label>
          </div>
          <div id="filterStatusOther">
            <label class="checkbox">
              <%= check_box_tag 'status[]', "Open", false, { data: { toggle: 'checkbox' }, id: 'orderStatusOpen' } %>
              Open
              <span class="pull-right">
                [<%= co_status_count_helper(current_user.client, "Open") %>]
              </span>
            </label>
            <label class="checkbox">
              <%= check_box_tag 'status[]', "Completed", false, { data: { toggle: 'checkbox' }, id: 'orderStatusCompleted' } %>
              Completed
              <span class="pull-right">
                [<%= co_status_count_helper(current_user.client, "Completed") %>]
              </span>
            </label>
            <label class="checkbox">
              <%= check_box_tag 'status[]', "Reactivated", false, { data: { toggle: 'checkbox' }, id: 'orderStatusReactivated' } %>
              Reactivated
              <span class="pull-right">
                [<%= co_status_count_helper(current_user.client, "Reactivated") %>]
              </span>
            </label>
            <label class="checkbox">
              <%= check_box_tag 'status[]', "Canceled", false, { data: { toggle: 'checkbox' }, id: 'orderStatusCanceled' } %>
              Canceled
              <span class="pull-right">
                [<%= co_status_count_helper(current_user.client, "Canceled") %>]
              </span>
            </label>
          </div>
        <% end %>
      </div>
    </div>
cdouble.bhuck
  • 507
  • 1
  • 5
  • 19
  • is it `input[type=checkbox]` or `input[type='checkbox']`? – Pytth Feb 02 '18 at 18:16
  • It’s supposed to be but I’m assuming by your asking that the quotes around checkbox are necessary – cdouble.bhuck Feb 02 '18 at 18:18
  • Sorry for not being clear., but I believe having the quotes in the selector is the way to go. Another way to get the same selector would be `$(':checkbox')` Your selector is probably wrong, which is why your elseIF if always registering as true, because if there are no items in a selection, none of them can be "checked" – Pytth Feb 02 '18 at 18:20
  • Ah good to know, I added the quotes but `all_status_filters.not(':checked') is still firing every time – cdouble.bhuck Feb 02 '18 at 18:23
  • Can you please post the pertinent HTML you are working with? – Pytth Feb 02 '18 at 18:24
  • Ahh nvm my bad. Im pretty sure that will always register as true any way, since the `not` item is still a selector. It will return something either way. – Pytth Feb 02 '18 at 18:27
  • Added the HTML per your request – cdouble.bhuck Feb 02 '18 at 18:28
  • You want to check `.length` of your `.not(':checked')` to see if it matched anything. – Heretic Monkey Feb 02 '18 at 18:34

2 Answers2

1

The problem is, that else If statement will always register as "true" regardless because it is still a selector and will ring true because it is still returning a truthy value, even if the selector doesn't actually grab anything.

Take a look at this fiddle as an example.

https://jsfiddle.net/tx4L5rcL/

js

let a = $('.item').find('*').not('div');

console.log(a ? 'here' : 'nothere');

html

<div class="item">
  <div class="item-1">Heklklo</div>
  <div class="item-2"></div>
</div>

To get what you are looking for, consider doing something like this:

else if (all_status_filters.not(':checked').length)
Pytth
  • 4,008
  • 24
  • 29
  • Okay, I changed it but when I click any of the other ones (open, completed, etc), `all` still stays checked and the `else if` still fires when the `else` should be firing, i.e. `all` should be unchecked. Also doesn't help for when I click the `all` checkbox, since it should be either checking itself or unchecking everything else. – cdouble.bhuck Feb 02 '18 at 18:53
  • Henry Lu's answer worked for me but thank you for your time and efforts! – cdouble.bhuck Feb 02 '18 at 18:59
1
  1. As Mike McCaughan said, you need to check the number of not checked checkboxes.
  2. checkbox is a tricky one to 'uncheck' you need to manipulate it via prop and not attr Setting "checked" for a checkbox with jQuery?
  3. the conditions in the first nested looks wrong. it looks like you are wanting to check that all status checkboxes are not checked, but that includes the all checkbox. this should be changed to just all the other 4 checkboxs.

the resulting script (after removing unrelated code) should look something like the below, here's a link to jsfiddle with a simplified html https://jsfiddle.net/45jg4aa7/2/ :

jQuery(function($) {
    var tempConsole = $('#tempConsole');
    // all checkbox 
    var all_checkbox = $('#orderStatusAll');
    // 'other' checkboxes
    var open_checkbox = $('#orderStatusOpen');
    var complete_checkbox = $('#orderStatusCompleted');
    var reactivated_checkbox = $('#orderStatusReactivated');
    var canceled_checkbox = $('#orderStatusCanceled');
    var status_filter_form = $('#orderStatusFilters');
    var all_status_filters = status_filter_form.find('input[type="checkbox"]');
    var filter_status_all = $('#filterStatusAll').find('input[type="checkbox"]');
    var filter_status_other = $('#filterStatusOther').find('input[type="checkbox"]');

    all_status_filters.on('change', function() {
       if ($(this).attr('id') === 'orderStatusAll' ) {
           if (filter_status_other.not(':checked').length === filter_status_other.length) {
               $(this).prop('checked', true);
               // more code
           }
           else {
               filter_status_other.each(function() {
                   $(this).prop('checked', false);        
                   // mode code
               });
           } 
           // more code
       } else if (filter_status_other.not(':checked').length === filter_status_other.length) {
           all_checkbox.prop('checked', true);
           // more code
       } else {
           all_checkbox.prop('checked', false);
           // more code
       }
    });
});
Henry Lu
  • 381
  • 1
  • 8