Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone view event handler for checkbox and labels

I'm trying to find out whether a checkbox ids cheked o not after a user has clicked it. The checkbox is wrapped in a label which must be clickable too. My HTML code is:

<div id="view_container">a</div>

<script type="text/template" id="view_template">
  <ul>
        <li>
            <label>
                <input type="checkbox" checked="" value="a">A option
           </label>
        </li>
        <li>
            <label>
              <input type="checkbox" value="b">B option
            </label>
       </li>
        <li>
           <label>
             <input type="checkbox" value="c">C option
          </label>
        </li>
     </ul> 
</script>

Ans the JavaScript:

View = Backbone.View.extend({
  initialize: function() {
    console.log('in the init');
    this.render();
  },
  render: function() {
    var template = _.template($("#view_template").html(), {});
    this.el.html(template);
  },
  events: {
    'click ul li label': 'clicked',
  },

  clicked: function(e) {
    e.stopPropagation();

    console.log('e.target is ' + e.target);
    console.log('e.currentTarget is ' + e.currentTarget);

    var isChecked = $(e.currentTarget).find('input:first').is(':checked');
    var selected = $(e.currentTarget).find('input:first').val().trim();

    console.log('isChecked is ' + isChecked);

  },
});

var view = new View({
  el: $("#view_container")
});

The jsfiddle is here.

There are a few problems. If i click the input, it works fine and the value of isChecked is true. However, if i clicked the label, the function clicked is executed twice with console.log output of:

e.target is [object HTMLLabelElement]
e.currentTarget is [object HTMLLabelElement]
isChecked is false
e.target is [object HTMLInputElement]
e.currentTarget is [object HTMLLabelElement]
isChecked is true

So isChecked is false the first time.

So, how do I know if the checkbox is being checked or not, regardless of if user clicks on label or directly on the input? And how do I stop handler from executing twice when user clicks on label?

like image 727
Mark Avatar asked Oct 28 '15 08:10

Mark


1 Answers

The issue is that, by nature, the labels containing <input>s or connected to them via for attribute triggers a click event on the associated input. So there are 2 click events:

  • Actual click on <label>
  • Click triggered on <input> by the label.

So if we listen to click events on label, we will catch both of them in this case - because the click triggered on <input> will also bubble up to <label> since it's a parent.

We can solve this by listening to clicks on the checkbox itself, rather than the label.

You can do the following using jQuery is method with :checkbox and :checked selectors as shown below.

    View = Backbone.View.extend({
    initialize: function () {
        this.render();
    },
    template: _.template($("#view_template").html()),
    events: {
        'click ul li :checkbox': 'clicked',
    },
    render: function () {
        this.$el.append(this.template({}));
    },
    clicked: function (e) {
        var $target = $(e.target);
        var selected = $target .is(':checked');
        console.log('selected: ', selected, 'value: ', $target.val());
    }
});

var view = new View({
    el: $("#view_container")
});

Updated fiddle

Note that it's a good practice to cache the template function rather than calling _template each time render is called.

like image 77
T J Avatar answered Sep 21 '22 16:09

T J