Adam Salma
Adam Salma

Reputation: 1836

Double Nested "this"

I encountered a problem today that i'm not sure how to deal with efficiently. As you can see in the code below there are three buttons, when one of them is clicked they all move to a designated spot, and then the button that was clicked changes color.

This was what I was hoping to achieve initially, however the background-color changes instantly without giving the buttons a chance to "arrive at their destinations". I tried to combat this by using a setTimeout(), but the nested function does not recognize the this.

$('.groupOfButtons').on('click', function(){
    $('#oneButton').animate({
        left:"425px",
        top:"-=24px"    
    }, 1000)
    $('#anotherButton').animate({
        left:"273px",
        top:"+=5px" 
    }, 1000)
    $('#oneMoreButton').animate({
        left:"137px",
        top:"+=34px"    
    }, 1000)
    setTimeout(function(){
        $(this).css({'background-color': 'green'})}, 1000)    // here!
    })
})

If someone could give me a workaround that would be great! But in the meantime I'm curious about double nested this's. How would one use it within one or more functions? Is it possible?

Upvotes: 0

Views: 99

Answers (2)

Josh Crozier
Josh Crozier

Reputation: 240968

Within the setTimeout function callback, this references the global window object.

You need to capture the value of this outside of that scope:

$('.groupOfButtons').on('click', function(){
    var self = this;

    $('#oneButton').animate({
        left:"425px",
        top:"-=24px"    
    }, 1000)
    $('#anotherButton').animate({
        left:"273px",
        top:"+=5px" 
    }, 1000)
    $('#oneMoreButton').animate({
        left:"137px",
        top:"+=34px"    
    }, 1000)
    setTimeout(function(){
        $(self).css({'background-color': 'green'});
    }, 1000);
});

Also, as Félix pointed out in the comments, you can also change the value of this by using the .bind() method on the function:

setTimeout(function(){
    $(this).css({'background-color': 'green'});
}.bind(this), 1000);

Upvotes: 6

Roamer-1888
Roamer-1888

Reputation: 19288

To fix the actual issue, keep a reference to which button was clicked, while this still refers to it.

Also .... setTimeout() will sort of do the job but leaves the timing somewhat to chance. For guaranteed timing, use promises and jQuery.when() to aggregate them.

$('.groupOfButtons').on('click', function() {
    var clickedButton = $(this); // keep a reference to which button was clicked
    var promise1 = $('#oneButton').animate({
        left:"425px",
        top:"-=24px"    
    }, 1000).promise();
    var promise2 = $('#anotherButton').animate({
        left:"273px",
        top:"+=5px" 
    }, 1000).promise();
    var promise3 = $('#oneMoreButton').animate({
        left:"137px",
        top:"+=34px"    
    }, 1000).promise();
    $.when(promise1, promise2, promise3).then(function() {
        clickedButton.css('backgroundColor', 'green')); // here use the reference made earlier.
    });
});

This will guarantee that the color change happens when all the buttons have arrived at their new positions, even if some computer glitch delays one or all of them.

Upvotes: 1

Related Questions