ronnyrr
ronnyrr

Reputation: 1551

jQuery prototype using $(this) and this

I'm building a jQuery/javascript web application using jQuery .prototype. In the following code I want to update to prototypes global value this.score, I'll have to bind(this) to make this work.

Without binding this I can reach the span inside btnElement with $(this), but after binding this doesn't work anymore. Is there any workaround for something like 'this'? ;)

showQuestion: function(question) {
   this.quiz.show();

   var btnElement = this.quiz.find('div');
   btnElement.on('click', function() {
        var btn = $(this).find('span');

        if (btn.hasClass('correct')) {
            btn.addClass('good');
            this.score += 1;
        } else {
            $('span.correct').addClass('good');
            btn.addClass('wrong');
        }
   }.bind(this));
}

Upvotes: 0

Views: 62

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074158

You've bound your event callback, so this within it refers to what this outside it referred to. That means you can't use the usual meaning this has within jQuery event handlers (the DOM element related to the event).

Three ways to handle that:

  1. Use btnElement instead, since you have it handy:

    showQuestion: function(question) {
       this.quiz.show();
    
       var btnElement = this.quiz.find('div');
       btnElement.on('click', function() {
            var btn = btnElement.find('span');   // <====
    
            if (btn.hasClass('correct')) {
                btn.addClass('good');
                this.score += 1;
            } else {
                $('span.correct').addClass('good');
                btn.addClass('wrong');
            }
       }.bind(this));
    }
    
  2. Use e.currentTarget, which is the same thing this would have been if the handler weren't bound (in this case, you could also use e.delegateTarget because it's not a delegated handler and so it's the same value):

    showQuestion: function(question) {
       this.quiz.show();
    
       var btnElement = this.quiz.find('div');
       btnElement.on('click', function(e) {              // <====
            var btn = $(e.currentTarget).find('span');   // <====
    
            if (btn.hasClass('correct')) {
                btn.addClass('good');
                this.score += 1;
            } else {
                $('span.correct').addClass('good');
                btn.addClass('wrong');
            }
       }.bind(this));
    }
    

    I haven't recommended e.target because it's not quite the same thing: e.target is the element where the event occurred; this is the element you had the event hooked on. So e.target may be a descendant element of your quiz's div.

  3. Don't bind the event handler, and instead store a reference to this in a variable the event handler closes over:

    showQuestion: function(question) {
       var self = this;                        // <====
    
       self.quiz.show();
    
       var btnElement = this.quiz.find('div');
       btnElement.on('click', function() {
            var btn = $(this).find('span');
    
            if (btn.hasClass('correct')) {
                btn.addClass('good');
                self.score += 1;                // <====
            } else {
                $('span.correct').addClass('good');
                btn.addClass('wrong');
            }
       });                                      // <====
    }
    

Upvotes: 4

Dhiraj
Dhiraj

Reputation: 33618

One way is to NOT bind and use a reference to this outside the click handler

showQuestion: function(question) {
   this.quiz.show();

   var btnElement = this.quiz.find('div');
   var self = this; // create a reference to this
   btnElement.on('click', function() {
        var btn = $(this).find('span'); // you will still be able to find the span which is inside the btnElement

        if (btn.hasClass('correct')) {
            btn.addClass('good');
            self.score += 1; // you will still be able to refer this which is reference as self outside
        } else {
            $('span.correct').addClass('good');
            btn.addClass('wrong');
        }
   });
}

Other way is to send a parameter to bind (Apart from all the other options in the other answers)

showQuestion: function(question) {
   this.quiz.show();

   var btnElement = this.quiz.find('div');
   btnElement.on('click', function(event, btnElement) { // 
        var btn = btnElement.find('span'); 
        if (btn.hasClass('correct')) {
            btn.addClass('good');
            this.score += 1; 
        } else {
            $('span.correct').addClass('good');
            btn.addClass('wrong');
        }
   }.bind(this, btnElement));
}

This works too just that it is a bit weird to send the button reference to its own click handler.

Upvotes: 2

Related Questions