0

Using Bootstrap 3.3.6, JQuery 1.11.3

I'm having an issue with a small section, specifically regarding selecting and deselecting a checkbox when the user clicks on the row.

jsfiddle here: http://jsfiddle.net/0ogdt9s7/1/

html

<div id="not-working">
  <h3>
  Click the text, then click the box
  </h3>
  <div class="selection-row" data-id="20">
    <div class="col-md-5">Data 20</div>
    <div class="col-md-2">
      <input type="checkbox" name="selectedDudes[]" value="20" id="checkbox-20">
    </div>
  </div>
  <div class="selection-row" data-id="22">
    <div class="col-md-5">Data 22</div>
    <div class="col-md-2">
      <input type="checkbox" name="selectedDudes[]" value="22" id="checkbox-22">
    </div>
  </div>
</div>

js

$(document).ready(function() {

  $(document).on("click", "div.selection-row", function() {
    var $cb = $('input[type="checkbox"]', $(this)), is_on = $cb.prop('checked');
    if (is_on) {
      //if its already checked, uncheck it
      $cb.prop('checked', false);
    } else {
      //if its not checked, check it
      $cb.prop('checked', true);
    }
  });
});

The row selection works great, but if you click on the checkbox inside the row, the checkbox does not change. Clicking on 'Data' will change it correctly, but the checkbox is doing nothing.

Important to note:

It must reside inside $(document).on() because this is inside a popup which is displayed through AJAX.

I'm assuming this is something super simple, but I just cannot find it. Any help is appreciated.

Sol Walters
  • 51
  • 1
  • 5
  • 1
    What happens if you add `onclick="return false"` to the checkbox input to stop it from responding to the onclick event. I'm just wondering if both row and input click events are triggering toggling the checkbox on then off or off then on in one click. – cmp-202 Dec 15 '16 at 19:51
  • doesn't seem to affect it: http://jsfiddle.net/0ogdt9s7/5/ – Sol Walters Dec 15 '16 at 20:03

5 Answers5

3

You are getting an unexpected result because of the Bubbling phase.

The Bubbling phase of an event triggers all element's parents that are listening to the event from bottom to top:

enter image description here

In your case, when the user clicks the checkbox first time, it gets selected. Then the Blubbling phase takes place and starts to search for the closest parent that is listening to the event, if there's any, its handler (function) gets executed. You have one: selection-row.

The handler of the .selection-row element reverts what the default behavior of the checkbox did, so it's like the user hasn't done nothing.

If what you want is that when the user clicks the Data 20 or the Data 22 and the respective checkbox gets selected, you don't need to use javascript. A label element should solve that:

  <div class="selection-row" data-id="20">
  <div class="col-md-5"><label for="checkbox-20" >Data 20</label></div>
    <div class="col-md-2">
      <input type="checkbox" name="selectedDudes[]" value="20" id="checkbox-20">
    </div>
  </div>
  <div class="selection-row" data-id="22">
  <div class="col-md-5"><label for="checkbox-22">Data 22</label></div>
    <div class="col-md-2">
      <input type="checkbox" name="selectedDudes[]" value="22" id="checkbox-22">
    </div>
  </div>

Check it out: http://jsfiddle.net/0ogdt9s7/2/

As you said you need the entire row to be clickable, you may check if the original target of the event (the innermost element) is the input and let the default behavior do its job by interrupting the handler with a return;:

$(document).on("click", "div.selection-row", function(e) {
    if ($(e.target).is('input')) return;

    var $cb = $('input[type="checkbox"]', $(this)), is_on = $cb.prop('checked');
    if (is_on) {
      //if its already checked, uncheck it
      $cb.prop('checked', false);
    } else {
      //if its not checked, check it
      $cb.prop('checked', true);
    }
  });

Check this solution: http://jsfiddle.net/0ogdt9s7/6/

rdleal
  • 987
  • 5
  • 15
  • thats an interesting option but the issue is scalability. its using the bootstrap css library, and im using rows of data (with more than just one field), and i want the whole row to be clickable. – Sol Walters Dec 15 '16 at 19:59
  • Wrap the label around the row. Then clicking anywhere in the row will click the checkbox. – Barmar Dec 15 '16 at 20:23
  • @Barmar the problem is that a `div` element is not allowed to be inside a `label` element, even though most browsers will not strictly follow these rules: http://stackoverflow.com/questions/18609554/is-div-inside-label-block-correct. – rdleal Dec 15 '16 at 20:28
0

Coworker just solved it with a target check:

http://jsfiddle.net/0ogdt9s7/4/

$(document).ready(function() {
  $(document).on("click", ".selection-row", function(e) {
    var $target = $(e.toElement);
    if (!$target.is('input')) {
      console.log($target);
      var $cb = $('input[type="checkbox"]', $(this)),
        is_on = $cb.is(':checked');
      if (is_on) {
        //if its already checked, uncheck it
        $cb.prop('checked', false);
      } else {
        //if its not checked, check it
        $cb.prop('checked', true);
      }
    }
  });
});

Will keep this open for a bit to see if theres a better solution

Sol Walters
  • 51
  • 1
  • 5
0

you could change the element you are listening for clicks on to the text like this:

$(".selector").click(function() {
  $(this).parent().find(".chkbox").trigger("click");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="not-working">
  <h3>
  Click the text, then click the box
  </h3>
  <div data-id="20">
    <div class="col-md-5 selector">Data 20</div>
    <div class="col-md-2">
      <input class="chkbox" type="checkbox" name="selectedDudes[]" value="20" id="checkbox-20">
    </div>
  </div>
  <div data-id="22">
    <div class="col-md-5 selector">Data 22</div>
    <div class="col-md-2">
      <input class="chkbox" type="checkbox" name="selectedDudes[]" value="22" id="checkbox-22">
    </div>
  </div>
</div>
Brad
  • 8,044
  • 10
  • 39
  • 50
0

Ok here's a nice clean example with bootstrap's list-group

$(".list-group-item").click(function(event) {
  var target = $(event.target);
  if (target.is("input")) {
    $(this).trigger(click);
  } else {
    $(this).find(".chkbox").trigger("click");
  }
});

<ul class="list-group">
  <li class="list-group-item">
    <input class="chkbox" type="checkbox"> Porta ac consectetur ac
  </li>
</ul>

DEMO

Brad
  • 8,044
  • 10
  • 39
  • 50
0

The bubble-up explanation is correct, but a simpler change would be to append event.stopPropagation() to your function

HTML

<input type="checkbox" id="agree-to-terms">
<label for="agree-to-terms">
  Agree to Terms and Conditions
</label>

<div class="proceed-to-checkout">
  <button type="submit" name="checkout" class="checkout-button button full"
  disabled>
  </button>

</div>

Javascript

const checkoutButton = document.querySelector(".checkout-button");

document.querySelector("#agree-to-terms").addEventListener("change",
function() {
  // stop bubble up
  this.checked ? checkoutButton.disabled = false: checkoutButton.disabled = true;
  event.stopPropagation();

});

Yohan de Rose
  • 134
  • 2
  • 7