SamV SamV - 4 months ago 11
jQuery Question

jQuery & Backbone click events not firing

I know this problem comes up a million times, but I've been through the answers and none of them help.

JS Fiddle: http://jsfiddle.net/ZrYYy/

var LoginView = Backbone.View.extend({

initialize: function () {

console.log('Login View Intialized');
this.el = $('#login-container');

},

// Setup the events (mainly the login)
events: {

// Login - function
"click #login-btn" : "checkLogin"

},

// Actually authorization function
checkLogin: function() {

console.log("Authorizing login details with server...");

}

});

// Get it all up and going
var newLogin = new Login();
var loginView = new LoginView({model: newLogin});


In the fiddle, when I bind a normal $('#login-btn').click(); event it works fine although it does not in my own setup (with chrome). The backbone click event does not work whatsoever.

This is what the header looks like on my own code

<link href="assets/admin/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="assets/admin/css/bootstrap-responsive.css" rel="stylesheet" type="text/css">
<link href="assets/admin/css/style.css" rel="stylesheet" type="text/css">
<script src="assets/admin/js/jquery-1.9.1.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<script src="assets/admin/backbone/login.js"></script>
<script src="assets/admin/js/bootstrap.min.js"></script>


I know its best practice to include these in the footer but for now whilst this problem exists it doesn't matter(?).

Answer

You're setting your View element like this:

var LoginView = Backbone.View.extend({
  initialize: function() {
    this.el = $('#login-container');
  }
});

Every Backbone view gets a detached DOM node as its el upon creation. When you set this.el like this, Backbone is not aware that you've changed the element, and is still listening to events on the original, detached node.

Try declaring an el property on the view instead. This is the correct way to bind the view to a pre-existing element:

var LoginView = Backbone.View.extend({
  el: "#login-container",
});

If you need to set a view's el dynamically during the view's lifetime, you should use view.setElement, which enables Backbone to bind the events to the new element:

var LoginView = Backbone.View.extend({
  initialize: function() {
    this.setElement($('#login-container'));
  }
});

Edit based on comments: Sounds like you're trying to initialize the view before your page is fully loaded. Backbone doesn't find the #login-container element, ergo doesn't bind the events. You can verify this with your fiddle too: change the onLoad setting to No wrap <in head> in the fiddle settings, and lo and behold, no events are handled.

You should only initialize your app after the DOM is ready:

$(document).on('ready', function() {
  var newLogin = new Login();
  var loginView = new LoginView({model: newLogin});      
});

Or shortcut for the same:

$(function() {
  var newLogin = new Login();
  var loginView = new LoginView({model: newLogin});      
});