0

So I have a ul of checkboxes and I want to allow users to click anywhere on the li elements to check/uncheck the corresponding checkbox, then apply the changes to the page via javascript. Note that all checkboxes start out checked. This is the layout, excluding a few more li elements.

<ul class="dropdown-menu">
    <li class="cat-header">
        <strong>Category</strong>
    </li>
    <li>
        <a class="filter-link"> Red
            <input checked="true" class="cat-check" data-filter="red" data-type="color" type="Checkbox">
        </a>
    </li>

I then try use javascript to alter the page based on the checked property of the checkboxes. However, I'm getting different values based on how I select the elements. My function looks like this

setFilter = function(checkbox) {
  console.log(checkbox);
  if ($(checkbox).attr('data-type') === "color") {
    if (!$(checkbox).prop('checked')) {
      $(checkbox).prop('checked', false);
      colorFilter = colorFilter.replace($(checkbox).attr('data-filter') + " ", "");
    } else {
      $(checkbox).prop('checked', true);
      colorFilter = colorFilter + $(checkbox).attr('data-filter') + " ";
    }
  } else if ($(checkbox).attr('data-type') === "shape") {
    if (!$(checkbox).prop('checked')) {
      $(checkbox).prop('checked', false);
      shapeFilter = shapeFilter + $(checkbox).attr('data-filter') + " ";
    } else {
      $(checkbox).prop('checked', true);
      shapeFilter = shapeFilter.replace($(checkbox).attr('data-filter') + " ", "");
    }
  }
}; 

A couple notes. First, color is inclusive (i.e. show it if it has that color) while shape is exclusive (i.e. don't show it if it has that shape) hence the slightly different logic. Second, .prop() function was returning the opposite values of what I was expecting hence the if (!$(checkbox).prop('checked'))

When I call it like this, it prints out true

$('.filter-link').click(function(e) { 
    setFilter($(this).children("input")[0]);
  });

but when I call it like this, it prints out false

$('.cat-check').click(function(e) {
    setFilter($(this)[0]);
  });

I tried to reproduce the effect in JSFiddle (https://jsfiddle.net/eg9c4vkv/6/) but was unable to duplicate the effect. I also thought it might be related to console.log (How can I change the default behavior of console.log? (*Error console in safari, no add-on*)) but theoretically the function should be behaving the same as long as the input is the same. Does anyone have any ideas?

Community
  • 1
  • 1
cfatt10
  • 688
  • 1
  • 7
  • 13

2 Answers2

1

I see a couple minor issues, but nothing obvious.

First would be the way in which you're accessing the data attribute. Second is how you're binding handlers (are you sure each time you click the checkbox an event isn't fired for both .cat-check AND .filter-link, which could result in unexpected behavior).. either way I've redone the function just in case it helps.

By including the data- prefix on the attribute, you're telling jQuery to cache the value automatically when it's ready.
Refer to jQuery data docs for more info

<a href="" data-filter="hello">Hello</a>

would be equivalent to

$('a').data('filter', 'hello');

To access the data, you just need to use the key, which in this case would be filter or type. It's worth noting that you could access the attribute directly like you are, but use one or the other (preferably .data).

e.g. with markup..

<a href="" data-filter="hello">Hello</a>

$('a').on('click', function(e) { 
    var $this = $(this);
    $this.data('filter', 'I changed');
    //what will the value be?  It's going to be 'Hello', because we're accessing the attribute in the DOM
    alert($this.attr('data-filter'));

    //what will the value be?  It's going to be 'I changed', because we're accessing the data cache
    alert($this.data('filter'));
});

Give this a try, I think I understand what it is you're trying to do (the below snippet should work for you)

var colorFilter = 'red green yellow ';

$('.dropdown-menu')
  .on('click', 'li', function(e) {
   //check the source of the event, if the checkbox was already clicked then let it continue
    if (e.target.type != "checkbox") {
      $(this).find(".cat-check").click();
    }
  })
  .on('click', '.cat-check', function() {
    console.log(this);
    var $checkbox = $(this),
      type = $checkbox.data('type'),
      filter = $checkbox.data('filter'),
      isChecked = $checkbox.prop('checked');
    console.log(isChecked);
    if (type === "color") {
      if (isChecked) {
        if (colorFilter.indexOf(filter) < 0) {
          colorFilter = colorFilter + filter + " ";
        }
      } else {
        colorFilter = colorFilter.replace(filter + " ", "");
      }
      $('#filter').text(colorFilter);
    } else if (type === "shape") {
      if (isChecked) {
        if (colorFilter.indexOf(filter) < 0) {
          shapeFilter = shapeFilter + filter + " ";
        }
      } else {
        shapeFilter = shapeFilter.replace(filter + " ", "");
      }
      console.log(shapeFilter);
    }
  });
li {
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id='filter'>
  red green yellow
</div>
<ul class="dropdown-menu">
  <li class="cat-header">
    <strong>Category</strong>
  </li>
  <li>
    <a class="filter-link"> Red
            <input checked="true" class="cat-check" data-filter="red" data-type="color" type="Checkbox">
        </a>
  </li>
  <li>
    <a class="filter-link"> Green
            <input checked="true" class="cat-check" data-filter="green" data-type="color" type="Checkbox">
        </a>
  </li>
  <li>
    <a class="filter-link"> Yellow
            <input checked="true" class="cat-check" data-filter="yellow" data-type="color" type="Checkbox">
        </a>
  </li>
</ul>
Kevin
  • 2,752
  • 1
  • 24
  • 46
  • It turned out that when the checkbox was clicked rather than the `a`, the `checked` property was beings switched before the function was being called, thus giving the opposite input. Your solution avoids this problem by calling the `$(this).find(".cat-check").click();` if the target isn't a checkbox, which is much more elegant than the solution I developed on my own. – cfatt10 Apr 06 '16 at 17:33
0

It turned out that if the checkbox was clicked rather than the a, the checked property would be switched before the method was being called thus giving the different values during runtime. The simple workaround is to trigger a click event on the checkbox when the a is clicked.

$('.filter-link').click(function(e) { 
    $(this).children("input").click();
});

Thanks to @Kevin for help and a more elegant solution than I originally developed.

cfatt10
  • 688
  • 1
  • 7
  • 13